private static AssemblyLoadContext CreateLoadContext(
            string baseDir,
            PluginConfig config,
            Type[] sharedTypes)
        {
            var depsJsonFile = Path.Combine(baseDir, config.MainAssembly.Name + ".deps.json");

            var builder = new AssemblyLoadContextBuilder();

            if (File.Exists(depsJsonFile))
            {
                builder.AddDependencyContext(depsJsonFile);
            }

            builder.SetBaseDirectory(baseDir);

            foreach (var ext in config.PrivateAssemblies)
            {
                builder.PreferLoadContextAssembly(ext);
            }

            if (sharedTypes != null)
            {
                foreach (var type in sharedTypes)
                {
                    builder.PreferDefaultLoadContextAssembly(type.Assembly.GetName());
                }
            }

            var runtimeConfigFile = Path.Combine(baseDir, config.MainAssembly.Name + ".runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(runtimeConfigFile, includeDevConfig: true, out _);

            return(builder.Build());
        }
        private static AssemblyLoadContext CreateLoadContext(string assemblyPath, Core.ILogger logger = null)
        {
            var validatedPath = ValidateFilePath(assemblyPath, logger);

            if (validatedPath == null)
            {
                return(null);
            }

            var mainAssemblyName = Path.GetFileNameWithoutExtension(validatedPath);
            var builder          = new AssemblyLoadContextBuilder();

            // Set base directory.
            var baseDir = Path.GetDirectoryName(validatedPath);

            builder.SetBaseDirectory(baseDir);
            logger?.LogTrace($"Base directory: '{baseDir}'");

            // Add deps file as a source for finding dependencies.
            var depsJsonFile = Path.Combine(baseDir, $"{mainAssemblyName}.deps.json");

            if (File.Exists(depsJsonFile))
            {
                builder.AddDependencyContext(depsJsonFile);
                logger?.LogTrace($"Added '{depsJsonFile}' as a deps file dependency");
            }

            // Add runtimeconfig file as a source for finding dependencies.
            var pluginRuntimeConfigFile = Path.Combine(baseDir, $"{mainAssemblyName}.runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, includeDevConfig: true, out _);

            return(builder.Build());
        }
Beispiel #3
0
        /// <summary>
        /// Creates a new <see cref="LoadContext"/> for individual mods/plugins.
        /// </summary>
        public static LoadContext BuildModLoadContext(string assemblyPath, bool isUnloadable, Type[] sharedTypes, AssemblyLoadContext defaultContext = null)
        {
            var builder = new AssemblyLoadContextBuilder()
                          .SetMainAssemblyPath(assemblyPath)
                          .IsLazyLoaded(true);

            if (defaultContext != null)
            {
                builder.SetDefaultContext(defaultContext);
            }

            if (isUnloadable)
            {
                builder.EnableUnloading();
            }

            foreach (var type in sharedTypes)
            {
                builder.PreferDefaultLoadContextAssembly(type.Assembly.GetName());
            }

            var context = builder.Build();

            return(new LoadContext(context, assemblyPath));
        }
Beispiel #4
0
        private static AssemblyLoadContext CreateLoadContext(PluginConfig config)
        {
            var builder = new AssemblyLoadContextBuilder();

            builder.SetMainAssemblyPath(config.MainAssemblyPath);

            foreach (var ext in config.PrivateAssemblies)
            {
                builder.PreferLoadContextAssembly(ext);
            }

            if (config.PreferSharedTypes)
            {
                builder.PreferDefaultLoadContext(true);
            }

            if (config.IsUnloadable)
            {
                builder.EnableUnloading();
            }

            foreach (var assemblyName in config.SharedAssemblies)
            {
                builder.PreferDefaultLoadContextAssembly(assemblyName);
            }


            builder.SetupSharedAssemblyPrefix(config.SharedAssemblyPrefixes);

            return(builder.Build());
        }
Beispiel #5
0
        private static AssemblyLoadContextBuilder CreateLoadContextBuilder(PluginConfig config)
        {
            var builder = new AssemblyLoadContextBuilder();

            builder.SetMainAssemblyPath(config.MainAssemblyPath);

            foreach (var ext in config.PrivateAssemblies)
            {
                builder.PreferLoadContextAssembly(ext);
            }

            if (config.PreferSharedTypes)
            {
                builder.PreferDefaultLoadContext(true);
            }

#if FEATURE_UNLOAD
            if (config.IsUnloadable || config.EnableHotReload)
            {
                builder.EnableUnloading();
            }

            if (config.EnableHotReload)
            {
                builder.PreloadAssembliesIntoMemory();
            }
#endif

            foreach (var assemblyName in config.SharedAssemblies)
            {
                builder.PreferDefaultLoadContextAssembly(assemblyName);
            }

#if !FEATURE_NATIVE_RESOLVER
            // In .NET Core 3.0, this code is unnecessary because the API, AssemblyDependencyResolver, handles parsing these files.
            var baseDir          = Path.GetDirectoryName(config.MainAssemblyPath);
            var assemblyFileName = Path.GetFileNameWithoutExtension(config.MainAssemblyPath);

            var depsJsonFile = Path.Combine(baseDir, assemblyFileName + ".deps.json");
            if (File.Exists(depsJsonFile))
            {
                builder.AddDependencyContext(depsJsonFile);
            }

            var pluginRuntimeConfigFile = Path.Combine(baseDir, assemblyFileName + ".runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, includeDevConfig: true, out _);

            // Always include runtimeconfig.json from the host app.
            // in some cases, like `dotnet test`, the entry assembly does not actually match with the
            // runtime config file which is why we search for all files matching this extensions.
            foreach (var runtimeconfig in Directory.GetFiles(AppContext.BaseDirectory, "*.runtimeconfig.json"))
            {
                builder.TryAddAdditionalProbingPathFromRuntimeConfig(runtimeconfig, includeDevConfig: true, out _);
            }
#endif

            return(builder);
        }
        private static AssemblyLoadContextBuilder CreateLoadContextBuilder(PluginConfig config)
        {
            var builder = new AssemblyLoadContextBuilder();

            builder.SetMainAssemblyPath(config.MainAssemblyPath);
            builder.SetDefaultContext(config.DefaultContext);

            foreach (var ext in config.PrivateAssemblies)
            {
                builder.PreferLoadContextAssembly(ext);
            }

            if (config.PreferSharedTypes)
            {
                builder.PreferDefaultLoadContext(true);
            }

            if (config.IsUnloadable || config.EnableHotReload)
            {
                builder.EnableUnloading();
            }

            if (config.LoadInMemory)
            {
                builder.PreloadAssembliesIntoMemory();
                builder.ShadowCopyNativeLibraries();
            }

            builder.IsLazyLoaded(config.IsLazyLoaded);
            foreach (var assemblyName in config.SharedAssemblies)
            {
                builder.PreferDefaultLoadContextAssembly(assemblyName);
            }

            var baseDir          = Path.GetDirectoryName(config.MainAssemblyPath);
            var assemblyFileName = Path.GetFileNameWithoutExtension(config.MainAssemblyPath);

            if (baseDir == null)
            {
                throw new InvalidOperationException("Could not determine which directory to watch. "
                                                    + "Please set MainAssemblyPath to an absolute path so its parent directory can be discovered.");
            }

            var pluginRuntimeConfigFile = Path.Combine(baseDir, assemblyFileName + ".runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, includeDevConfig: true, out _);

            // Always include runtimeconfig.json from the host app.
            // in some cases, like `dotnet test`, the entry assembly does not actually match with the
            // runtime config file which is why we search for all files matching this extensions.
            foreach (var runtimeconfig in Directory.GetFiles(AppContext.BaseDirectory, "*.runtimeconfig.json"))
            {
                builder.TryAddAdditionalProbingPathFromRuntimeConfig(runtimeconfig, includeDevConfig: true, out _);
            }

            return(builder);
        }
        private static AssemblyLoadContext CreateLoadContext(
            string baseDir,
            PluginConfig config,
            Type[] sharedTypes,
            PluginLoaderOptions loaderOptions)
        {
            var depsJsonFile = Path.Combine(baseDir, config.MainAssembly.Name + ".deps.json");

            var builder = new AssemblyLoadContextBuilder();

            if (File.Exists(depsJsonFile))
            {
                builder.AddDependencyContext(depsJsonFile);
            }

            builder.SetBaseDirectory(baseDir);

            foreach (var ext in config.PrivateAssemblies)
            {
                builder.PreferLoadContextAssembly(ext);
            }

            if (loaderOptions.HasFlag(PluginLoaderOptions.PreferSharedTypes))
            {
                builder.PreferDefaultLoadContext(true);
            }

#if FEATURE_UNLOAD
            if (loaderOptions.HasFlag(PluginLoaderOptions.IsUnloadable))
            {
                builder.EnableUnloading();
            }
#endif

            if (sharedTypes != null)
            {
                foreach (var type in sharedTypes)
                {
                    builder.PreferDefaultLoadContextAssembly(type.Assembly.GetName());
                }
            }

            var pluginRuntimeConfigFile = Path.Combine(baseDir, config.MainAssembly.Name + ".runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, includeDevConfig: true, out _);

            // Always include runtimeconfig.json from the host app.
            // in some cases, like `dotnet test`, the entry assembly does not actually match with the
            // runtime config file which is why we search for all files matching this extensions.
            foreach (var runtimeconfig in Directory.GetFiles(AppContext.BaseDirectory, "*.runtimeconfig.json"))
            {
                builder.TryAddAdditionalProbingPathFromRuntimeConfig(runtimeconfig, includeDevConfig: true, out _);
            }

            return(builder.Build());
        }
 /// <summary>
 /// Initialize an instance of <see cref="PluginLoader" />
 /// </summary>
 /// <param name="config">The configuration for the plugin.</param>
 public PluginLoader(PluginConfig config)
 {
     _config         = config ?? throw new ArgumentNullException(nameof(config));
     _contextBuilder = CreateLoadContextBuilder(config);
     _context        = (ManagedLoadContext)_contextBuilder.Build();
     if (config.EnableHotReload)
     {
         StartFileWatcher();
     }
 }
Beispiel #9
0
        public void ItUpgradesTypesInContext()
        {
            var samplePath = TestResources.GetTestProjectAssembly("XunitSample");
            var context    = new AssemblyLoadContextBuilder()
                             .AddProbingPath(samplePath)
                             .AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "XunitSample.deps.json"))
                             .PreferDefaultLoadContext(true)
                             .Build();

            Assert.Same(typeof(TheoryData).Assembly, LoadAssembly(context, "xunit.core"));
        }
Beispiel #10
0
        public void ContextsHavePrivateVersionsByDefault()
        {
            var samplePath = TestResources.GetTestProjectAssembly("XunitSample");

            var context = new AssemblyLoadContextBuilder()
                          .SetMainAssemblyPath(samplePath)
                          .AddProbingPath(samplePath)
                          .Build();

            Assert.NotSame(typeof(TheoryData).Assembly, LoadAssembly(context, "xunit.core"));
        }
Beispiel #11
0
        public void ItUpgradesTypesInContext()
        {
            var samplePath = TestResources.GetTestProjectAssembly("XunitSample");
            var context    = new AssemblyLoadContextBuilder()
                             .SetMainAssemblyPath(samplePath)
                             .AddProbingPath(samplePath)
                             .PreferDefaultLoadContext(true)
                             .Build();

            Assert.Same(typeof(TheoryData).Assembly, LoadAssembly(context, "xunit.core"));
        }
Beispiel #12
0
        public void ContextsHavePrivateVersionsByDefault()
        {
            var samplePath = TestResources.GetTestProjectAssembly("XunitSample");

            var context = new AssemblyLoadContextBuilder()
                          .AddProbingPath(samplePath)
                          .AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "XunitSample.deps.json"))
                          .Build();

            Assert.NotSame(typeof(TheoryData).Assembly, LoadAssembly(context, "xunit.core"));
        }
Beispiel #13
0
        /// <summary>
        /// Initialize an instance of <see cref="PluginLoader" />
        /// </summary>
        /// <param name="config">The configuration for the plugin.</param>
        public PluginLoader(PluginConfig config)
        {
            _config         = config ?? throw new ArgumentNullException(nameof(config));
            _contextBuilder = CreateLoadContextBuilder(config);
            _context        = _contextBuilder.Build();
#if FEATURE_UNLOAD
            if (config.EnableHotReload)
            {
                StartFileWatcher();
            }
#endif
        }
Beispiel #14
0
        public static AssemblyLoadContextBuilder AddDependencyContextWithCompileDeps(this AssemblyLoadContextBuilder builder, string depsFilePath)
        {
            var reader = new DependencyContextJsonReader();

            using (var file = File.OpenRead(depsFilePath))
            {
                var deps = reader.Read(file);
                builder.SetBaseDirectory(Path.GetDirectoryName(depsFilePath));
                builder.AddDependencyContext(deps);
                builder.AddCompileDependencies(deps);
            }
            return(builder);
        }
Beispiel #15
0
    /// <summary>
    /// Creates a new empty Shared <see cref="LoadContext"/> used for storing plugin shared interfaces.
    /// </summary>
    public static LoadContext BuildSharedLoadContext()
    {
        var loaderFolder      = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        var interFacesAsmName = typeof(IModLoader).Assembly.GetName();
        var interFacesAsmFile = Path.Combine(loaderFolder, $"{interFacesAsmName.Name}.dll");
        var builder           = new AssemblyLoadContextBuilder()
                                .IsLazyLoaded(true)
                                .PreferDefaultLoadContextAssembly(interFacesAsmName)
                                .SetMainAssemblyPath(interFacesAsmFile);

        var context = builder.Build();

        return(new LoadContext(context, interFacesAsmFile));
    }
Beispiel #16
0
        /// <summary>
        /// Based on https://github.com/natemcmaster/DotNetCorePlugins/blob/master/src/Plugins/PluginLoader.cs
        /// </summary>
        public static AssemblyLoadContext CreateLoadContext(string baseDir,
                                                            AssemblyName mainAssemblyName,
                                                            PluginLoaderOptions loaderOptions = PluginLoaderOptions.None,
                                                            IEnumerable <AssemblyName> preferDefaultLoadContextForAssemblies = null,
                                                            IEnumerable <AssemblyName> privateAssemblies = null)
        {
            var depsJsonFile = Path.Combine(baseDir, mainAssemblyName.Name + ".deps.json");

            var builder = new AssemblyLoadContextBuilder();

            if (File.Exists(depsJsonFile))
            {
                builder.AddDependencyContextWithCompileDeps(depsJsonFile);
            }

            builder.SetBaseDirectory(baseDir);

            if (privateAssemblies != null)
            {
                foreach (var ext in privateAssemblies)
                {
                    builder.PreferLoadContextAssembly(ext);
                }
            }

            if (loaderOptions.HasFlag(PluginLoaderOptions.PreferSharedTypes))
            {
                builder.PreferDefaultLoadContext(true);
            }

            if (preferDefaultLoadContextForAssemblies != null)
            {
                foreach (var assemblyName in preferDefaultLoadContextForAssemblies)
                {
                    builder.PreferDefaultLoadContextAssembly(assemblyName);
                }
            }

            var pluginRuntimeConfigFile = Path.Combine(baseDir, mainAssemblyName.Name + ".runtimeconfig.json");

            builder.TryAddAdditionalProbingPathFromRuntimeConfig(pluginRuntimeConfigFile, true, out _);

            foreach (var runtimeconfig in Directory.GetFiles(AppContext.BaseDirectory, "*.runtimeconfig.json"))
            {
                builder.TryAddAdditionalProbingPathFromRuntimeConfig(runtimeconfig, true, out _);
            }

            return(builder.Build());
        }
Beispiel #17
0
 public static void AddCompileDependencies(this AssemblyLoadContextBuilder builder, DependencyContext dependencyContext)
 {
     foreach (var library in dependencyContext.CompileLibraries.Where(cl => !dependencyContext.RuntimeLibraries.Any(rl => cl.Name.Equals(rl.Name))))
     {
         foreach (var libraryAssembly in library.Assemblies.Where(a => a.StartsWith("lib", StringComparison.OrdinalIgnoreCase)))
         {
             var managedLibrary = ManagedLibrary.CreateFromPackage(library.Name, library.Version, libraryAssembly);
             try
             {
                 builder.AddManagedLibrary(managedLibrary);
             }
             catch (Exception ex)
             {
                 Console.WriteLine(ex.Message);
             }
         }
     }
 }
Beispiel #18
0
        public void ItCanDowngradeUnifiedTypes()
        {
            var samplePath = TestResources.GetTestProjectAssembly("NetCoreApp2App");

            var defaultLoader = new AssemblyLoadContextBuilder()
                                .SetMainAssemblyPath(samplePath)
                                .AddProbingPath(samplePath)
                                .PreferDefaultLoadContext(false)
                                .Build();

            var unifedLoader = new AssemblyLoadContextBuilder()
                               .SetMainAssemblyPath(samplePath)
                               .AddProbingPath(samplePath)
                               .PreferDefaultLoadContext(true)
                               .Build();

            Assert.Equal(new Version("2.0.0.0"), LoadAssembly(defaultLoader, "Test.Referenced.Library").GetName().Version);
            Assert.Equal(new Version("1.0.0.0"), LoadAssembly(unifedLoader, "Test.Referenced.Library").GetName().Version);
        }
Beispiel #19
0
        public void ItCanDowngradeUnifiedTypes()
        {
            var samplePath = TestResources.GetTestProjectAssembly("NetCoreApp20App");

            var defaultLoader = new AssemblyLoadContextBuilder()
                                .AddProbingPath(samplePath)
                                .PreferDefaultLoadContext(false)
                                .AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "NetCoreApp20App.deps.json"))
                                .Build();

            var unifedLoader = new AssemblyLoadContextBuilder()
                               .AddProbingPath(samplePath)
                               .PreferDefaultLoadContext(true)
                               .AddDependencyContext(Path.Combine(Path.GetDirectoryName(samplePath), "NetCoreApp20App.deps.json"))
                               .Build();

            Assert.Equal(new Version("2.0.0.0"), LoadAssembly(defaultLoader, "Test.Referenced.Library").GetName().Version);
            Assert.Equal(new Version("1.0.0.0"), LoadAssembly(unifedLoader, "Test.Referenced.Library").GetName().Version);
        }
Beispiel #20
0
        static AssemblyLoadContext GetAssemblyLoadContext(string assemblyPath, string depsPath, bool enableUnloading)
        {
            var builder = new AssemblyLoadContextBuilder()
                          .SetMainAssemblyPath(assemblyPath)
                          .PreferDefaultLoadContext(true)
                          .AddDependencyContext(depsPath)
                          .AddProbingPath(Path.GetDirectoryName(assemblyPath))
                          .PreferDefaultLoadContextAssembly(typeof(IPlatformStartup).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IServiceCollection).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IServiceCollectionBusConfigurator).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(ILogger).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IBus).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IRabbitMqBusFactoryConfigurator).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IActiveMqBusFactoryConfigurator).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IAmazonSqsBusFactoryConfigurator).Assembly.GetName())
                          .PreferDefaultLoadContextAssembly(typeof(IServiceBusBusFactoryConfigurator).Assembly.GetName());

            if (enableUnloading)
            {
                builder = builder.EnableUnloading();
            }

            return(builder.Build());
        }