mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-19 10:07:55 +08:00
Support for dynamic loading of module references
--HG-- branch : dev
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
@@ -56,10 +57,26 @@ namespace Orchard.Tests.Environment.Extensions {
|
|||||||
get { return this.GetType().Name; }
|
get { return this.GetType().Name; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Assembly LoadReference(ReferenceDescriptor reference) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
||||||
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
|
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
||||||
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
|
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
@@ -55,10 +56,26 @@ namespace Orchard.Tests.Environment.Extensions {
|
|||||||
get { throw new NotImplementedException(); }
|
get { throw new NotImplementedException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Assembly LoadReference(ReferenceDescriptor reference) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
||||||
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
|
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
||||||
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
|
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.CodeDom;
|
using System.CodeDom;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Orchard.Environment.Extensions.Loaders;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
using Orchard.FileSystems.VirtualPath;
|
using Orchard.FileSystems.VirtualPath;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
@@ -9,17 +11,24 @@ using Orchard.Logging;
|
|||||||
|
|
||||||
namespace Orchard.Environment.Extensions.Compilers {
|
namespace Orchard.Environment.Extensions.Compilers {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compile a C# extension into an assembly given a directory location
|
/// Compile an extension project file into an assembly
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultExtensionCompiler : IExtensionCompiler {
|
public class DefaultExtensionCompiler : IExtensionCompiler {
|
||||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||||
private readonly IProjectFileParser _projectFileParser;
|
private readonly IProjectFileParser _projectFileParser;
|
||||||
private readonly IDependenciesFolder _dependenciesFolder;
|
private readonly IDependenciesFolder _dependenciesFolder;
|
||||||
|
private readonly IEnumerable<IExtensionLoader> _loaders;
|
||||||
|
|
||||||
|
public DefaultExtensionCompiler(
|
||||||
|
IVirtualPathProvider virtualPathProvider,
|
||||||
|
IProjectFileParser projectFileParser,
|
||||||
|
IDependenciesFolder dependenciesFolder,
|
||||||
|
IEnumerable<IExtensionLoader> loaders) {
|
||||||
|
|
||||||
public DefaultExtensionCompiler(IVirtualPathProvider virtualPathProvider, IProjectFileParser projectFileParser, IDependenciesFolder dependenciesFolder ) {
|
|
||||||
_virtualPathProvider = virtualPathProvider;
|
_virtualPathProvider = virtualPathProvider;
|
||||||
_projectFileParser = projectFileParser;
|
_projectFileParser = projectFileParser;
|
||||||
_dependenciesFolder = dependenciesFolder;
|
_dependenciesFolder = dependenciesFolder;
|
||||||
|
_loaders = loaders;
|
||||||
|
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
@@ -31,20 +40,33 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
public void Compile(CompileExtensionContext context) {
|
public void Compile(CompileExtensionContext context) {
|
||||||
Logger.Information("Generate code for file \"{0}\"", context.VirtualPath);
|
Logger.Information("Generate code for file \"{0}\"", context.VirtualPath);
|
||||||
var moduleName = Path.GetFileNameWithoutExtension(context.VirtualPath);
|
var moduleName = Path.GetFileNameWithoutExtension(context.VirtualPath);
|
||||||
if (_dependenciesFolder.GetDescriptor(moduleName) == null)
|
var dependencyDescriptor = _dependenciesFolder.GetDescriptor(moduleName);
|
||||||
|
if (dependencyDescriptor == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using (var stream = _virtualPathProvider.OpenFile(context.VirtualPath)) {
|
using (var stream = _virtualPathProvider.OpenFile(context.VirtualPath)) {
|
||||||
var descriptor = _projectFileParser.Parse(stream);
|
var descriptor = _projectFileParser.Parse(stream);
|
||||||
|
|
||||||
|
// Add source files
|
||||||
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
|
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
|
||||||
foreach (var filename in descriptor.SourceFilenames.Select(f => _virtualPathProvider.Combine(directory, f))) {
|
foreach (var filename in descriptor.SourceFilenames.Select(f => _virtualPathProvider.Combine(directory, f))) {
|
||||||
context.AssemblyBuilder.AddCodeCompileUnit(CreateCompileUnit(filename));
|
context.AssemblyBuilder.AddCodeCompileUnit(CreateCompileUnit(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add assembly references
|
||||||
|
foreach (var reference in dependencyDescriptor.References) {
|
||||||
|
var referenceTemp = reference;
|
||||||
|
var loader = _loaders.SingleOrDefault(l => l.Name == referenceTemp.LoaderName);
|
||||||
|
if (loader != null) {
|
||||||
|
var assembly = loader.LoadReference(reference);
|
||||||
|
if (assembly != null)
|
||||||
|
context.AssemblyBuilder.AddAssemblyReference(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e) {
|
catch (Exception e) {
|
||||||
throw new OrchardCoreException(T("Error compiling module \"{0}\" from file \"{1}\"", moduleName, context.VirtualPath), e);
|
throw new OrchardCoreException(T("Error compiling module \"{0}\" from file \"{1}\"", moduleName, context.VirtualPath), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
@@ -40,7 +41,14 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
.Elements(ns("ItemGroup"))
|
.Elements(ns("ItemGroup"))
|
||||||
.Elements(ns("Reference"))
|
.Elements(ns("Reference"))
|
||||||
.Attributes("Include")
|
.Attributes("Include")
|
||||||
.Select(c => new ReferenceDescriptor { AssemblyName = c.Value });
|
.Select(c => new ReferenceDescriptor { AssemblyName = ExtractAssemblyName(c.Value) });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ExtractAssemblyName(string value) {
|
||||||
|
int index = value.IndexOf(',');
|
||||||
|
if (index < 0)
|
||||||
|
return value;
|
||||||
|
return value.Substring(0, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static XName ns(string name) {
|
private static XName ns(string name) {
|
||||||
|
@@ -5,6 +5,7 @@ using Orchard.Caching;
|
|||||||
using Orchard.Environment.Extensions.Loaders;
|
using Orchard.Environment.Extensions.Loaders;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
|
using Orchard.FileSystems.VirtualPath;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.Logging;
|
using Orchard.Logging;
|
||||||
|
|
||||||
@@ -12,19 +13,25 @@ namespace Orchard.Environment.Extensions {
|
|||||||
public class ExtensionLoaderCoordinator : IExtensionLoaderCoordinator {
|
public class ExtensionLoaderCoordinator : IExtensionLoaderCoordinator {
|
||||||
private readonly IDependenciesFolder _dependenciesFolder;
|
private readonly IDependenciesFolder _dependenciesFolder;
|
||||||
private readonly IExtensionManager _extensionManager;
|
private readonly IExtensionManager _extensionManager;
|
||||||
|
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||||
private readonly IEnumerable<IExtensionLoader> _loaders;
|
private readonly IEnumerable<IExtensionLoader> _loaders;
|
||||||
private readonly IHostEnvironment _hostEnvironment;
|
private readonly IHostEnvironment _hostEnvironment;
|
||||||
|
private readonly IBuildManager _buildManager;
|
||||||
|
|
||||||
public ExtensionLoaderCoordinator(
|
public ExtensionLoaderCoordinator(
|
||||||
IDependenciesFolder dependenciesFolder,
|
IDependenciesFolder dependenciesFolder,
|
||||||
IExtensionManager extensionManager,
|
IExtensionManager extensionManager,
|
||||||
|
IVirtualPathProvider virtualPathProvider,
|
||||||
IEnumerable<IExtensionLoader> loaders,
|
IEnumerable<IExtensionLoader> loaders,
|
||||||
IHostEnvironment hostEnvironment) {
|
IHostEnvironment hostEnvironment,
|
||||||
|
IBuildManager buildManager) {
|
||||||
|
|
||||||
_dependenciesFolder = dependenciesFolder;
|
_dependenciesFolder = dependenciesFolder;
|
||||||
_extensionManager = extensionManager;
|
_extensionManager = extensionManager;
|
||||||
|
_virtualPathProvider = virtualPathProvider;
|
||||||
_loaders = loaders.OrderBy(l => l.Order);
|
_loaders = loaders.OrderBy(l => l.Order);
|
||||||
_hostEnvironment = hostEnvironment;
|
_hostEnvironment = hostEnvironment;
|
||||||
|
_buildManager = buildManager;
|
||||||
|
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
@@ -36,50 +43,76 @@ namespace Orchard.Environment.Extensions {
|
|||||||
public void SetupExtensions() {
|
public void SetupExtensions() {
|
||||||
Logger.Information("Loading extensions.");
|
Logger.Information("Loading extensions.");
|
||||||
|
|
||||||
var extensions = _extensionManager.AvailableExtensions().Where(d => d.ExtensionType == "Module").ToList();
|
var context = CreateLoadingContext();
|
||||||
var existingDependencies = _dependenciesFolder.LoadDescriptors().ToList();
|
|
||||||
var deletedDependencies = existingDependencies.Where(e => !extensions.Any(e2 => StringComparer.OrdinalIgnoreCase.Equals(e2.Name, e.Name))).ToList();
|
|
||||||
|
|
||||||
var loadingContext = new ExtensionLoadingContext();
|
|
||||||
|
|
||||||
// Notify all loaders about extensions removed from the web site
|
// Notify all loaders about extensions removed from the web site
|
||||||
foreach (var dependency in deletedDependencies) {
|
foreach (var dependency in context.DeletedDependencies) {
|
||||||
Logger.Information("Extension {0} has been removed from site", dependency.Name);
|
Logger.Information("Extension {0} has been removed from site", dependency.Name);
|
||||||
foreach (var loader in _loaders) {
|
foreach (var loader in _loaders) {
|
||||||
if (dependency.LoaderName == loader.Name) {
|
if (dependency.LoaderName == loader.Name) {
|
||||||
loader.ExtensionRemoved(loadingContext, dependency);
|
loader.ExtensionRemoved(context, dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all existing extensions in the site, ask each loader if they can
|
// For all existing extensions in the site, ask each loader if they can
|
||||||
// load that extension.
|
// load that extension.
|
||||||
var newDependencies = new List<DependencyDescriptor>();
|
foreach (var extension in context.AvailableExtensions) {
|
||||||
foreach (var extension in extensions) {
|
ProcessExtension(context, extension);
|
||||||
ProcessExtension(loadingContext, extension, existingDependencies, newDependencies);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute all the work need by "ctx"
|
// Execute all the work need by "ctx"
|
||||||
ProcessContextCommands(loadingContext);
|
ProcessContextCommands(context);
|
||||||
|
|
||||||
// And finally save the new entries in the dependencies folder
|
// And finally save the new entries in the dependencies folder
|
||||||
_dependenciesFolder.StoreDescriptors(newDependencies);
|
_dependenciesFolder.StoreDescriptors(context.NewDependencies);
|
||||||
Logger.Information("Done loading extensions.");
|
Logger.Information("Done loading extensions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessExtension(
|
private ExtensionLoadingContext CreateLoadingContext() {
|
||||||
ExtensionLoadingContext loadingContext,
|
var availableExtensions = _extensionManager.AvailableExtensions().Where(d => d.ExtensionType == "Module").ToList();
|
||||||
ExtensionDescriptor extension,
|
var previousDependencies = _dependenciesFolder.LoadDescriptors().ToList();
|
||||||
IEnumerable<DependencyDescriptor> existingDependencies,
|
var availableExtensionsProbes = availableExtensions.SelectMany(extension => _loaders
|
||||||
List<DependencyDescriptor> newDependencies) {
|
.Select(loader => loader.Probe(extension))
|
||||||
|
.Where(probe => probe != null))
|
||||||
|
.GroupBy(e => e.Descriptor.Name)
|
||||||
|
.ToDictionary(g => g.Key, g => g.AsEnumerable()
|
||||||
|
.OrderByDescending(probe => probe.LastModificationTimeUtc)
|
||||||
|
.ThenBy(probe => probe.Loader.Order), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var extensionProbes = _loaders
|
var deletedDependencies = previousDependencies
|
||||||
.Select(loader => loader.Probe(extension))
|
.Where(e => !availableExtensions.Any(e2 => StringComparer.OrdinalIgnoreCase.Equals(e2.Name, e.Name)))
|
||||||
.Where(probe => probe != null)
|
|
||||||
.OrderByDescending(probe => probe.LastModificationTimeUtc)
|
|
||||||
.ThenBy(probe => probe.Loader.Order)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// Collect references for all modules
|
||||||
|
var references =
|
||||||
|
availableExtensions
|
||||||
|
.SelectMany(extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var referencesByModule = references
|
||||||
|
.GroupBy(entry => entry.Descriptor.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var referencesByName = references
|
||||||
|
.GroupBy(reference => reference.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
return new ExtensionLoadingContext {
|
||||||
|
AvailableExtensions = availableExtensions,
|
||||||
|
PreviousDependencies = previousDependencies,
|
||||||
|
DeletedDependencies = deletedDependencies,
|
||||||
|
AvailableExtensionsProbes = availableExtensionsProbes,
|
||||||
|
ReferencesByName = referencesByName,
|
||||||
|
ReferencesByModule = referencesByModule
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessExtension(ExtensionLoadingContext context, ExtensionDescriptor extension) {
|
||||||
|
|
||||||
|
var extensionProbes = context.AvailableExtensionsProbes.ContainsKey(extension.Name) ?
|
||||||
|
context.AvailableExtensionsProbes[extension.Name] :
|
||||||
|
Enumerable.Empty<ExtensionProbeEntry>();
|
||||||
|
|
||||||
if (Logger.IsEnabled(LogLevel.Debug)) {
|
if (Logger.IsEnabled(LogLevel.Debug)) {
|
||||||
Logger.Debug("Loaders for extension \"{0}\": ", extension.Name);
|
Logger.Debug("Loaders for extension \"{0}\": ", extension.Name);
|
||||||
@@ -91,28 +124,110 @@ namespace Orchard.Environment.Extensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var activatedExtension = extensionProbes.FirstOrDefault();
|
var activatedExtension = extensionProbes.FirstOrDefault();
|
||||||
var previousDependency = existingDependencies.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Name)).FirstOrDefault();
|
var previousDependency = context.PreviousDependencies.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Name)).FirstOrDefault();
|
||||||
|
|
||||||
if (activatedExtension == null) {
|
if (activatedExtension == null) {
|
||||||
Logger.Warning("No loader found for extension \"{0}\"!", extension.Name);
|
Logger.Warning("No loader found for extension \"{0}\"!", extension.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var references = ProcessExtensionReferences(context, activatedExtension);
|
||||||
|
|
||||||
foreach (var loader in _loaders) {
|
foreach (var loader in _loaders) {
|
||||||
if (activatedExtension != null && activatedExtension.Loader.Name == loader.Name) {
|
if (activatedExtension != null && activatedExtension.Loader.Name == loader.Name) {
|
||||||
Logger.Information("Activating extension \"{0}\" with loader \"{1}\"", activatedExtension.Descriptor.Name, loader.Name);
|
Logger.Information("Activating extension \"{0}\" with loader \"{1}\"", activatedExtension.Descriptor.Name, loader.Name);
|
||||||
loader.ExtensionActivated(loadingContext, extension);
|
loader.ExtensionActivated(context, extension);
|
||||||
}
|
}
|
||||||
else if (previousDependency != null && previousDependency.LoaderName == loader.Name) {
|
else if (previousDependency != null && previousDependency.LoaderName == loader.Name) {
|
||||||
Logger.Information("Deactivating extension \"{0}\" from loader \"{1}\"", previousDependency.Name, loader.Name);
|
Logger.Information("Deactivating extension \"{0}\" from loader \"{1}\"", previousDependency.Name, loader.Name);
|
||||||
loader.ExtensionDeactivated(loadingContext, extension);
|
loader.ExtensionDeactivated(context, extension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activatedExtension != null) {
|
if (activatedExtension != null) {
|
||||||
newDependencies.Add(new DependencyDescriptor {
|
context.NewDependencies.Add(new DependencyDescriptor {
|
||||||
Name = extension.Name,
|
Name = extension.Name,
|
||||||
LoaderName = activatedExtension.Loader.Name,
|
LoaderName = activatedExtension.Loader.Name,
|
||||||
VirtualPath = activatedExtension.VirtualPath
|
VirtualPath = activatedExtension.VirtualPath,
|
||||||
|
References = references
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<ReferenceDescriptor> ProcessExtensionReferences(ExtensionLoadingContext context, ExtensionProbeEntry activatedExtension) {
|
||||||
|
if (activatedExtension == null)
|
||||||
|
return Enumerable.Empty<ReferenceDescriptor>();
|
||||||
|
|
||||||
|
var referenceNames = (context.ReferencesByModule.ContainsKey(activatedExtension.Descriptor.Name) ?
|
||||||
|
context.ReferencesByModule[activatedExtension.Descriptor.Name] :
|
||||||
|
Enumerable.Empty<ExtensionReferenceEntry>())
|
||||||
|
.Select(r => r.Name)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var referencesDecriptors = new List<ReferenceDescriptor>();
|
||||||
|
foreach (var referenceName in referenceNames) {
|
||||||
|
ProcessExtensionReference(context, activatedExtension, referenceName, referencesDecriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return referencesDecriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessExtensionReference(ExtensionLoadingContext context,
|
||||||
|
ExtensionProbeEntry activatedExtension,
|
||||||
|
string referenceName,
|
||||||
|
IList<ReferenceDescriptor> activatedReferences) {
|
||||||
|
|
||||||
|
// Assemblies loaded by the BuildManager are ignored, since
|
||||||
|
// we don't want to update them and they are automatically
|
||||||
|
// referenced by the build manager
|
||||||
|
if (_buildManager.GetReferencedAssemblies().Any(a => StringComparer.OrdinalIgnoreCase.Equals(a.GetName().Name, referenceName)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var references = context.ReferencesByName.ContainsKey(referenceName) ?
|
||||||
|
context.ReferencesByName[referenceName] :
|
||||||
|
Enumerable.Empty<ExtensionReferenceEntry>();
|
||||||
|
|
||||||
|
// Binary references
|
||||||
|
var bestBinaryReference = references
|
||||||
|
.Where(entry => !string.IsNullOrEmpty(entry.VirtualPath))
|
||||||
|
.Select(entry => new { Entry = entry, LastWriteTimeUtc = _virtualPathProvider.GetFileLastWriteTimeUtc(entry.VirtualPath) })
|
||||||
|
.OrderBy(e => e.LastWriteTimeUtc)
|
||||||
|
.ThenBy(e => e.Entry.Name).FirstOrDefault();
|
||||||
|
|
||||||
|
var probes = context.AvailableExtensionsProbes.ContainsKey(referenceName) ?
|
||||||
|
context.AvailableExtensionsProbes[referenceName] :
|
||||||
|
Enumerable.Empty<ExtensionProbeEntry>();
|
||||||
|
var bestProbe = probes.FirstOrDefault();
|
||||||
|
|
||||||
|
// Pick the best one of module vs binary
|
||||||
|
if (bestProbe != null && bestBinaryReference != null) {
|
||||||
|
if (bestProbe.LastModificationTimeUtc >= bestBinaryReference.LastWriteTimeUtc) {
|
||||||
|
bestBinaryReference = null;
|
||||||
|
} else {
|
||||||
|
bestProbe = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate the binary ref
|
||||||
|
if (bestBinaryReference != null) {
|
||||||
|
if (!context.ProcessedReferences.Contains(bestBinaryReference.Entry.Name)) {
|
||||||
|
context.ProcessedReferences.Add(bestBinaryReference.Entry.Name);
|
||||||
|
bestBinaryReference.Entry.Loader.ReferenceActivated(context, bestBinaryReference.Entry);
|
||||||
|
}
|
||||||
|
activatedReferences.Add(new ReferenceDescriptor {
|
||||||
|
LoaderName = bestBinaryReference.Entry.Loader.Name,
|
||||||
|
Name = bestBinaryReference.Entry.Name,
|
||||||
|
VirtualPath = bestBinaryReference.Entry.VirtualPath
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activated the module ref
|
||||||
|
if (bestProbe != null)
|
||||||
|
{
|
||||||
|
activatedReferences.Add(new ReferenceDescriptor {
|
||||||
|
LoaderName = bestProbe.Loader.Name,
|
||||||
|
Name = referenceName,
|
||||||
|
VirtualPath = bestProbe.VirtualPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,61 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Orchard.Environment.Extensions.Loaders;
|
||||||
|
using Orchard.Environment.Extensions.Models;
|
||||||
|
using Orchard.FileSystems.Dependencies;
|
||||||
|
|
||||||
namespace Orchard.Environment.Extensions {
|
namespace Orchard.Environment.Extensions {
|
||||||
public class ExtensionLoadingContext {
|
public class ExtensionLoadingContext {
|
||||||
public ExtensionLoadingContext() {
|
public ExtensionLoadingContext() {
|
||||||
|
ProcessedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
ProcessedReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
DeleteActions = new List<Action>();
|
DeleteActions = new List<Action>();
|
||||||
CopyActions = new List<Action>();
|
CopyActions = new List<Action>();
|
||||||
|
NewDependencies = new List<DependencyDescriptor>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Action> DeleteActions { get; set; }
|
public ISet<string> ProcessedExtensions { get; private set; }
|
||||||
public IList<Action> CopyActions { get; set; }
|
public ISet<string> ProcessedReferences { get; private set; }
|
||||||
|
|
||||||
|
public IList<DependencyDescriptor> NewDependencies { get; private set; }
|
||||||
|
|
||||||
|
public IList<Action> DeleteActions { get; private set; }
|
||||||
|
public IList<Action> CopyActions { get; private set; }
|
||||||
|
|
||||||
public bool RestartAppDomain { get; set; }
|
public bool RestartAppDomain { get; set; }
|
||||||
public bool ResetSiteCompilation { get; set; }
|
public bool ResetSiteCompilation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of extensions (modules) present in the system
|
||||||
|
/// </summary>
|
||||||
|
public List<ExtensionDescriptor> AvailableExtensions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of extensions (modules) that were loaded during a previous successful run
|
||||||
|
/// </summary>
|
||||||
|
public List<DependencyDescriptor> PreviousDependencies { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of extensions/modules that are were present in the previous successful run
|
||||||
|
/// and that are not present in the system anymore.
|
||||||
|
/// </summary>
|
||||||
|
public List<DependencyDescriptor> DeletedDependencies { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For every extension name, the list of loaders that can potentially load
|
||||||
|
/// that extension (in order of "best-of" applicable)
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, IOrderedEnumerable<ExtensionProbeEntry>> AvailableExtensionsProbes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For every reference name, list of potential loaders/locations
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, IEnumerable<ExtensionReferenceEntry>> ReferencesByModule { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For every extension name, list of references
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, IEnumerable<ExtensionReferenceEntry>> ReferencesByName { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,29 +2,35 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
|
using Orchard.Environment.Extensions.Compilers;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
using Orchard.FileSystems.VirtualPath;
|
using Orchard.FileSystems.VirtualPath;
|
||||||
using Orchard.Logging;
|
using Orchard.Logging;
|
||||||
|
using ReferenceDescriptor = Orchard.FileSystems.Dependencies.ReferenceDescriptor;
|
||||||
|
|
||||||
namespace Orchard.Environment.Extensions.Loaders {
|
namespace Orchard.Environment.Extensions.Loaders {
|
||||||
public class DynamicExtensionLoader : ExtensionLoaderBase {
|
public class DynamicExtensionLoader : ExtensionLoaderBase {
|
||||||
private readonly IBuildManager _buildManager;
|
private readonly IBuildManager _buildManager;
|
||||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||||
private readonly IVirtualPathMonitor _virtualPathMonitor;
|
private readonly IVirtualPathMonitor _virtualPathMonitor;
|
||||||
|
private readonly IProjectFileParser _projectFileParser;
|
||||||
private static readonly ReloadWorkaround _reloadWorkaround = new ReloadWorkaround();
|
private static readonly ReloadWorkaround _reloadWorkaround = new ReloadWorkaround();
|
||||||
|
|
||||||
public DynamicExtensionLoader(
|
public DynamicExtensionLoader(
|
||||||
IBuildManager buildManager,
|
IBuildManager buildManager,
|
||||||
IVirtualPathProvider virtualPathProvider,
|
IVirtualPathProvider virtualPathProvider,
|
||||||
IVirtualPathMonitor virtualPathMonitor,
|
IVirtualPathMonitor virtualPathMonitor,
|
||||||
IDependenciesFolder dependenciesFolder)
|
IDependenciesFolder dependenciesFolder,
|
||||||
|
IProjectFileParser projectFileParser)
|
||||||
: base(dependenciesFolder) {
|
: base(dependenciesFolder) {
|
||||||
|
|
||||||
_buildManager = buildManager;
|
_buildManager = buildManager;
|
||||||
_virtualPathProvider = virtualPathProvider;
|
_virtualPathProvider = virtualPathProvider;
|
||||||
_virtualPathMonitor = virtualPathMonitor;
|
_virtualPathMonitor = virtualPathMonitor;
|
||||||
|
_projectFileParser = projectFileParser;
|
||||||
|
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
@@ -74,6 +80,27 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
|
||||||
|
string projectPath = GetProjectPath(descriptor);
|
||||||
|
if (projectPath == null)
|
||||||
|
return Enumerable.Empty<ExtensionReferenceEntry>();
|
||||||
|
|
||||||
|
using(var stream = _virtualPathProvider.OpenFile(projectPath)) {
|
||||||
|
var projectFile = _projectFileParser.Parse(stream);
|
||||||
|
|
||||||
|
return projectFile.References.Select(r => new ExtensionReferenceEntry {
|
||||||
|
Descriptor = descriptor,
|
||||||
|
Loader = this,
|
||||||
|
Name = r.AssemblyName,
|
||||||
|
VirtualPath = null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Assembly LoadReference(ReferenceDescriptor reference) {
|
||||||
|
return _buildManager.GetCompiledAssembly(reference.VirtualPath);
|
||||||
|
}
|
||||||
|
|
||||||
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
||||||
string projectPath = GetProjectPath(descriptor);
|
string projectPath = GetProjectPath(descriptor);
|
||||||
if (projectPath == null)
|
if (projectPath == null)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
@@ -16,6 +17,14 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
public abstract int Order { get; }
|
public abstract int Order { get; }
|
||||||
public string Name { get { return this.GetType().Name; } }
|
public string Name { get { return this.GetType().Name; } }
|
||||||
|
|
||||||
|
public virtual IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
|
||||||
|
return Enumerable.Empty<ExtensionReferenceEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Assembly LoadReference(ReferenceDescriptor reference) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
|
public abstract ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
|
||||||
|
|
||||||
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
||||||
@@ -26,9 +35,13 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) { }
|
||||||
|
public virtual void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) { }
|
||||||
|
|
||||||
public virtual void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
|
public virtual void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
|
||||||
public virtual void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
|
public virtual void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
|
||||||
public virtual void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) { }
|
public virtual void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) { }
|
||||||
|
|
||||||
public virtual void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor) { }
|
public virtual void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor) { }
|
||||||
|
|
||||||
protected abstract ExtensionEntry LoadWorker(ExtensionDescriptor descriptor);
|
protected abstract ExtensionEntry LoadWorker(ExtensionDescriptor descriptor);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
@@ -12,10 +13,22 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
public DateTime LastModificationTimeUtc { get; set; }
|
public DateTime LastModificationTimeUtc { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ExtensionReferenceEntry {
|
||||||
|
public ExtensionDescriptor Descriptor { get; set; }
|
||||||
|
public IExtensionLoader Loader { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string VirtualPath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public interface IExtensionLoader {
|
public interface IExtensionLoader {
|
||||||
int Order { get; }
|
int Order { get; }
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
|
IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor);
|
||||||
|
Assembly LoadReference(ReferenceDescriptor reference);
|
||||||
|
void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry);
|
||||||
|
void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry);
|
||||||
|
|
||||||
ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
|
ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
|
||||||
ExtensionEntry Load(ExtensionDescriptor descriptor);
|
ExtensionEntry Load(ExtensionDescriptor descriptor);
|
||||||
|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.FileSystems.Dependencies;
|
using Orchard.FileSystems.Dependencies;
|
||||||
@@ -93,6 +95,28 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
|
||||||
|
if (string.IsNullOrEmpty(referenceEntry.VirtualPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string sourceFileName = _virtualPathProvider.MapPath(referenceEntry.VirtualPath);
|
||||||
|
|
||||||
|
// Copy the assembly if it doesn't exist or if it is older than the source file.
|
||||||
|
bool copyAssembly =
|
||||||
|
!_assemblyProbingFolder.AssemblyExists(referenceEntry.Name) ||
|
||||||
|
File.GetLastWriteTimeUtc(sourceFileName) > _assemblyProbingFolder.GetAssemblyDateTimeUtc(referenceEntry.Name);
|
||||||
|
|
||||||
|
if (copyAssembly) {
|
||||||
|
context.CopyActions.Add(() => _assemblyProbingFolder.StoreAssembly(referenceEntry.Name, sourceFileName));
|
||||||
|
|
||||||
|
// We need to restart the appDomain if the assembly is loaded
|
||||||
|
if (IsAssemblyLoaded(referenceEntry.Name)) {
|
||||||
|
Logger.Information("ReferenceActivated: Reference \"{0}\" is activated with newer file and its assembly is loaded, forcing AppDomain restart", referenceEntry.Name);
|
||||||
|
context.RestartAppDomain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void Monitor(ExtensionDescriptor descriptor, Action<IVolatileToken> monitor) {
|
public override void Monitor(ExtensionDescriptor descriptor, Action<IVolatileToken> monitor) {
|
||||||
string assemblyPath = GetAssemblyPath(descriptor);
|
string assemblyPath = GetAssemblyPath(descriptor);
|
||||||
if (assemblyPath != null) {
|
if (assemblyPath != null) {
|
||||||
@@ -101,6 +125,23 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
|
||||||
|
var assemblyPath = GetAssemblyPath(descriptor);
|
||||||
|
if (assemblyPath == null)
|
||||||
|
return Enumerable.Empty<ExtensionReferenceEntry>();
|
||||||
|
|
||||||
|
return _virtualPathProvider
|
||||||
|
.ListFiles(_virtualPathProvider.GetDirectoryName(assemblyPath))
|
||||||
|
.Where(s => StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(s), ".dll"))
|
||||||
|
.Where(s => !StringComparer.OrdinalIgnoreCase.Equals(Path.GetFileNameWithoutExtension(s), descriptor.Name))
|
||||||
|
.Select(path => new ExtensionReferenceEntry {
|
||||||
|
Descriptor = descriptor,
|
||||||
|
Loader = this,
|
||||||
|
Name = Path.GetFileNameWithoutExtension(path),
|
||||||
|
VirtualPath = path
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
|
||||||
var assemblyPath = GetAssemblyPath(descriptor);
|
var assemblyPath = GetAssemblyPath(descriptor);
|
||||||
if (assemblyPath == null)
|
if (assemblyPath == null)
|
||||||
@@ -114,6 +155,10 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Assembly LoadReference(ReferenceDescriptor reference) {
|
||||||
|
return _assemblyProbingFolder.LoadAssembly(reference.Name);
|
||||||
|
}
|
||||||
|
|
||||||
protected override ExtensionEntry LoadWorker(ExtensionDescriptor descriptor) {
|
protected override ExtensionEntry LoadWorker(ExtensionDescriptor descriptor) {
|
||||||
var assembly = _assemblyProbingFolder.LoadAssembly(descriptor.Name);
|
var assembly = _assemblyProbingFolder.LoadAssembly(descriptor.Name);
|
||||||
if (assembly == null)
|
if (assembly == null)
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
using System.CodeDom;
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
|
using System.Reflection;
|
||||||
using System.Web.Compilation;
|
using System.Web.Compilation;
|
||||||
|
|
||||||
namespace Orchard.Environment {
|
namespace Orchard.Environment {
|
||||||
public interface IAssemblyBuilder {
|
public interface IAssemblyBuilder {
|
||||||
void AddCodeCompileUnit(CodeCompileUnit compileUnit);
|
void AddCodeCompileUnit(CodeCompileUnit compileUnit);
|
||||||
|
void AddAssemblyReference(Assembly assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AspNetAssemblyBuilder : IAssemblyBuilder {
|
public class AspNetAssemblyBuilder : IAssemblyBuilder {
|
||||||
@@ -18,5 +21,9 @@ namespace Orchard.Environment {
|
|||||||
public void AddCodeCompileUnit(CodeCompileUnit compileUnit) {
|
public void AddCodeCompileUnit(CodeCompileUnit compileUnit) {
|
||||||
_assemblyBuilder.AddCodeCompileUnit(_buildProvider, compileUnit);
|
_assemblyBuilder.AddCodeCompileUnit(_buildProvider, compileUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddAssemblyReference(Assembly assembly) {
|
||||||
|
_assemblyBuilder.AddAssemblyReference(assembly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -69,9 +69,12 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
.Select(e => new DependencyDescriptor {
|
.Select(e => new DependencyDescriptor {
|
||||||
Name = elem(e, "ModuleName"),
|
Name = elem(e, "ModuleName"),
|
||||||
VirtualPath = elem(e, "VirtualPath"),
|
VirtualPath = elem(e, "VirtualPath"),
|
||||||
LoaderName = elem(e, "LoaderName")
|
LoaderName = elem(e, "LoaderName"),
|
||||||
})
|
References = e.Elements(ns("References")).Elements(ns("Reference")).Select(r => new ReferenceDescriptor {
|
||||||
.ToList();
|
Name = elem(r, "Name"),
|
||||||
|
LoaderName = elem(r, "LoaderName"),
|
||||||
|
VirtualPath = elem(r, "VirtualPath")
|
||||||
|
})}).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +86,13 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
var elements = dependencies.Select(d => new XElement("Dependency",
|
var elements = dependencies.Select(d => new XElement("Dependency",
|
||||||
new XElement(ns("ModuleName"), d.Name),
|
new XElement(ns("ModuleName"), d.Name),
|
||||||
new XElement(ns("VirtualPath"), d.VirtualPath),
|
new XElement(ns("VirtualPath"), d.VirtualPath),
|
||||||
new XElement(ns("LoaderName"), d.LoaderName)));
|
new XElement(ns("LoaderName"), d.LoaderName),
|
||||||
|
new XElement(ns("References"), d.References
|
||||||
|
.Select(r => new XElement(ns("Reference"),
|
||||||
|
new XElement(ns("Name"), r.Name),
|
||||||
|
new XElement(ns("LoaderName"), r.LoaderName),
|
||||||
|
new XElement(ns("VirtualPath"), r.VirtualPath))).ToArray())));
|
||||||
|
|
||||||
document.Root.Add(elements);
|
document.Root.Add(elements);
|
||||||
|
|
||||||
using (var stream = _appDataFolder.CreateFile(persistancePath)) {
|
using (var stream = _appDataFolder.CreateFile(persistancePath)) {
|
||||||
@@ -99,7 +108,27 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class DependencyDescriptorComparer : EqualityComparer<DependencyDescriptor> {
|
private class DependencyDescriptorComparer : EqualityComparer<DependencyDescriptor> {
|
||||||
|
private readonly ReferenceDescriptorComparer _referenceDescriptorComparer = new ReferenceDescriptorComparer();
|
||||||
|
|
||||||
public override bool Equals(DependencyDescriptor x, DependencyDescriptor y) {
|
public override bool Equals(DependencyDescriptor x, DependencyDescriptor y) {
|
||||||
|
return
|
||||||
|
StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name) &&
|
||||||
|
StringComparer.OrdinalIgnoreCase.Equals(x.LoaderName, y.LoaderName) &&
|
||||||
|
StringComparer.OrdinalIgnoreCase.Equals(x.VirtualPath, y.VirtualPath) &&
|
||||||
|
x.References.SequenceEqual(y.References, _referenceDescriptorComparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode(DependencyDescriptor obj) {
|
||||||
|
return
|
||||||
|
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name) ^
|
||||||
|
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.LoaderName) ^
|
||||||
|
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.VirtualPath) ^
|
||||||
|
obj.References.Aggregate(0, (a, entry) => a + _referenceDescriptorComparer.GetHashCode(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReferenceDescriptorComparer : EqualityComparer<ReferenceDescriptor> {
|
||||||
|
public override bool Equals(ReferenceDescriptor x, ReferenceDescriptor y) {
|
||||||
return
|
return
|
||||||
StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name) &&
|
StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name) &&
|
||||||
StringComparer.OrdinalIgnoreCase.Equals(x.LoaderName, y.LoaderName) &&
|
StringComparer.OrdinalIgnoreCase.Equals(x.LoaderName, y.LoaderName) &&
|
||||||
@@ -107,7 +136,7 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode(DependencyDescriptor obj) {
|
public override int GetHashCode(ReferenceDescriptor obj) {
|
||||||
return
|
return
|
||||||
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name) ^
|
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name) ^
|
||||||
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.LoaderName) ^
|
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.LoaderName) ^
|
||||||
|
@@ -6,6 +6,13 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string LoaderName { get; set; }
|
public string LoaderName { get; set; }
|
||||||
public string VirtualPath { get; set; }
|
public string VirtualPath { get; set; }
|
||||||
|
public IEnumerable<ReferenceDescriptor> References { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReferenceDescriptor {
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string LoaderName { get; set; }
|
||||||
|
public string VirtualPath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IDependenciesFolder : IVolatileProvider {
|
public interface IDependenciesFolder : IVolatileProvider {
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Web.Hosting;
|
using System.Web.Hosting;
|
||||||
|
|
||||||
namespace Orchard.FileSystems.VirtualPath {
|
namespace Orchard.FileSystems.VirtualPath {
|
||||||
@@ -7,6 +10,14 @@ namespace Orchard.FileSystems.VirtualPath {
|
|||||||
return Path.GetDirectoryName(virtualPath).Replace(Path.DirectorySeparatorChar, '/');
|
return Path.GetDirectoryName(virtualPath).Replace(Path.DirectorySeparatorChar, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> ListFiles(string path) {
|
||||||
|
return HostingEnvironment.VirtualPathProvider.GetDirectory(path).Files.OfType<VirtualFile>().Select(f => f.VirtualPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> ListDirectories(string path) {
|
||||||
|
return HostingEnvironment.VirtualPathProvider.GetDirectory(path).Directories.OfType<VirtualDirectory>().Select(d => d.VirtualPath);
|
||||||
|
}
|
||||||
|
|
||||||
public string Combine(params string[] paths) {
|
public string Combine(params string[] paths) {
|
||||||
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
|
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
|
||||||
}
|
}
|
||||||
@@ -19,6 +30,10 @@ namespace Orchard.FileSystems.VirtualPath {
|
|||||||
return File.CreateText(MapPath(virtualPath));
|
return File.CreateText(MapPath(virtualPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateTime GetFileLastWriteTimeUtc(string virtualPath) {
|
||||||
|
return File.GetLastWriteTimeUtc(MapPath(virtualPath));
|
||||||
|
}
|
||||||
|
|
||||||
public string MapPath(string virtualPath) {
|
public string MapPath(string virtualPath) {
|
||||||
return HostingEnvironment.MapPath(virtualPath);
|
return HostingEnvironment.MapPath(virtualPath);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using Orchard.Caching;
|
using Orchard.Caching;
|
||||||
|
|
||||||
namespace Orchard.FileSystems.VirtualPath {
|
namespace Orchard.FileSystems.VirtualPath {
|
||||||
@@ -9,9 +11,13 @@ namespace Orchard.FileSystems.VirtualPath {
|
|||||||
bool FileExists(string virtualPath);
|
bool FileExists(string virtualPath);
|
||||||
Stream OpenFile(string virtualPath);
|
Stream OpenFile(string virtualPath);
|
||||||
StreamWriter CreateText(string virtualPath);
|
StreamWriter CreateText(string virtualPath);
|
||||||
|
DateTime GetFileLastWriteTimeUtc(string virtualPath);
|
||||||
|
|
||||||
bool DirectoryExists(string virtualPath);
|
bool DirectoryExists(string virtualPath);
|
||||||
void CreateDirectory(string virtualPath);
|
void CreateDirectory(string virtualPath);
|
||||||
string GetDirectoryName(string virtualPath);
|
string GetDirectoryName(string virtualPath);
|
||||||
|
|
||||||
|
IEnumerable<string> ListFiles(string path);
|
||||||
|
IEnumerable<string> ListDirectories(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user