Adding new ProbingExtensionLoader, and fixing DynamicCompilation priority.

The DyanmicExtensionLoader could randomly take over PrecompiledExtensionLoader
as the Priority was not set on ProbeExtensionEntry.

Also a new ProbingExtensionLoader is added and is used when DyanmicExtensionLoader
is disabled. It will compile an extension dynamically but cache the assembly in
the probing folder (Dependencies) so that it's not recompiled on each app start,
and such that there is no need to inspect dependency files.

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2012-08-15 17:47:41 -07:00
parent f79444d9ec
commit 26a21344e2
7 changed files with 235 additions and 1 deletions

View File

@@ -21,6 +21,10 @@
</UpgradeBackupLocation>
<TargetFrameworkProfile />
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View File

@@ -171,6 +171,7 @@ namespace Orchard.Environment.Extensions.Loaders {
var result = new ExtensionProbeEntry {
Descriptor = descriptor,
Loader = this,
Priority = 50,
VirtualPath = projectPath,
VirtualPathDependencies = GetDependencies(projectPath).ToList(),
};

View File

@@ -173,7 +173,7 @@ namespace Orchard.Environment.Extensions.Loaders {
// A pre-compiled module is _not_ compatible with a dynamically loaded module
// because a pre-compiled module usually references a pre-compiled assembly binary
// which will have a different identity (i.e. name) from the dynamic module.
bool result = references.All(r => r.Loader.GetType() != typeof (DynamicExtensionLoader));
bool result = references.All(r => r.Loader.GetType() != typeof(DynamicExtensionLoader) && r.Loader.GetType() != typeof(ProbingExtensionLoader));
if (!result) {
Logger.Information("Extension \"{0}\" will not be loaded as pre-compiled extension because one or more referenced extension is dynamically compiled", extension.Id);
}
@@ -193,6 +193,7 @@ namespace Orchard.Environment.Extensions.Loaders {
var result = new ExtensionProbeEntry {
Descriptor = descriptor,
Loader = this,
Priority = 80,
VirtualPath = assemblyPath,
VirtualPathDependencies = new[] { assemblyPath },
};

View File

@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment.Extensions.Compilers;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
using Orchard.Utility.Extensions;
namespace Orchard.Environment.Extensions.Loaders {
/// <summary>
/// In case <see cref="DynamicExtensionLoader"/> is disabled, this loader will dynamically compile the assembly
/// and save a copy to the probing folder so that next restart doesn't need to compile it again.
/// </summary>
public class ProbingExtensionLoader : ExtensionLoaderBase {
public static readonly string[] ExtensionsVirtualPathPrefixes = { "~/Modules/", "~/Themes/" };
private readonly IBuildManager _buildManager;
private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IHostEnvironment _hostEnvironment;
private readonly IAssemblyProbingFolder _assemblyProbingFolder;
private readonly IDependenciesFolder _dependenciesFolder;
private readonly IProjectFileParser _projectFileParser;
public ProbingExtensionLoader(
IBuildManager buildManager,
IVirtualPathProvider virtualPathProvider,
IVirtualPathMonitor virtualPathMonitor,
IHostEnvironment hostEnvironment,
IAssemblyProbingFolder assemblyProbingFolder,
IDependenciesFolder dependenciesFolder,
IProjectFileParser projectFileParser)
: base(dependenciesFolder) {
_buildManager = buildManager;
_virtualPathProvider = virtualPathProvider;
_hostEnvironment = hostEnvironment;
_assemblyProbingFolder = assemblyProbingFolder;
_projectFileParser = projectFileParser;
_dependenciesFolder = dependenciesFolder;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public bool Disabled { get; set; }
public override int Order { get { return 110; } }
public override IEnumerable<ExtensionCompilationReference> GetCompilationReferences(DependencyDescriptor dependency) {
yield return new ExtensionCompilationReference { BuildProviderTarget = dependency.VirtualPath };
}
public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) {
}
public override void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) {
}
public override void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) {
}
public override IEnumerable<ExtensionReferenceProbeEntry> ProbeReferences(ExtensionDescriptor descriptor) {
if (Disabled)
return Enumerable.Empty<ExtensionReferenceProbeEntry>();
Logger.Information("Probing references for module '{0}'", descriptor.Id);
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)
return Enumerable.Empty<ExtensionReferenceProbeEntry>();
var projectFile = _projectFileParser.Parse(projectPath);
var result = projectFile.References.Select(r => new ExtensionReferenceProbeEntry {
Descriptor = descriptor,
Loader = this,
Name = r.SimpleName,
VirtualPath = _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, r.SimpleName, r.Path)
});
Logger.Information("Done probing references for module '{0}'", descriptor.Id);
return result;
}
public override Assembly LoadReference(DependencyReferenceDescriptor reference) {
if (Disabled)
return null;
Logger.Information("Loading reference '{0}'", reference.Name);
// DynamicExtensionLoader has 2 types of references: assemblies from module bin directory
// and .csproj.
Assembly result;
if (StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(reference.VirtualPath), ".dll"))
result = _assemblyProbingFolder.LoadAssembly(reference.Name);
else {
result = ProbeAssembly(reference.Name, reference.VirtualPath);
}
Logger.Information("Done loading reference '{0}'", reference.Name);
return result;
}
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
if (Disabled)
return null;
Logger.Information("Probing for module '{0}'", descriptor.Id);
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)
return null;
var result = new ExtensionProbeEntry {
Descriptor = descriptor,
Loader = this,
Priority = 50,
VirtualPath = projectPath,
VirtualPathDependencies = new string[] { projectPath },
};
Logger.Information("Done probing for module '{0}'", descriptor.Id);
return result;
}
protected override ExtensionEntry LoadWorker(ExtensionDescriptor descriptor) {
if (Disabled)
return null;
Logger.Information("Start loading dynamic extension \"{0}\"", descriptor.Name);
var assembly = _assemblyProbingFolder.LoadAssembly(descriptor.Id);
if (assembly == null) {
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)
return null;
assembly = ProbeAssembly(descriptor.Id, projectPath);
}
if (assembly == null)
return null;
Logger.Information("Done loading dynamic extension \"{0}\": assembly name=\"{1}\"", descriptor.Name, assembly.FullName);
return new ExtensionEntry {
Descriptor = descriptor,
Assembly = assembly,
ExportedTypes = assembly.GetExportedTypes(),
};
}
private void AddDependencies(string projectPath, HashSet<string> currentSet) {
// Skip files from locations other than "~/Modules" and "~/Themes"
if (string.IsNullOrEmpty(PrefixMatch(projectPath, ExtensionsVirtualPathPrefixes))) {
return;
}
// Add project path
currentSet.Add(projectPath);
// Add source file paths
var projectFile = _projectFileParser.Parse(projectPath);
string basePath = _virtualPathProvider.GetDirectoryName(projectPath);
currentSet.UnionWith(projectFile.SourceFilenames.Select(f => _virtualPathProvider.Combine(basePath, f)));
// Add Project and Library references
if (projectFile.References != null) {
foreach (ReferenceDescriptor referenceDescriptor in projectFile.References.Where(reference => !string.IsNullOrEmpty(reference.Path))) {
string path = referenceDescriptor.ReferenceType == ReferenceType.Library
? _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, referenceDescriptor.SimpleName, referenceDescriptor.Path)
: _virtualPathProvider.Combine(basePath, referenceDescriptor.Path);
// Normalize the virtual path (avoid ".." in the path name)
if (!string.IsNullOrEmpty(path)) {
path = _virtualPathProvider.ToAppRelative(path);
}
// Attempt to reference the project / library file
if (!string.IsNullOrEmpty(path) && !currentSet.Contains(path) && _virtualPathProvider.TryFileExists(path)) {
switch (referenceDescriptor.ReferenceType) {
case ReferenceType.Project:
AddDependencies(path, currentSet);
break;
case ReferenceType.Library:
currentSet.Add(path);
break;
}
}
}
}
}
private Assembly ProbeAssembly(string moduleName, string virtualPath) {
var assembly = _buildManager.GetCompiledAssembly(virtualPath);
if (assembly != null) {
_assemblyProbingFolder.StoreAssembly(moduleName, assembly.Location);
return assembly;
}
return null;
}
private static string PrefixMatch(string virtualPath, params string[] prefixes) {
return prefixes
.FirstOrDefault(p => virtualPath.StartsWith(p, StringComparison.OrdinalIgnoreCase));
}
private string GetProjectPath(ExtensionDescriptor descriptor) {
string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
descriptor.Id + ".csproj");
if (!_virtualPathProvider.FileExists(projectPath)) {
return null;
}
return projectPath;
}
}
}

View File

@@ -103,6 +103,7 @@ namespace Orchard.Environment {
builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();
builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
builder.RegisterType<ProbingExtensionLoader>().As<IExtensionLoader>().SingleInstance();
builder.RegisterType<RawThemeExtensionLoader>().As<IExtensionLoader>().SingleInstance();
}
}

View File

@@ -111,6 +111,7 @@ namespace Orchard.FileSystems.Dependencies {
// implementations.
return
loaderName == "DynamicExtensionLoader" ||
loaderName == "ProbingExtensionLoader" ||
loaderName == "PrecompiledExtensionLoader";
}

View File

@@ -201,6 +201,7 @@
<Compile Include="Environment\Extensions\Folders\CoreModuleFolders.cs" />
<Compile Include="Environment\Extensions\Folders\IExtensionHarvester.cs" />
<Compile Include="Environment\Extensions\IExtensionMonitoringCoordinator.cs" />
<Compile Include="Environment\Extensions\Loaders\ProbingExtensionLoader.cs" />
<Compile Include="Environment\Extensions\OrchardSuppressDependencyAttribute.cs" />
<Compile Include="Environment\Features\IFeatureManager.cs" />
<Compile Include="Environment\IAssemblyNameResolver.cs" />