diff --git a/.gitignore b/.gitignore
index cf7bd7d87b22..441cf052871c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -239,3 +239,6 @@ src/DataFactory/DataFactoryV2.Test/SessionRecords/Microsoft.Azure.Commands.DataF
# GitHub codespaces
.venv
+
+# Visual studio live unit testing config
+*.lutconfig
diff --git a/build.proj b/build.proj
index 00959af144a1..189960e55d9a 100644
--- a/build.proj
+++ b/build.proj
@@ -205,7 +205,7 @@
Microsoft.Powershell.*.dll,System*.dll,Microsoft.VisualBasic.dll,Microsoft.CSharp.dll,Microsoft.CodeAnalysis.dll,Microsoft.CodeAnalysis.CSharp.dll
System.Security.Cryptography.ProtectedData.dll,System.Configuration.ConfigurationManager.dll,System.Runtime.CompilerServices.Unsafe.dll,System.IO.FileSystem.AccessControl.dll,System.Buffers.dll,System.Text.Encodings.Web.dll,System.CodeDom.dll,System.Management.dll,System.Text.Json.dll,System.Threading.Tasks.Extensions.dll,System.IO.Hashing.dll
-
+
diff --git a/src/Accounts/Accounts.sln b/src/Accounts/Accounts.sln
index 08af4b6e000f..1a4e2f29b2fa 100644
--- a/src/Accounts/Accounts.sln
+++ b/src/Accounts/Accounts.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30503.244
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Accounts", "Accounts\Accounts.csproj", "{142D7B0B-388A-4CEB-A228-7F6D423C5C2E}"
EndProject
@@ -22,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.Test", "Auth
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthenticationAssemblyLoadContext", "AuthenticationAssemblyLoadContext\AuthenticationAssemblyLoadContext.csproj", "{D06EF9FC-4C3E-4A51-A4AD-D39AD426EF8C}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssemblyLoading", "AssemblyLoading\AssemblyLoading.csproj", "{74C0BD7E-DFFD-4B96-818E-D8660FA43159}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyLoading.Test", "AssemblyLoading.Test\AssemblyLoading.Test.csproj", "{D4540550-9808-4DEB-9D5E-F88E38D58A85}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -64,6 +68,14 @@ Global
{D06EF9FC-4C3E-4A51-A4AD-D39AD426EF8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D06EF9FC-4C3E-4A51-A4AD-D39AD426EF8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D06EF9FC-4C3E-4A51-A4AD-D39AD426EF8C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {74C0BD7E-DFFD-4B96-818E-D8660FA43159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {74C0BD7E-DFFD-4B96-818E-D8660FA43159}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {74C0BD7E-DFFD-4B96-818E-D8660FA43159}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {74C0BD7E-DFFD-4B96-818E-D8660FA43159}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4540550-9808-4DEB-9D5E-F88E38D58A85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4540550-9808-4DEB-9D5E-F88E38D58A85}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4540550-9808-4DEB-9D5E-F88E38D58A85}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4540550-9808-4DEB-9D5E-F88E38D58A85}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -71,6 +83,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{152D78F0-A642-4D0E-B3A8-2FC64FFA9714} = {95C16AED-FD57-42A0-86C3-2CF4300A4817}
{43BE9983-BD21-4474-92E5-1FFC0220B2AD} = {95C16AED-FD57-42A0-86C3-2CF4300A4817}
+ {D4540550-9808-4DEB-9D5E-F88E38D58A85} = {95C16AED-FD57-42A0-86C3-2CF4300A4817}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AA51E4F8-AA75-429D-9626-12C7B4305D72}
diff --git a/src/Accounts/Accounts/Accounts.csproj b/src/Accounts/Accounts/Accounts.csproj
index db5804cb5603..e514c5754b5f 100644
--- a/src/Accounts/Accounts/Accounts.csproj
+++ b/src/Accounts/Accounts/Accounts.csproj
@@ -11,8 +11,10 @@
-
-
+
+
+
+
@@ -22,10 +24,12 @@
-
-
+
+
+
+
-
@@ -43,19 +47,19 @@
-
+
-
+
-
+
..\..\lib\FuzzySharp.dll
-
+
True
@@ -63,7 +67,7 @@
Resources.resx
-
+
ResXFileCodeGenerator
diff --git a/src/Accounts/Accounts/Az.Accounts.psd1 b/src/Accounts/Accounts/Az.Accounts.psd1
index ae636beebfc7..e6f132707d1e 100644
--- a/src/Accounts/Accounts/Az.Accounts.psd1
+++ b/src/Accounts/Accounts/Az.Accounts.psd1
@@ -56,7 +56,8 @@ DotNetFrameworkVersion = '4.7.2'
# RequiredModules = @()
# Assemblies that must be loaded prior to importing this module
-RequiredAssemblies = 'Microsoft.Azure.PowerShell.Authentication.Abstractions.dll',
+RequiredAssemblies = 'Microsoft.Azure.PowerShell.AssemblyLoading.dll',
+ 'Microsoft.Azure.PowerShell.Authentication.Abstractions.dll',
'Microsoft.Azure.PowerShell.Authentication.dll',
'Microsoft.Azure.PowerShell.Authenticators.dll',
'Microsoft.Azure.PowerShell.Authentication.ResourceManager.dll',
diff --git a/src/Accounts/Accounts/ChangeLog.md b/src/Accounts/Accounts/ChangeLog.md
index e55384f8c14c..ffc42d25ef14 100644
--- a/src/Accounts/Accounts/ChangeLog.md
+++ b/src/Accounts/Accounts/ChangeLog.md
@@ -19,6 +19,7 @@
-->
## Upcoming Release
+* Optimized the mechanism for assembly loading.
* Enabled AzKeyStore with keyring in Linux.
## Version 2.10.4
diff --git a/src/Accounts/Accounts/StartupScripts/InitializeAssemblyResolver.ps1 b/src/Accounts/Accounts/StartupScripts/InitializeAssemblyResolver.ps1
index 3b1b77952550..d146fd3ae8d7 100644
--- a/src/Accounts/Accounts/StartupScripts/InitializeAssemblyResolver.ps1
+++ b/src/Accounts/Accounts/StartupScripts/InitializeAssemblyResolver.ps1
@@ -1,4 +1,8 @@
-if ($PSEdition -eq 'Desktop') {
+$assemblyRootPath = [System.IO.Path]::Combine($PSScriptRoot, "..", "lib")
+$conditionalAssemblyContext = [Microsoft.Azure.PowerShell.AssemblyLoading.ConditionalAssemblyContext]::new($Host.Version)
+[Microsoft.Azure.PowerShell.AssemblyLoading.ConditionalAssemblyProvider]::Initialize($assemblyRootPath, $conditionalAssemblyContext)
+
+if ($PSEdition -eq 'Desktop') {
try {
[Microsoft.Azure.Commands.Profile.Utilities.CustomAssemblyResolver]::Initialize()
}
@@ -9,9 +13,8 @@
else {
try {
Add-Type -Path ([System.IO.Path]::Combine($PSScriptRoot, "..", "Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.dll")) | Out-Null
- $assemblyLoadContextFolder = [System.IO.Path]::Combine($PSScriptRoot, "..", "AzSharedAlcAssemblies")
- Write-Debug "Registering Az shared AssemblyLoadContext for path: '$assemblyLoadContextFolder'."
- [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer]::RegisterAzSharedAssemblyLoadContext($assemblyLoadContextFolder)
+ Write-Debug "Registering Az shared AssemblyLoadContext."
+ [Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext.AzAssemblyLoadContextInitializer]::RegisterAzSharedAssemblyLoadContext()
Write-Debug "AssemblyLoadContext registered."
}
catch {
diff --git a/src/Accounts/AssemblyLoading.Test/AssemblyLoading.Test.csproj b/src/Accounts/AssemblyLoading.Test/AssemblyLoading.Test.csproj
new file mode 100644
index 000000000000..2c6e5cfe652c
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/AssemblyLoading.Test.csproj
@@ -0,0 +1,17 @@
+
+
+ Accounts
+
+
+
+
+
+ Microsoft.Azure.PowerShell.AssemblyLoading.Test
+ Microsoft.Azure.PowerShell.AssemblyLoading.Test
+
+
+
+
+
+
+
diff --git a/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssembly.cs b/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssembly.cs
new file mode 100644
index 000000000000..5e3d1718412b
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssembly.cs
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading.Test.Mocks
+{
+ internal class MockConditionalAssembly : IConditionalAssembly
+ {
+ public MockConditionalAssembly(IConditionalAssemblyContext context)
+ {
+ Context = context;
+ }
+ public bool ShouldLoad { get; set; } = true;
+
+ public string Name { get; set; }
+
+ public string Path { get; set; }
+
+ public Version Version { get; set; }
+
+ public IConditionalAssemblyContext Context { get; set; }
+
+ public void UpdateShouldLoad(bool shouldLoad)
+ {
+ ShouldLoad = ShouldLoad && shouldLoad;
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssemblyContext.cs b/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssemblyContext.cs
new file mode 100644
index 000000000000..21fd73f86ad8
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/Mocks/MockConditionalAssemblyContext.cs
@@ -0,0 +1,31 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading.Test.Mocks
+{
+ internal class MockConditionalAssemblyContext : IConditionalAssemblyContext
+ {
+ public Version PSVersion { get; set; }
+ public Architecture OSArchitecture { get; set; }
+ public OSPlatform OS { get; set; }
+
+ public bool IsOSPlatform(OSPlatform os)
+ {
+ return OS.Equals(os);
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyExtensionsTests.cs b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyExtensionsTests.cs
new file mode 100644
index 000000000000..0476b8ccbaee
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyExtensionsTests.cs
@@ -0,0 +1,82 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.PowerShell.AssemblyLoading.Test.Mocks;
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using System;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading.Test.UnitTests
+{
+ public class ConditionalAssemblyExtensionsTests
+ {
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void CanWorkWithPSVersion()
+ {
+ var windowsPSContext = new MockConditionalAssemblyContext()
+ {
+ PSVersion = Version.Parse("5.1.22621.608")
+ };
+ var windowsPSAssembly = new MockConditionalAssembly(windowsPSContext)
+ .WithWindowsPowerShell();
+ var psCoreAssembly = new MockConditionalAssembly(
+ windowsPSContext)
+ .WithPowerShellCore();
+ Assert.True(windowsPSAssembly.ShouldLoad);
+ Assert.False(psCoreAssembly.ShouldLoad);
+
+ var ps7Context = new MockConditionalAssemblyContext()
+ {
+ PSVersion = Version.Parse("7.3.0")
+ };
+ windowsPSAssembly = new MockConditionalAssembly(
+ ps7Context)
+ .WithWindowsPowerShell();
+ psCoreAssembly = new MockConditionalAssembly(
+ ps7Context)
+ .WithPowerShellCore();
+ Assert.True(psCoreAssembly.ShouldLoad);
+ Assert.False(windowsPSAssembly.ShouldLoad);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void CanWorkWithOS()
+ {
+ var windowsContext = new MockConditionalAssemblyContext()
+ {
+ OS = OSPlatform.Windows
+ };
+ var windowsAssembly = new MockConditionalAssembly(windowsContext)
+ .WithWindows();
+ var linuxAssembly = new MockConditionalAssembly(windowsContext)
+ .WithLinux();
+ Assert.True(windowsAssembly.ShouldLoad);
+ Assert.False(linuxAssembly.ShouldLoad);
+
+ var linuxContext = new MockConditionalAssemblyContext()
+ {
+ OS = OSPlatform.Linux
+ };
+ windowsAssembly = new MockConditionalAssembly(linuxContext)
+ .WithWindows();
+ linuxAssembly = new MockConditionalAssembly(linuxContext)
+ .WithLinux();
+ Assert.False(windowsAssembly.ShouldLoad);
+ Assert.True(linuxAssembly.ShouldLoad);
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyProviderTests.cs b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyProviderTests.cs
new file mode 100644
index 000000000000..35db84a69923
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyProviderTests.cs
@@ -0,0 +1,80 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.PowerShell.AssemblyLoading.Test.Mocks;
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading.Test.UnitTests
+{
+ public class ConditionalAssemblyProviderTests
+ {
+ private const string NetFx = "netfx";
+ private const string NetStandard20 = "netstandard2.0";
+ private const string NetCoreApp21 = "netcoreapp2.1";
+ private const string RootPath = "root";
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void CanGetAssembliesOnWindowsPowerShell()
+ {
+ var context = new MockConditionalAssemblyContext()
+ {
+ OS = OSPlatform.Windows,
+ PSVersion = Version.Parse("5.1.22621.608"),
+ OSArchitecture = Architecture.X64
+ };
+ ConditionalAssemblyProvider.Initialize(RootPath, context);
+ var assemblies = ConditionalAssemblyProvider.GetAssemblies();
+
+ Assert.True(assemblies.TryGetValue("Azure.Core", out var azureCore));
+ Assert.Equal(GetExpectedAssemblyPath(NetFx, "Azure.Core"), azureCore.Path);
+ Assert.True(assemblies.TryGetValue("Newtonsoft.Json", out var newtonsoftJson));
+ Assert.Equal(GetExpectedAssemblyPath(NetFx, "Newtonsoft.Json"), newtonsoftJson.Path);
+
+ Assert.True(assemblies.TryGetValue("Azure.Identity", out var azureIdentity));
+ Assert.Equal(GetExpectedAssemblyPath(NetStandard20, "Azure.Identity"), azureIdentity.Path);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void CanGetAssembliesOnPowerShellCorePlus()
+ {
+ var context = new MockConditionalAssemblyContext()
+ {
+ OS = OSPlatform.Windows,
+ PSVersion = Version.Parse("7.3.0"),
+ OSArchitecture = Architecture.X64
+ };
+ ConditionalAssemblyProvider.Initialize(RootPath, context);
+ var assemblies = ConditionalAssemblyProvider.GetAssemblies();
+
+ Assert.True(assemblies.TryGetValue("Azure.Core", out var azureCore));
+ Assert.Equal(GetExpectedAssemblyPath(NetCoreApp21, "Azure.Core"), azureCore.Path);
+
+ Assert.False(assemblies.TryGetValue("Newtonsoft.Json", out _));
+
+ Assert.True(assemblies.TryGetValue("Azure.Identity", out var azureIdentity));
+ Assert.Equal(GetExpectedAssemblyPath(NetStandard20, "Azure.Identity"), azureIdentity.Path);
+ }
+
+ private string GetExpectedAssemblyPath(string framework, string assemblyName)
+ {
+ return Path.Combine(RootPath, framework, $"{assemblyName}.dll");
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyTests.cs b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyTests.cs
new file mode 100644
index 000000000000..f5f97643f265
--- /dev/null
+++ b/src/Accounts/AssemblyLoading.Test/UnitTests/ConditionalAssemblyTests.cs
@@ -0,0 +1,172 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.PowerShell.AssemblyLoading.Test.Mocks;
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using System;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading.Test.UnitTests
+{
+ public class ConditionalAssemblyTests
+ {
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ShouldLoadAssemblyWithoutConstraint()
+ {
+ var context = new MockConditionalAssemblyContext();
+ var assembly = NewDummyAssembly(context);
+ Assert.True(assembly.ShouldLoad);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ShouldLoadAssemblyAccordingToPSVersion()
+ {
+ // windows powershell
+ var context = new MockConditionalAssemblyContext()
+ { PSVersion = Version.Parse("5.1.22621.608") };
+ var windowsPSAssembly = NewDummyAssembly(context).WithWindowsPowerShell();
+ var psCoreAssembly = NewDummyAssembly(context).WithPowerShellCore();
+ var neturalAssembly = NewDummyAssembly(context);
+ Assert.True(windowsPSAssembly.ShouldLoad);
+ Assert.False(psCoreAssembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+
+ // powershell core and 7+
+ context.PSVersion = Version.Parse("7.3.0");
+ windowsPSAssembly = NewDummyAssembly(context).WithWindowsPowerShell();
+ psCoreAssembly = NewDummyAssembly(context).WithPowerShellCore();
+ neturalAssembly = NewDummyAssembly(context);
+ Assert.False(windowsPSAssembly.ShouldLoad);
+ Assert.True(psCoreAssembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ShouldLoadAssemblyAccordingToOS()
+ {
+ // windows
+ var context = new MockConditionalAssemblyContext()
+ { OS = OSPlatform.Windows };
+ var windowsAssembly = NewDummyAssembly(context).WithWindows();
+ var linuxAssembly = NewDummyAssembly(context).WithLinux();
+ var macOSAssembly = NewDummyAssembly(context).WithMacOS();
+ var neturalAssembly = NewDummyAssembly(context);
+ Assert.True(windowsAssembly.ShouldLoad);
+ Assert.False(linuxAssembly.ShouldLoad);
+ Assert.False(macOSAssembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+
+ // linux
+ context = new MockConditionalAssemblyContext()
+ { OS = OSPlatform.Linux };
+ windowsAssembly = NewDummyAssembly(context).WithWindows();
+ linuxAssembly = NewDummyAssembly(context).WithLinux();
+ macOSAssembly = NewDummyAssembly(context).WithMacOS();
+ neturalAssembly = NewDummyAssembly(context);
+ Assert.False(windowsAssembly.ShouldLoad);
+ Assert.True(linuxAssembly.ShouldLoad);
+ Assert.False(macOSAssembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+
+ // macos
+ context = new MockConditionalAssemblyContext()
+ { OS = OSPlatform.OSX };
+ windowsAssembly = NewDummyAssembly(context).WithWindows();
+ linuxAssembly = NewDummyAssembly(context).WithLinux();
+ macOSAssembly = NewDummyAssembly(context).WithMacOS();
+ neturalAssembly = NewDummyAssembly(context);
+ Assert.False(windowsAssembly.ShouldLoad);
+ Assert.False(linuxAssembly.ShouldLoad);
+ Assert.True(macOSAssembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ShouldLoadAssemblyAccordingToCpuArch()
+ {
+ // x86
+ var context = new MockConditionalAssemblyContext()
+ { OSArchitecture = Architecture.X86 };
+ var x86Assembly = NewDummyAssembly(context).WithX86();
+ var x64Assembly = NewDummyAssembly(context).WithX64();
+ var arm64Assembly = NewDummyAssembly(context).WithArm64();
+ var neturalAssembly = NewDummyAssembly(context);
+ Assert.True(x86Assembly.ShouldLoad);
+ Assert.False(x64Assembly.ShouldLoad);
+ Assert.False(arm64Assembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+
+ // x64
+ context = new MockConditionalAssemblyContext()
+ { OSArchitecture = Architecture.X64 };
+ x86Assembly = NewDummyAssembly(context).WithX86();
+ x64Assembly = NewDummyAssembly(context).WithX64();
+ arm64Assembly = NewDummyAssembly(context).WithArm64();
+ neturalAssembly = NewDummyAssembly(context);
+ Assert.False(x86Assembly.ShouldLoad);
+ Assert.True(x64Assembly.ShouldLoad);
+ Assert.False(arm64Assembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+
+ // arm64
+ context = new MockConditionalAssemblyContext()
+ { OSArchitecture = Architecture.Arm64 };
+ x86Assembly = NewDummyAssembly(context).WithX86();
+ x64Assembly = NewDummyAssembly(context).WithX64();
+ arm64Assembly = NewDummyAssembly(context).WithArm64();
+ neturalAssembly = NewDummyAssembly(context);
+ Assert.False(x86Assembly.ShouldLoad);
+ Assert.False(x64Assembly.ShouldLoad);
+ Assert.True(arm64Assembly.ShouldLoad);
+ Assert.True(neturalAssembly.ShouldLoad);
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ShouldSupportMultipleConstraints()
+ {
+ // assembly requires powershell 7+ on linux
+ // if both meet, it should be loaded
+ var context = new MockConditionalAssemblyContext();
+ context.OS = OSPlatform.Linux;
+ context.PSVersion = Version.Parse("7.3.0");
+ var assembly = NewDummyAssembly(context).WithLinux().WithPowerShellVersion(Version.Parse("7.0.0"));
+ Assert.True(assembly.ShouldLoad);
+
+ // otherwise it shouldn't
+ context.OS = OSPlatform.Windows;
+ context.PSVersion = Version.Parse("7.3.0");
+ assembly = NewDummyAssembly(context).WithLinux().WithPowerShellVersion(Version.Parse("7.0.0"));
+ Assert.False(assembly.ShouldLoad);
+
+ context.OS = OSPlatform.Linux;
+ context.PSVersion = Version.Parse("6.2.0");
+ Assert.False(assembly.ShouldLoad);
+
+ context.OS = OSPlatform.Windows;
+ context.PSVersion = Version.Parse("5.1.0");
+ Assert.False(assembly.ShouldLoad);
+ }
+
+ private static ConditionalAssembly NewDummyAssembly(MockConditionalAssemblyContext context)
+ {
+ return new ConditionalAssembly(context, "name", "path", new Version(1, 0, 0));
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/AssemblyLoading.csproj b/src/Accounts/AssemblyLoading/AssemblyLoading.csproj
new file mode 100644
index 000000000000..b9c2e2d65331
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/AssemblyLoading.csproj
@@ -0,0 +1,14 @@
+
+
+ Accounts
+
+
+
+
+
+ Microsoft.Azure.PowerShell.AssemblyLoading
+ Microsoft.Azure.PowerShell.AssemblyLoading
+
+
+
+
diff --git a/src/Accounts/AssemblyLoading/ConditionalAssembly.cs b/src/Accounts/AssemblyLoading/ConditionalAssembly.cs
new file mode 100644
index 000000000000..78a00f9e50cc
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/ConditionalAssembly.cs
@@ -0,0 +1,52 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ public class ConditionalAssembly : IConditionalAssembly
+ {
+ public ConditionalAssembly(IConditionalAssemblyContext context, string name, string path, Version version)
+ {
+ Context = context;
+ Name = name;
+ Path = path;
+ Version = version;
+ ShouldLoad = true;
+ }
+
+ ///
+ public bool ShouldLoad { get; private set; }
+
+ ///
+ public void UpdateShouldLoad(bool shouldLoad)
+ {
+ ShouldLoad = ShouldLoad && shouldLoad;
+ }
+
+ ///
+ public IConditionalAssemblyContext Context { get; }
+
+ ///
+ public Version Version { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public string Path { get; }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/ConditionalAssemblyContext.cs b/src/Accounts/AssemblyLoading/ConditionalAssemblyContext.cs
new file mode 100644
index 000000000000..5896b59c33b3
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/ConditionalAssemblyContext.cs
@@ -0,0 +1,37 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ public class ConditionalAssemblyContext : IConditionalAssemblyContext
+ {
+ public ConditionalAssemblyContext(Version psVersion)
+ {
+ PSVersion = psVersion;
+ }
+
+ ///
+ public Version PSVersion { get; private set; }
+
+ ///
+ public bool IsOSPlatform(OSPlatform os) => RuntimeInformation.IsOSPlatform(os);
+
+ ///
+ public Architecture OSArchitecture => RuntimeInformation.OSArchitecture;
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/ConditionalAssemblyExtensions.cs b/src/Accounts/AssemblyLoading/ConditionalAssemblyExtensions.cs
new file mode 100644
index 000000000000..e4ba071f9ba6
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/ConditionalAssemblyExtensions.cs
@@ -0,0 +1,107 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ /// Contains a set of definitions of constraints of conditional assemblies here, using builder pattern.
+ ///
+ public static class ConditionalAssemblyExtensions
+ {
+ ///
+ /// The given assembly should be loaded in Windows PowerShell.
+ ///
+ public static IConditionalAssembly WithWindowsPowerShell(this IConditionalAssembly assembly)
+ {
+ return assembly.WithPowerShellVersion(new Version("5.0.0"), new Version("6.0.0"));
+ }
+
+ ///
+ /// The given assembly should be loaded in PowerShell Core (6+).
+ ///
+ public static IConditionalAssembly WithPowerShellCore(this IConditionalAssembly assembly)
+ {
+ return assembly.WithPowerShellVersion(new Version("6.0.0"));
+ }
+
+ ///
+ /// The given assembly should be loaded when the version of PowerShell lies in
+ /// [, ).
+ ///
+ ///
+ /// Lower limit of PowerShell version, inclusive.
+ /// Upper limit of PowerShell version, exclusive.
+ public static IConditionalAssembly WithPowerShellVersion(this IConditionalAssembly assembly, Version lower, Version upper = null)
+ {
+ bool shouldLoad = lower <= assembly.Context.PSVersion;
+ if (upper != null)
+ {
+ shouldLoad = shouldLoad && assembly.Context.PSVersion < upper;
+ }
+ assembly.UpdateShouldLoad(shouldLoad);
+ return assembly;
+ }
+
+ ///
+ /// The given assembly should be loaded on Windows.
+ ///
+ public static IConditionalAssembly WithWindows(this IConditionalAssembly assembly)
+ => assembly.WithOS(OSPlatform.Windows);
+
+ ///
+ /// The given assembly should be loaded on Mac OS.
+ ///
+ public static IConditionalAssembly WithMacOS(this IConditionalAssembly assembly)
+ => assembly.WithOS(OSPlatform.OSX);
+
+ ///
+ /// The given assembly should be loaded on Linux.
+ ///
+ public static IConditionalAssembly WithLinux(this IConditionalAssembly assembly)
+ => assembly.WithOS(OSPlatform.Linux);
+
+ private static IConditionalAssembly WithOS(this IConditionalAssembly assembly, OSPlatform os)
+ {
+ assembly.UpdateShouldLoad(assembly.Context.IsOSPlatform(os));
+ return assembly;
+ }
+
+ ///
+ /// The given assembly should be loaded on x86 platform.
+ ///
+ public static IConditionalAssembly WithX86(this IConditionalAssembly assembly)
+ => assembly.WithOSArchitecture(Architecture.X86);
+
+ ///
+ /// The given assembly should be loaded on x64 platform.
+ ///
+ public static IConditionalAssembly WithX64(this IConditionalAssembly assembly)
+ => assembly.WithOSArchitecture(Architecture.X64);
+
+ ///
+ /// The given assembly should be loaded on ARM64 platform.
+ ///
+ public static IConditionalAssembly WithArm64(this IConditionalAssembly assembly)
+ => assembly.WithOSArchitecture(Architecture.Arm64);
+
+ private static IConditionalAssembly WithOSArchitecture(this IConditionalAssembly assembly, Architecture arch)
+ {
+ assembly.UpdateShouldLoad(assembly.Context.OSArchitecture.Equals(arch));
+ return assembly;
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/ConditionalAssemblyProvider.cs b/src/Accounts/AssemblyLoading/ConditionalAssemblyProvider.cs
new file mode 100644
index 000000000000..23f580c58c3d
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/ConditionalAssemblyProvider.cs
@@ -0,0 +1,97 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ /// Provides a set of assemblies to load for different environments.
+ ///
+ public static class ConditionalAssemblyProvider
+ {
+ private static IConditionalAssemblyContext _context = null;
+ private static IEnumerable _assemblies = null;
+ private static string _rootPath = null;
+
+ ///
+ /// Initializes the conditional assembly provider.
+ ///
+ /// Root path of the assemblies.
+ /// Context according to which an assembly is loaded or not.
+ public static void Initialize(string rootPath, IConditionalAssemblyContext context)
+ {
+ _rootPath = rootPath ?? throw new ArgumentNullException(nameof(rootPath));
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ _assemblies = new List()
+ {
+ // todo: add a tool to update assembly versions after replacing the assemblies. (Can it support newly introduced assemblies?)
+ // todo: consider moving the list to a standalone config file
+ #region AssemblyList
+ CreateAssembly("netcoreapp2.1", "Azure.Core", "1.25.0.0").WithPowerShellCore(),
+ CreateAssembly("netcoreapp2.1", "Microsoft.Identity.Client", "4.46.2.0").WithPowerShellCore(),
+ CreateAssembly("netcoreapp3.1", "Microsoft.Identity.Client.Extensions.Msal", "2.23.0.0").WithPowerShellCore(),
+
+ CreateAssembly("netstandard2.0", "Azure.Identity", "1.6.1.0"),
+ CreateAssembly("netstandard2.0", "Microsoft.Bcl.AsyncInterfaces", "1.0.0.0"),
+ CreateAssembly("netstandard2.0", "Microsoft.IdentityModel.Abstractions", "6.22.1.0"),
+ CreateAssembly("netstandard2.0", "System.Memory.Data", "1.0.2.0"),
+ CreateAssembly("netstandard2.0", "System.Text.Json", "4.0.1.2"),
+ CreateAssembly("netstandard2.0", "System.Buffers", "4.0.3.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Memory", "4.0.1.1").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Net.Http.WinHttpHandler", "4.0.2.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Private.ServiceModel", "4.7.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Security.AccessControl", "4.1.1.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Security.Permissions", "4.0.1.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Security.Principal.Windows", "4.1.1.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.ServiceModel.Primitives", "4.7.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netstandard2.0", "System.Threading.Tasks.Extensions", "4.2.0.1").WithWindowsPowerShell(),
+
+ CreateAssembly("netfx", "Azure.Core", "1.25.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "Microsoft.Identity.Client", "4.46.2.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "Microsoft.Identity.Client.Extensions.Msal", "2.23.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "Newtonsoft.Json", "12.0.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Diagnostics.DiagnosticSource", "4.0.4.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Numerics.Vectors", "4.1.4.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Reflection.DispatchProxy", "4.0.4.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Runtime.CompilerServices.Unsafe", "4.0.6.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Security.Cryptography.Cng", "4.3.0.0").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Text.Encodings.Web", "4.0.5.1").WithWindowsPowerShell(),
+ CreateAssembly("netfx", "System.Xml.ReaderWriter", "4.1.0.0").WithWindowsPowerShell(),
+ #endregion
+ };
+ }
+
+ ///
+ /// Shorthand syntax to define a conditional assembly.
+ ///
+ private static ConditionalAssembly CreateAssembly(string framework, string name, string version)
+ {
+ string path = Path.Combine(_rootPath, framework, $"{name}.dll");
+ return new ConditionalAssembly(_context, name, path, new Version(version));
+ }
+
+ ///
+ /// Returns a set of assemblies that should be loaded into the current environment.
+ ///
+ public static IDictionary GetAssemblies()
+ {
+ if (_context == null || _assemblies == null) throw new InvalidOperationException($"Call {nameof(Initialize)}() first.");
+ return _assemblies.Where(x => x.ShouldLoad).ToDictionary(x => x.Name, x => (x.Path, x.Version));
+ }
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/IConditionalAssembly.cs b/src/Accounts/AssemblyLoading/IConditionalAssembly.cs
new file mode 100644
index 000000000000..6dd2ffd7133c
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/IConditionalAssembly.cs
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ /// Represents a dependency assembly of Az modules.
+ ///
+ public interface IConditionalAssembly
+ {
+ ///
+ /// Whether the assembly should be loaded given the constraints it comes with
+ /// and the current .
+ ///
+ bool ShouldLoad { get; }
+
+ ///
+ /// Name of the assembly. Should be its file name without extension.
+ ///
+ string Name { get; }
+
+ ///
+ /// Path to the assembly file.
+ ///
+ string Path { get; }
+
+ ///
+ /// Assembly version.
+ ///
+ Version Version { get; }
+
+ ///
+ /// Context with information about the current environment.
+ /// Used to calculate if this assembly should be loaded.
+ ///
+ IConditionalAssemblyContext Context { get; }
+
+ ///
+ /// Update .
+ ///
+ ///
+ /// This method shortcuts, meaning if is false, it can never be updated.
+ ///
+ /// The new value of
+ void UpdateShouldLoad(bool shouldLoad);
+ }
+}
diff --git a/src/Accounts/AssemblyLoading/IConditionalAssemblyContext.cs b/src/Accounts/AssemblyLoading/IConditionalAssemblyContext.cs
new file mode 100644
index 000000000000..7b8c9bedb013
--- /dev/null
+++ b/src/Accounts/AssemblyLoading/IConditionalAssemblyContext.cs
@@ -0,0 +1,43 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.PowerShell.AssemblyLoading
+{
+ ///
+ /// Context with information about the current environment.
+ /// Used to calculate if this assembly should be loaded.
+ ///
+ public interface IConditionalAssemblyContext
+ {
+ ///
+ /// Version of PowerShell. For example "5.1.22621.608".
+ ///
+ Version PSVersion { get; }
+
+ ///
+ /// Returns if the current operating system matches .
+ ///
+ /// The expected operating system
+ bool IsOSPlatform(OSPlatform os);
+
+ ///
+ /// OS Architecture. For example "X86".
+ ///
+ ///
+ Architecture OSArchitecture { get; }
+ }
+}
diff --git a/src/Accounts/Authentication/Authentication.csproj b/src/Accounts/Authentication/Authentication.csproj
index 53aef056e27b..d8b7284460d3 100644
--- a/src/Accounts/Authentication/Authentication.csproj
+++ b/src/Accounts/Authentication/Authentication.csproj
@@ -17,6 +17,10 @@
+
+
+
+
True
diff --git a/src/Accounts/Authentication/Utilities/CustomAssemblyResolver.cs b/src/Accounts/Authentication/Utilities/CustomAssemblyResolver.cs
index f599c219167e..f99a50fc26ce 100644
--- a/src/Accounts/Authentication/Utilities/CustomAssemblyResolver.cs
+++ b/src/Accounts/Authentication/Utilities/CustomAssemblyResolver.cs
@@ -12,53 +12,23 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
+using Microsoft.Azure.PowerShell.AssemblyLoading;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Reflection;
namespace Microsoft.Azure.Commands.Profile.Utilities
{
+ ///
+ /// Handles how common dependency assemblies like Azure.Core are loaded on .NET framework.
+ ///
public static class CustomAssemblyResolver
{
- private static IDictionary NetFxPreloadAssemblies =
- new Dictionary(StringComparer.InvariantCultureIgnoreCase)
- {
- {"Azure.Core", new Version("1.25.0.0")},
- {"Azure.Identity", new Version("1.6.1.0")},
- {"Microsoft.Bcl.AsyncInterfaces", new Version("1.1.1.0")},
- {"Microsoft.Identity.Client", new Version("4.46.2.0") },
- {"Microsoft.Identity.Client.Extensions.Msal", new Version("2.23.0.0") },
- {"Microsoft.IdentityModel.Abstractions", new Version("6.22.1.0") },
-
- {"Newtonsoft.Json", new Version("10.0.0.0")},
- {"System.Buffers", new Version("4.0.3.0")},
- {"System.Diagnostics.DiagnosticSource", new Version("4.0.4.0")},
- {"System.Memory", new Version("4.0.1.1")},
- {"System.Memory.Data", new Version("1.0.2.0")},
- {"System.Net.Http.WinHttpHandler", new Version("4.0.2.0")},
- {"System.Numerics.Vectors", new Version("4.1.4.0")},
- {"System.Private.ServiceModel", new Version("4.7.0.0")}, //used by Compute
- {"System.Reflection.DispatchProxy", new Version("4.0.4.0")},
- {"System.Runtime.CompilerServices.Unsafe", new Version("4.0.6.0")},
- {"System.Security.AccessControl", new Version("4.1.1.0")},
- {"System.Security.Cryptography.Cng", new Version("4.3.0.0")},
- {"System.Security.Permissions", new Version("4.0.1.0")},
- {"System.Security.Principal.Windows", new Version("4.1.1.0")},
- {"System.ServiceModel.Primitives", new Version("4.7.0.0")}, //used by Compute
- {"System.Text.Encodings.Web", new Version("4.0.5.1")},
- {"System.Text.Json", new Version("4.0.1.2")},
- {"System.Threading.Tasks.Extensions", new Version("4.2.0.1")},
- {"System.Xml.ReaderWriter", new Version("4.1.0.0")}
- };
-
- private static string PreloadAssemblyFolder { get; set; }
+ private static IDictionary NetFxPreloadAssemblies = ConditionalAssemblyProvider.GetAssemblies();
public static void Initialize()
{
//This function is call before loading assemblies in PreloadAssemblies folder, so NewtonSoft.Json could not be used here
- var accountFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- PreloadAssemblyFolder = Path.Combine(accountFolder, "PreloadAssemblies");
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
@@ -70,13 +40,14 @@ public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEvent
try
{
AssemblyName name = new AssemblyName(args.Name);
- if (NetFxPreloadAssemblies.TryGetValue(name.Name, out Version version))
+ if (NetFxPreloadAssemblies.TryGetValue(name.Name, out var assembly))
{
//For Newtonsoft.Json, allow to use bigger version to replace smaller version
- if (version >= name.Version && (version.Major == name.Version.Major || string.Equals(name.Name, "Newtonsoft.Json", StringComparison.OrdinalIgnoreCase)))
+ if (assembly.Version >= name.Version
+ && (assembly.Version.Major == name.Version.Major
+ || string.Equals(name.Name, "Newtonsoft.Json", StringComparison.OrdinalIgnoreCase)))
{
- string requiredAssembly = Path.Combine(PreloadAssemblyFolder, $"{name.Name}.dll");
- return Assembly.LoadFrom(requiredAssembly);
+ return Assembly.LoadFrom(assembly.Path);
}
}
}
diff --git a/src/Accounts/AuthenticationAssemblyLoadContext/AuthenticationAssemblyLoadContext.csproj b/src/Accounts/AuthenticationAssemblyLoadContext/AuthenticationAssemblyLoadContext.csproj
index cc38ef9b6755..69e04117b36e 100644
--- a/src/Accounts/AuthenticationAssemblyLoadContext/AuthenticationAssemblyLoadContext.csproj
+++ b/src/Accounts/AuthenticationAssemblyLoadContext/AuthenticationAssemblyLoadContext.csproj
@@ -23,5 +23,8 @@
TRACE;RELEASE;SIGN
+
+
+
diff --git a/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContext.cs b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContext.cs
index 0023a97aa0d7..8e3e2dab975e 100644
--- a/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContext.cs
+++ b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContext.cs
@@ -12,7 +12,6 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
@@ -20,18 +19,27 @@
namespace Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext
{
- internal class AzAssemblyLoadContext : AssemblyLoadContext
+ ///
+ /// Assembly load context of a service module.
+ /// The way of looking for assemblies is based on directory.
+ ///
+ internal class AzAssemblyLoadContext : AzAssemblyLoadContextBase
{
private string AssemblyDirectory { get; set; }
- private ConcurrentDictionary AssemblyCache { get; set; } =
- new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
+ private static readonly ConcurrentDictionary DependencyLoadContexts = new ConcurrentDictionary();
- private static readonly ConcurrentDictionary DependencyLoadContexts = new ConcurrentDictionary();
-
- internal static AzAssemblyLoadContext GetForDirectory(string directoryPath)
+ ///
+ /// Get an ALC for a certain directory that contains assemblies.
+ ///
+ ///
+ /// There are two types of possible value for :
+ /// 1. which will create if not exist and return an ALC for shared libraries.
+ /// 2. A directory in a service module that contains the assemblies to be loaded into the ALC of the service module.
+ ///
+ internal static AssemblyLoadContext GetForDirectory(string directoryPath)
{
- return DependencyLoadContexts.GetOrAdd(directoryPath, (path) => new AzAssemblyLoadContext(path));
+ return DependencyLoadContexts.GetOrAdd(directoryPath, path => path.Equals(AzSharedAssemblyLoadContext.Key) ? new AzSharedAssemblyLoadContext() : (AssemblyLoadContext)new AzAssemblyLoadContext(directoryPath));
}
///
@@ -39,21 +47,13 @@ internal static AzAssemblyLoadContext GetForDirectory(string directoryPath)
///
/// Root directory to look for assembly.
///
- public AzAssemblyLoadContext(string directory)
+ public AzAssemblyLoadContext(string directory) : base(directory)
{
AssemblyDirectory = directory;
}
- protected override Assembly Load(AssemblyName requestedAssemblyName)
+ protected override Assembly LoadAfterCacheMiss(AssemblyName requestedAssemblyName)
{
- if (AssemblyCache.TryGetValue(requestedAssemblyName.Name, out Assembly assembly))
- {
- if (IsAssemblyMatching(requestedAssemblyName, assembly.GetName()))
- {
- return assembly;
- }
- }
-
string assemblyFileName = $"{requestedAssemblyName.Name}.dll";
// Now try to load the assembly from the dependency directory
@@ -65,51 +65,12 @@ protected override Assembly Load(AssemblyName requestedAssemblyName)
var loadedAssemblyName = loadedAssembly.GetName();
if (IsAssemblyMatching(requestedAssemblyName, loadedAssemblyName))
{
- AssemblyCache.TryAdd(loadedAssemblyName.Name, loadedAssembly);
+ CacheAssembly(loadedAssemblyName.Name, loadedAssembly);
}
return loadedAssembly;
}
return null;
}
-
- private bool IsAssemblyMatching(AssemblyName requestedAssembly, AssemblyName loadedAssembly)
- {
- // We use the same rules as CoreCLR loader to compare the requested assembly and loaded assembly:
- // 1. If 'Version' of the requested assembly is specified, then the requested version should be less or equal to the loaded version;
- // 2. If 'CultureName' of the requested assembly is specified (not NullOrEmpty), then the CultureName of the loaded assembly should be the same;
- // 3. If 'PublicKeyToken' of the requested assembly is specified (not Null or EmptyArray), then the PublicKenToken of the loaded assembly should be the same.
-
- // Version of the requested assembly should be the same or before the version of loaded assembly
- if (requestedAssembly.Version != null && requestedAssembly.Version.CompareTo(loadedAssembly.Version) > 0)
- {
- return false;
- }
-
- // CultureName of requested assembly and loaded assembly should be the same
- string requestedCultureName = requestedAssembly.CultureName;
- if (!string.IsNullOrEmpty(requestedCultureName) && !requestedCultureName.Equals(loadedAssembly.CultureName, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- // PublicKeyToken should be the same, unless it's not specified in the requested assembly
- byte[] requestedPublicKeyToken = requestedAssembly.GetPublicKeyToken();
- byte[] loadedPublicKeyToken = loadedAssembly.GetPublicKeyToken();
-
- if (requestedPublicKeyToken != null && requestedPublicKeyToken.Length > 0)
- {
- if (loadedPublicKeyToken == null || requestedPublicKeyToken.Length != loadedPublicKeyToken.Length)
- return false;
-
- for (int i = 0; i < requestedPublicKeyToken.Length; i++)
- {
- if (requestedPublicKeyToken[i] != loadedPublicKeyToken[i])
- return false;
- }
- }
-
- return true;
- }
}
}
diff --git a/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextBase.cs b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextBase.cs
new file mode 100644
index 000000000000..5b9032b6be73
--- /dev/null
+++ b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextBase.cs
@@ -0,0 +1,99 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Concurrent;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext
+{
+ ///
+ /// Base class providing caching capability.
+ ///
+ internal abstract class AzAssemblyLoadContextBase : AssemblyLoadContext
+ {
+ private ConcurrentDictionary AssemblyCache { get; set; } =
+ new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
+
+ protected AzAssemblyLoadContextBase(string name) : base(name) { }
+
+ protected void CacheAssembly(string name, Assembly assembly)
+ {
+ AssemblyCache.TryAdd(name, assembly);
+ }
+
+ protected sealed override Assembly Load(AssemblyName requestedAssemblyName)
+ {
+ if (AssemblyCache.TryGetValue(requestedAssemblyName.Name, out Assembly assembly))
+ {
+ if (IsAssemblyMatching(requestedAssemblyName, assembly.GetName()))
+ {
+ return assembly;
+ }
+ }
+ return LoadAfterCacheMiss(requestedAssemblyName);
+ }
+
+ ///
+ /// Override this method to provide custom ways to load an assembly by name.
+ ///
+ ///
+ /// Call to add the loaded assembly to cache.
+ ///
+ protected virtual Assembly LoadAfterCacheMiss(AssemblyName requestAssemblyName)
+ {
+ return null;
+ }
+
+ protected bool IsAssemblyMatching(AssemblyName requestedAssembly, AssemblyName loadedAssembly)
+ {
+ // We use the same rules as CoreCLR loader to compare the requested assembly and loaded assembly:
+ // 1. If 'Version' of the requested assembly is specified, then the requested version should be less or equal to the loaded version;
+ // 2. If 'CultureName' of the requested assembly is specified (not NullOrEmpty), then the CultureName of the loaded assembly should be the same;
+ // 3. If 'PublicKeyToken' of the requested assembly is specified (not Null or EmptyArray), then the PublicKenToken of the loaded assembly should be the same.
+
+ // Version of the requested assembly should be the same or before the version of loaded assembly
+ if (requestedAssembly.Version != null && requestedAssembly.Version.CompareTo(loadedAssembly.Version) > 0)
+ {
+ return false;
+ }
+
+ // CultureName of requested assembly and loaded assembly should be the same
+ string requestedCultureName = requestedAssembly.CultureName;
+ if (!string.IsNullOrEmpty(requestedCultureName) && !requestedCultureName.Equals(loadedAssembly.CultureName, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ // PublicKeyToken should be the same, unless it's not specified in the requested assembly
+ byte[] requestedPublicKeyToken = requestedAssembly.GetPublicKeyToken();
+ byte[] loadedPublicKeyToken = loadedAssembly.GetPublicKeyToken();
+
+ if (requestedPublicKeyToken != null && requestedPublicKeyToken.Length > 0)
+ {
+ if (loadedPublicKeyToken == null || requestedPublicKeyToken.Length != loadedPublicKeyToken.Length)
+ return false;
+
+ for (int i = 0; i < requestedPublicKeyToken.Length; i++)
+ {
+ if (requestedPublicKeyToken[i] != loadedPublicKeyToken[i])
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextInitializer.cs b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextInitializer.cs
index 38a52e6de9f5..b95cc61db8eb 100644
--- a/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextInitializer.cs
+++ b/src/Accounts/AuthenticationAssemblyLoadContext/AzAssemblyLoadContextInitializer.cs
@@ -14,50 +14,34 @@
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.IO;
using System.Runtime.Loader;
+using Microsoft.Azure.PowerShell.AssemblyLoading;
namespace Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext
{
public static class AzAssemblyLoadContextInitializer
{
- private static string AzSharedAssemblyDirectory { get; set; }
- private static ConcurrentDictionary AzSharedAssemblyMap { get; set; }
+ private static ConcurrentDictionary AzSharedAssemblyMap { get; set; }
private static ConcurrentDictionary ModuleAlcEntryAssemblyMap { get; set; }
static AzAssemblyLoadContextInitializer()
{
- //TODO: Generate assembly version info into AzSharedAssemblies.json during build
- var azSharedAssemblies = new Dictionary()
- {
- {"Azure.Core", new Version("1.25.0.0")},
- {"Azure.Identity", new Version("1.6.1.0")},
- {"Microsoft.Bcl.AsyncInterfaces", new Version("1.1.1.0")},
- {"Microsoft.Identity.Client", new Version("4.46.2.0") },
- {"Microsoft.Identity.Client.Extensions.Msal", new Version("2.23.0.0") },
- {"Microsoft.IdentityModel.Abstractions", new Version("6.22.1.0") },
- {"System.Memory.Data", new Version("1.0.2.0")},
- {"System.Text.Json", new Version("4.0.1.2")},
- };
-
- AzSharedAssemblyMap = new ConcurrentDictionary(azSharedAssemblies, StringComparer.OrdinalIgnoreCase);
-
+ var azSharedAssemblies = ConditionalAssemblyProvider.GetAssemblies();
+ AzSharedAssemblyMap = new ConcurrentDictionary(azSharedAssemblies, StringComparer.OrdinalIgnoreCase);
ModuleAlcEntryAssemblyMap = new ConcurrentDictionary();
}
///
/// Registers the shared ALC and listen to assembly resolving event of the default ALC.
///
- /// Root directory to look for assemblies.
- public static void RegisterAzSharedAssemblyLoadContext(string azSharedAssemblyDirectory)
+ public static void RegisterAzSharedAssemblyLoadContext()
{
- AzSharedAssemblyDirectory = azSharedAssemblyDirectory;
AssemblyLoadContext.Default.Resolving += Default_Resolving;
}
///
- /// Registers an ALC to be instanciated later.
+ /// Registers an ALC to be instantiated later.
///
/// Name of the entry assembly, typically "{Module}.AlcWrapper.dll". It must be unique for each module.
/// Root directory to look for assemblies.
@@ -68,9 +52,9 @@ public static void RegisterModuleAssemblyLoadContext(string contextEntryAssembly
private static System.Reflection.Assembly Default_Resolving(AssemblyLoadContext context, System.Reflection.AssemblyName assemblyName)
{
- if (AzSharedAssemblyMap.ContainsKey(assemblyName.Name) && AzSharedAssemblyMap[assemblyName.Name] >= assemblyName.Version)
+ if (AzSharedAssemblyMap.TryGetValue(assemblyName.Name, out var azSharedAssembly) && azSharedAssembly.Version >= assemblyName.Version)
{
- return AzAssemblyLoadContext.GetForDirectory(AzSharedAssemblyDirectory).LoadFromAssemblyName(assemblyName);
+ return AzAssemblyLoadContext.GetForDirectory(AzSharedAssemblyLoadContext.Key).LoadFromAssemblyName(assemblyName);
}
if (ModuleAlcEntryAssemblyMap.TryGetValue(assemblyName.Name, out string moduleLoadContextDirectory))
diff --git a/src/Accounts/AuthenticationAssemblyLoadContext/AzSharedAssemblyLoadContext.cs b/src/Accounts/AuthenticationAssemblyLoadContext/AzSharedAssemblyLoadContext.cs
new file mode 100644
index 000000000000..0daf3d982133
--- /dev/null
+++ b/src/Accounts/AuthenticationAssemblyLoadContext/AzSharedAssemblyLoadContext.cs
@@ -0,0 +1,57 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.PowerShell.AssemblyLoading;
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Reflection;
+
+namespace Microsoft.Azure.PowerShell.AuthenticationAssemblyLoadContext
+{
+ ///
+ /// Assembly load context of the shared assemblies in Az.Accounts module.
+ /// Assemblies are provided by .
+ ///
+ internal class AzSharedAssemblyLoadContext : AzAssemblyLoadContextBase
+ {
+ ///
+ /// Key to get the shared ALC.
+ ///
+ public const string Key = nameof(AzSharedAssemblyLoadContext);
+
+ private ConcurrentDictionary _assemblies;
+
+ public AzSharedAssemblyLoadContext() : base(Key)
+ {
+ _assemblies = new ConcurrentDictionary(ConditionalAssemblyProvider.GetAssemblies(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ protected override Assembly LoadAfterCacheMiss(AssemblyName requestedAssemblyName)
+ {
+ if (_assemblies.TryGetValue(requestedAssemblyName.Name, out var assembly)
+ && File.Exists(assembly.Path))
+ {
+ var loadedAssembly = LoadFromAssemblyPath(assembly.Path);
+ var loadedAssemblyName = loadedAssembly.GetName();
+ if (IsAssemblyMatching(requestedAssemblyName, loadedAssemblyName))
+ {
+ CacheAssembly(loadedAssemblyName.Name, loadedAssembly);
+ }
+ return loadedAssembly;
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Accounts/Authenticators/Authenticators.csproj b/src/Accounts/Authenticators/Authenticators.csproj
index 7311b2b5ca4a..013d0badaf13 100644
--- a/src/Accounts/Authenticators/Authenticators.csproj
+++ b/src/Accounts/Authenticators/Authenticators.csproj
@@ -26,13 +26,6 @@
-
-
-
-
-
-
-
diff --git a/src/Storage/Storage/Storage.csproj b/src/Storage/Storage/Storage.csproj
index 0ca0120999c9..2c1e7b7dbc3f 100644
--- a/src/Storage/Storage/Storage.csproj
+++ b/src/Storage/Storage/Storage.csproj
@@ -23,11 +23,7 @@
-
-
-
-
-
+
$(RepoSrc)lib\Microsoft.Azure.Storage.DataMovement.dll
diff --git a/src/lib/NetFxPreloadAssemblies/Azure.Identity.dll b/src/lib/NetFxPreloadAssemblies/Azure.Identity.dll
deleted file mode 100644
index 5662a76ad958..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/Azure.Identity.dll and /dev/null differ
diff --git a/src/lib/NetFxPreloadAssemblies/Microsoft.Bcl.AsyncInterfaces.dll b/src/lib/NetFxPreloadAssemblies/Microsoft.Bcl.AsyncInterfaces.dll
deleted file mode 100644
index 869ac1b86c57..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/Microsoft.Bcl.AsyncInterfaces.dll and /dev/null differ
diff --git a/src/lib/NetFxPreloadAssemblies/Microsoft.IdentityModel.Abstractions.dll b/src/lib/NetFxPreloadAssemblies/Microsoft.IdentityModel.Abstractions.dll
deleted file mode 100644
index ab35e1c9e9f7..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/Microsoft.IdentityModel.Abstractions.dll and /dev/null differ
diff --git a/src/lib/NetFxPreloadAssemblies/Newtonsoft.Json.dll b/src/lib/NetFxPreloadAssemblies/Newtonsoft.Json.dll
deleted file mode 100644
index 77a5d89e605c..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/Newtonsoft.Json.dll and /dev/null differ
diff --git a/src/lib/NetFxPreloadAssemblies/System.Memory.Data.dll b/src/lib/NetFxPreloadAssemblies/System.Memory.Data.dll
deleted file mode 100644
index 5aa381018c00..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/System.Memory.Data.dll and /dev/null differ
diff --git a/src/lib/NetFxPreloadAssemblies/System.Text.Json.dll b/src/lib/NetFxPreloadAssemblies/System.Text.Json.dll
deleted file mode 100644
index a3a85c2b7255..000000000000
Binary files a/src/lib/NetFxPreloadAssemblies/System.Text.Json.dll and /dev/null differ
diff --git a/src/lib/NetCorePreloadAssemblies/Azure.Core.dll b/src/lib/netcoreapp2.1/Azure.Core.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Azure.Core.dll
rename to src/lib/netcoreapp2.1/Azure.Core.dll
diff --git a/src/lib/NetCorePreloadAssemblies/Microsoft.Identity.Client.dll b/src/lib/netcoreapp2.1/Microsoft.Identity.Client.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Microsoft.Identity.Client.dll
rename to src/lib/netcoreapp2.1/Microsoft.Identity.Client.dll
diff --git a/src/lib/NetCorePreloadAssemblies/Microsoft.Identity.Client.Extensions.Msal.dll b/src/lib/netcoreapp3.1/Microsoft.Identity.Client.Extensions.Msal.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Microsoft.Identity.Client.Extensions.Msal.dll
rename to src/lib/netcoreapp3.1/Microsoft.Identity.Client.Extensions.Msal.dll
diff --git a/src/lib/NetFxPreloadAssemblies/Azure.Core.dll b/src/lib/netfx/Azure.Core.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/Azure.Core.dll
rename to src/lib/netfx/Azure.Core.dll
diff --git a/src/lib/NetFxPreloadAssemblies/Microsoft.Identity.Client.Extensions.Msal.dll b/src/lib/netfx/Microsoft.Identity.Client.Extensions.Msal.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/Microsoft.Identity.Client.Extensions.Msal.dll
rename to src/lib/netfx/Microsoft.Identity.Client.Extensions.Msal.dll
diff --git a/src/lib/NetFxPreloadAssemblies/Microsoft.Identity.Client.dll b/src/lib/netfx/Microsoft.Identity.Client.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/Microsoft.Identity.Client.dll
rename to src/lib/netfx/Microsoft.Identity.Client.dll
diff --git a/src/lib/NetFxPreloadAssemblies/Newtonsoft.Json.12.0.3.dll b/src/lib/netfx/Newtonsoft.Json.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/Newtonsoft.Json.12.0.3.dll
rename to src/lib/netfx/Newtonsoft.Json.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Diagnostics.DiagnosticSource.dll b/src/lib/netfx/System.Diagnostics.DiagnosticSource.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Diagnostics.DiagnosticSource.dll
rename to src/lib/netfx/System.Diagnostics.DiagnosticSource.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Numerics.Vectors.dll b/src/lib/netfx/System.Numerics.Vectors.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Numerics.Vectors.dll
rename to src/lib/netfx/System.Numerics.Vectors.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Reflection.DispatchProxy.dll b/src/lib/netfx/System.Reflection.DispatchProxy.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Reflection.DispatchProxy.dll
rename to src/lib/netfx/System.Reflection.DispatchProxy.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Runtime.CompilerServices.Unsafe.dll b/src/lib/netfx/System.Runtime.CompilerServices.Unsafe.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Runtime.CompilerServices.Unsafe.dll
rename to src/lib/netfx/System.Runtime.CompilerServices.Unsafe.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Security.Cryptography.Cng.dll b/src/lib/netfx/System.Security.Cryptography.Cng.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Security.Cryptography.Cng.dll
rename to src/lib/netfx/System.Security.Cryptography.Cng.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Text.Encodings.Web.dll b/src/lib/netfx/System.Text.Encodings.Web.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Text.Encodings.Web.dll
rename to src/lib/netfx/System.Text.Encodings.Web.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Xml.ReaderWriter.dll b/src/lib/netfx/System.Xml.ReaderWriter.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Xml.ReaderWriter.dll
rename to src/lib/netfx/System.Xml.ReaderWriter.dll
diff --git a/src/lib/NetCorePreloadAssemblies/Azure.Identity.dll b/src/lib/netstandard2.0/Azure.Identity.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Azure.Identity.dll
rename to src/lib/netstandard2.0/Azure.Identity.dll
diff --git a/src/lib/NetCorePreloadAssemblies/Microsoft.Bcl.AsyncInterfaces.dll b/src/lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Microsoft.Bcl.AsyncInterfaces.dll
rename to src/lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll
diff --git a/src/lib/NetCorePreloadAssemblies/Microsoft.IdentityModel.Abstractions.dll b/src/lib/netstandard2.0/Microsoft.IdentityModel.Abstractions.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/Microsoft.IdentityModel.Abstractions.dll
rename to src/lib/netstandard2.0/Microsoft.IdentityModel.Abstractions.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Buffers.dll b/src/lib/netstandard2.0/System.Buffers.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Buffers.dll
rename to src/lib/netstandard2.0/System.Buffers.dll
diff --git a/src/lib/NetCorePreloadAssemblies/System.Memory.Data.dll b/src/lib/netstandard2.0/System.Memory.Data.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/System.Memory.Data.dll
rename to src/lib/netstandard2.0/System.Memory.Data.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Memory.dll b/src/lib/netstandard2.0/System.Memory.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Memory.dll
rename to src/lib/netstandard2.0/System.Memory.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Net.Http.WinHttpHandler.dll b/src/lib/netstandard2.0/System.Net.Http.WinHttpHandler.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Net.Http.WinHttpHandler.dll
rename to src/lib/netstandard2.0/System.Net.Http.WinHttpHandler.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Private.ServiceModel.dll b/src/lib/netstandard2.0/System.Private.ServiceModel.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Private.ServiceModel.dll
rename to src/lib/netstandard2.0/System.Private.ServiceModel.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Security.AccessControl.dll b/src/lib/netstandard2.0/System.Security.AccessControl.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Security.AccessControl.dll
rename to src/lib/netstandard2.0/System.Security.AccessControl.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Security.Permissions.dll b/src/lib/netstandard2.0/System.Security.Permissions.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Security.Permissions.dll
rename to src/lib/netstandard2.0/System.Security.Permissions.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Security.Principal.Windows.dll b/src/lib/netstandard2.0/System.Security.Principal.Windows.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Security.Principal.Windows.dll
rename to src/lib/netstandard2.0/System.Security.Principal.Windows.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.ServiceModel.Primitives.dll b/src/lib/netstandard2.0/System.ServiceModel.Primitives.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.ServiceModel.Primitives.dll
rename to src/lib/netstandard2.0/System.ServiceModel.Primitives.dll
diff --git a/src/lib/NetCorePreloadAssemblies/System.Text.Json.dll b/src/lib/netstandard2.0/System.Text.Json.dll
similarity index 100%
rename from src/lib/NetCorePreloadAssemblies/System.Text.Json.dll
rename to src/lib/netstandard2.0/System.Text.Json.dll
diff --git a/src/lib/NetFxPreloadAssemblies/System.Threading.Tasks.Extensions.dll b/src/lib/netstandard2.0/System.Threading.Tasks.Extensions.dll
similarity index 100%
rename from src/lib/NetFxPreloadAssemblies/System.Threading.Tasks.Extensions.dll
rename to src/lib/netstandard2.0/System.Threading.Tasks.Extensions.dll
diff --git a/src/lib/test.net472.config b/src/lib/test.net472.config
index 131361166d84..593b17b73709 100644
--- a/src/lib/test.net472.config
+++ b/src/lib/test.net472.config
@@ -7,9 +7,9 @@
publicKeyToken="30ad4fe6b2a6aeed"
culture="neutral" />
+ href="../../../../../lib/netfx/Newtonsoft.Json.9.dll"/>
+ href="../../../../../lib/netfx/Newtonsoft.Json.dll"/>
diff --git a/tools/AzureRM.Example.psm1 b/tools/AzureRM.Example.psm1
index 1404de564b79..b064db1aa2a0 100644
--- a/tools/AzureRM.Example.psm1
+++ b/tools/AzureRM.Example.psm1
@@ -44,7 +44,7 @@ function Preload-Assembly {
}
}
catch {}
- }
+ }
}
if (%ISAZMODULE% -and ($PSEdition -eq 'Desktop'))
@@ -74,36 +74,6 @@ if (Get-Module %AZORAZURERM%.profile -ErrorAction Ignore)
"If you are running in Azure Automation, take care that none of your runbooks import both Az and AzureRM modules. More information can be found here: https://aka.ms/azps-migration-guide.")
}
-$preloadPath = (Join-Path $PSScriptRoot -ChildPath "PreloadAssemblies")
-Preload-Assembly -AssemblyDirectory $preloadPath
-$preloadPath = (Join-Path $PSScriptRoot -ChildPath "ModuleAlcAssemblies")
-Preload-Assembly -AssemblyDirectory $preloadPath
-
-$netCorePath = (Join-Path $PSScriptRoot -ChildPath "NetCoreAssemblies")
-if($PSEdition -eq 'Core' -and (Test-Path $netCorePath -ErrorAction Ignore))
-{
- try
- {
- $loadedAssemblies = ([System.AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object {New-Object -TypeName System.Reflection.AssemblyName -ArgumentList $_.FullName} )
- Get-ChildItem -ErrorAction Stop -Path $netCorePath -Filter "*.dll" | ForEach-Object {
- $assemblyName = ([System.Reflection.AssemblyName]::GetAssemblyName($_.FullName))
- $matches = ($loadedAssemblies | Where-Object {$_.Name -eq $assemblyName.Name})
- if (-not $matches)
- {
- try
- {
- Add-Type -Path $_.FullName -ErrorAction Ignore | Out-Null
- }
- catch {
- Write-Verbose $_
- }
- }
- }
- }
- catch {}
-}
-
-
%IMPORTED-DEPENDENCIES%
if (Test-Path -Path "$PSScriptRoot\PostImportScripts" -ErrorAction Ignore)
diff --git a/tools/CheckAssemblies.ps1 b/tools/CheckAssemblies.ps1
index 90f030bcd96c..62a988a283e4 100644
--- a/tools/CheckAssemblies.ps1
+++ b/tools/CheckAssemblies.ps1
@@ -19,23 +19,18 @@ param(
function Get-PreloadAssemblies{
param(
+ [Parameter(Mandatory)]
+ [string] $BuildFolder,
[Parameter(Mandatory=$True)]
[string] $ModuleFolder
)
-
- if($PSEdition -eq 'Core') {
- $preloadFolderName = @("NetCoreAssemblies", "AzSharedAlcAssemblies")
- } else {
- $preloadFolderName = "PreloadAssemblies"
- }
- $preloadFolderName | ForEach-Object {
- $preloadAssemblies = @()
- $preloadFolder = [System.IO.Path]::Combine($ModuleFolder, $_)
- if(Test-Path $preloadFolder){
- $preloadAssemblies = (Get-ChildItem $preloadFolder -Filter "*.dll").Name | ForEach-Object { $_ -replace ".dll", ""}
- }
- $preloadAssemblies
- }
+ Write-Host "Getting preload assemblies in $BuildFolder for $ModuleFolder"
+ Add-Type -Path ([System.IO.Path]::Combine($BuildFolder, "Az.Accounts", "Microsoft.Azure.PowerShell.AssemblyLoading.dll"))
+ $assemblyRootPath = [System.IO.Path]::Combine($BuildFolder, "Az.Accounts", "lib")
+ $conditionalAssemblyContext = [Microsoft.Azure.PowerShell.AssemblyLoading.ConditionalAssemblyContext]::new($Host.Version)
+ [Microsoft.Azure.PowerShell.AssemblyLoading.ConditionalAssemblyProvider]::Initialize($assemblyRootPath, $conditionalAssemblyContext)
+ $assemblyDict = [Microsoft.Azure.PowerShell.AssemblyLoading.ConditionalAssemblyProvider]::GetAssemblies()
+ return $assemblyDict.Keys
}
$ProjectPaths = @( "$PSScriptRoot\..\artifacts\$BuildConfig" )
@@ -66,7 +61,7 @@ foreach ($ModuleManifest in $ModuleManifestFiles) {
$LoadedAssemblies += $ModuleMetadata.RequiredAssemblies
}
- $LoadedAssemblies += Get-PreloadAssemblies $ModuleManifest.Directory
+ $LoadedAssemblies += Get-PreloadAssemblies -BuildFolder "$PSScriptRoot\..\artifacts\$BuildConfig" -ModuleFolder $ModuleManifest.Directory
$LoadedAssemblies += $ModuleMetadata.NestedModules
if ($ModuleMetadata.RequiredModules) {
@@ -87,7 +82,7 @@ foreach ($ModuleManifest in $ModuleManifestFiles) {
}
$LoadedAssemblies += $ModuleMetadata.NestedModules
}
- $LoadedAssemblies += Get-PreloadAssemblies $RequiredModuleManifest.Directory
+ $LoadedAssemblies += Get-PreloadAssemblies -BuildFolder "$PSScriptRoot\..\artifacts\$BuildConfig" -ModuleFolder $RequiredModuleManifest.Directory
}
}
diff --git a/tools/CleanupBuild.ps1 b/tools/CleanupBuild.ps1
index a39cc4cfbdc5..d766254ddc07 100644
--- a/tools/CleanupBuild.ps1
+++ b/tools/CleanupBuild.ps1
@@ -111,6 +111,9 @@ foreach($RMPath in $resourceManagerPaths)
Write-Host "Removing redundant dlls in $($RMFolder.Name)"
$removedDlls = Get-ChildItem -Path $RMFolder.FullName -Filter "*.dll" -Recurse | where { $acceptedDlls -notcontains $_.Name -and !$_.FullName.Contains("Assemblies") }
+ # do not remove lib dlls (for example Az.Accounts/lib/netcoreapp2.1/Azure.Core.dll)
+ $libPattern = [System.IO.Path]::DirectorySeparatorChar + "lib" + [System.IO.Path]::DirectorySeparatorChar;
+ $removedDlls = $removedDlls | Where-Object { -not $_.FullName.Contains($libPattern) }
$removedDlls | % { Write-Host "Removing $($_.Name)"; Remove-Item $_.FullName -Force }
Write-Host "Removing scripts and psd1 in $($RMFolder.FullName)"
diff --git a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1 b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1
index 6c023bc3ac34..ef2d69283e9d 100644
--- a/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1
+++ b/tools/StaticAnalysis/ExampleAnalyzer/Measure-MarkdownOrScript.ps1
@@ -64,7 +64,7 @@ $null = New-Item -ItemType File $TempScriptPath
if ($PSCmdlet.ParameterSetName -eq "Markdown") {
# When the input $MarkdownPaths is the path of txt file contained markdown paths
if ((Test-Path $MarkdownPaths -PathType Leaf) -and $MarkdownPaths.EndsWith(".txt")) {
- $MarkdownPath = Get-Content $MarkdownPaths | Where-Object { $_.StartsWith("src") }
+ $MarkdownPath = Get-Content $MarkdownPaths | Where-Object { $_.StartsWith("src") -and (Test-Path $_) }
}
# When the input $MarkdownPaths is the path of a folder
else {
diff --git a/tools/Tools.Common/Loaders/CmdletLoader.cs b/tools/Tools.Common/Loaders/CmdletLoader.cs
index 39bfed4a1be2..d1e4f566b6f2 100644
--- a/tools/Tools.Common/Loaders/CmdletLoader.cs
+++ b/tools/Tools.Common/Loaders/CmdletLoader.cs
@@ -44,6 +44,10 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath, List common
{
dll = Directory.GetFiles(commonOutputFolder + "\\PreloadAssemblies", "*.dll").FirstOrDefault(f => Path.GetFileNameWithoutExtension(f) == assemblyName);
}
+ if (dll == null && Directory.Exists(commonOutputFolder + "\\lib"))
+ {
+ dll = Directory.GetFiles(commonOutputFolder + "\\lib", "*.dll", SearchOption.AllDirectories).FirstOrDefault(f => Path.GetFileNameWithoutExtension(f) == assemblyName);
+ }
if (dll == null)
{
continue;
diff --git a/tools/Tools.Common/Loaders/SharedAssemblyLoader.cs b/tools/Tools.Common/Loaders/SharedAssemblyLoader.cs
index 5eec07ca0671..92fe561de783 100644
--- a/tools/Tools.Common/Loaders/SharedAssemblyLoader.cs
+++ b/tools/Tools.Common/Loaders/SharedAssemblyLoader.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
@@ -8,12 +9,16 @@ namespace Tools.Common.Loaders
{
public class SharedAssemblyLoader
{
+ private const string NetCoreApp21 = "netcoreapp2.1";
+ private const string NetCoreApp31 = "netcoreapp3.1";
+ private const string NetStandard20 = "netstandard2.0";
+ private static readonly IEnumerable Frameworks = new string[] { NetCoreApp21, NetCoreApp31, NetStandard20 };
public static HashSet ProcessedFolderSet = new HashSet();
public static void Load(string directory)
{
directory = Path.GetFullPath(directory);
- if(!ProcessedFolderSet.Contains(directory))
+ if (!ProcessedFolderSet.Contains(directory))
{
ProcessedFolderSet.Add(directory);
PreloadSharedAssemblies(directory);
@@ -22,25 +27,29 @@ public static void Load(string directory)
private static void PreloadSharedAssemblies(string directory)
{
- var sharedAssemblyFolder = Path.Combine(directory, "Az.Accounts", "AzSharedAlcAssemblies");
- if (Directory.Exists(sharedAssemblyFolder))
+ var libFolder = Path.Combine(directory, "Az.Accounts", "lib");
+ foreach (string framework in Frameworks)
{
- foreach (var file in Directory.GetFiles(sharedAssemblyFolder))
+ var sharedAssemblyFolder = Path.Combine(libFolder, framework);
+ if (Directory.Exists(sharedAssemblyFolder))
{
- try
+ foreach (var file in Directory.EnumerateFiles(sharedAssemblyFolder, "*.dll"))
{
- Console.WriteLine($"PreloadSharedAssemblies: Starting to load assembly {file}.");
- Assembly.LoadFrom(file);
- }
- catch (Exception e)
- {
- Console.WriteLine($"PreloadSharedAssemblies: Failed (but could be IGNORED) to load assembly {Path.GetFileNameWithoutExtension(file)} with {e}");
+ try
+ {
+ Console.WriteLine($"PreloadSharedAssemblies: Starting to load assembly {file}.");
+ Assembly.LoadFrom(file);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"PreloadSharedAssemblies: Failed (but could be IGNORED) to load assembly {Path.GetFileNameWithoutExtension(file)} with {e}");
+ }
}
}
- }
- else
- {
- Console.WriteLine($"PreloadSharedAssemblies: Could not find directory {sharedAssemblyFolder}.");
+ else
+ {
+ Console.WriteLine($"PreloadSharedAssemblies: Could not find directory {libFolder}.");
+ }
}
}
}