mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
Implement dynamic compilation of modules
--HG-- branch : dev
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
using System.Web.Compilation;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
public class CSharpExtensionBuildProvider : BuildProvider {
|
||||
private readonly CompilerType _codeCompilerType;
|
||||
|
||||
public CSharpExtensionBuildProvider() {
|
||||
_codeCompilerType = GetDefaultCompilerTypeForLanguage("C#");
|
||||
}
|
||||
|
||||
public override CompilerType CodeCompilerType { get { return _codeCompilerType; } }
|
||||
|
||||
public override void GenerateCode(AssemblyBuilder assemblyBuilder) {
|
||||
var virtualPathProvider = new AspNetVirtualPathProvider();
|
||||
var compiler = new CSharpProjectMediumTrustCompiler(virtualPathProvider);
|
||||
|
||||
var aspNetAssemblyBuilder = new AspNetAssemblyBuilder(assemblyBuilder, this);
|
||||
compiler.CompileProject(this.VirtualPath, aspNetAssemblyBuilder);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web.Compilation;
|
||||
using System.Web.Hosting;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
/// <summary>
|
||||
/// Compile a C# extension into an assembly given a directory location
|
||||
/// </summary>
|
||||
public class CSharpExtensionCompiler {
|
||||
public CompilerResults CompileProject(string location) {
|
||||
var codeProvider = CodeDomProvider.CreateProvider("cs");
|
||||
|
||||
var references = GetAssemblyReferenceNames();
|
||||
var options = new CompilerParameters(references.ToArray());
|
||||
|
||||
var fileNames = GetSourceFileNames(location);
|
||||
var results = codeProvider.CompileAssemblyFromFile(options, fileNames.ToArray());
|
||||
return results;
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetAssemblyReferenceNames() {
|
||||
return Enumerable.Distinct<string>(BuildManager.GetReferencedAssemblies()
|
||||
.OfType<Assembly>()
|
||||
.Select(x => x.Location)
|
||||
.Where(x => !string.IsNullOrEmpty(x)));
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSourceFileNames(string path) {
|
||||
foreach (var file in Directory.GetFiles(path, "*.cs")) {
|
||||
yield return file;
|
||||
}
|
||||
|
||||
foreach (var folder in Directory.GetDirectories(path)) {
|
||||
if (Path.GetFileName(folder).StartsWith("."))
|
||||
continue;
|
||||
|
||||
foreach (var file in GetSourceFileNames(folder)) {
|
||||
yield return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
/// <summary>
|
||||
/// Compile a C# extension into an assembly given a directory location
|
||||
/// </summary>
|
||||
public class CSharpProjectFullTrustCompiler {
|
||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||
private readonly IBuildManager _buildManager;
|
||||
|
||||
public CSharpProjectFullTrustCompiler(IVirtualPathProvider virtualPathProvider, IBuildManager buildManager) {
|
||||
_virtualPathProvider = virtualPathProvider;
|
||||
_buildManager = buildManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile a csproj file given its virtual path. Use the CSharp CodeDomProvider
|
||||
/// class, which is only available in full trust.
|
||||
/// </summary>
|
||||
public CompilerResults CompileProject(string virtualPath) {
|
||||
var codeProvider = CodeDomProvider.CreateProvider("cs");
|
||||
var directory = _virtualPathProvider.GetDirectoryName(virtualPath);
|
||||
|
||||
using (var stream = _virtualPathProvider.OpenFile(virtualPath)) {
|
||||
var descriptor = new CSharpProjectParser().Parse(stream);
|
||||
|
||||
var references = GetAssemblyReferenceNames();
|
||||
var options = new CompilerParameters(references.ToArray());
|
||||
|
||||
var fileNames = descriptor.SourceFilenames
|
||||
.Select(f => _virtualPathProvider.Combine(directory, f))
|
||||
.Select(f => _virtualPathProvider.MapPath(f));
|
||||
|
||||
var results = codeProvider.CompileAssemblyFromFile(options, fileNames.ToArray());
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetAssemblyReferenceNames() {
|
||||
return _buildManager.GetReferencedAssemblies()
|
||||
.OfType<Assembly>()
|
||||
.Where(a => !string.IsNullOrEmpty(a.Location))
|
||||
.Select(a => a.Location)
|
||||
.Distinct();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.CodeDom;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
/// <summary>
|
||||
/// Compile a C# extension into an assembly given a directory location
|
||||
/// </summary>
|
||||
public class CSharpProjectMediumTrustCompiler {
|
||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||
|
||||
public CSharpProjectMediumTrustCompiler(IVirtualPathProvider virtualPathProvider) {
|
||||
_virtualPathProvider = virtualPathProvider;
|
||||
}
|
||||
/// <summary>
|
||||
/// Compile a csproj file given its virtual path, a build provider and an assembly builder.
|
||||
/// This method works in medium trust.
|
||||
/// </summary>
|
||||
public void CompileProject(string virtualPath, IAssemblyBuilder assemblyBuilder) {
|
||||
using (var stream = _virtualPathProvider.OpenFile(virtualPath)) {
|
||||
var descriptor = new CSharpProjectParser().Parse(stream);
|
||||
|
||||
var directory = _virtualPathProvider.GetDirectoryName(virtualPath);
|
||||
foreach (var filename in descriptor.SourceFilenames.Select(f => _virtualPathProvider.Combine(directory, f))) {
|
||||
assemblyBuilder.AddCodeCompileUnit(CreateCompileUnit(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CodeCompileUnit CreateCompileUnit(string virtualPath) {
|
||||
var contents = GetContents(virtualPath);
|
||||
var unit = new CodeSnippetCompileUnit(contents);
|
||||
var physicalPath = _virtualPathProvider.MapPath(virtualPath);
|
||||
if (!string.IsNullOrEmpty(physicalPath)) {
|
||||
unit.LinePragma = new CodeLinePragma(physicalPath, 1);
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
private string GetContents(string virtualPath) {
|
||||
string contents;
|
||||
using (var stream = _virtualPathProvider.OpenFile(virtualPath)) {
|
||||
using (var reader = new StreamReader(stream)) {
|
||||
contents = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
public class CSharpProjectDescriptor {
|
||||
public IEnumerable<string> SourceFilenames { get; set; }
|
||||
public IEnumerable<ReferenceDescriptor> References { get; set; }
|
||||
}
|
||||
|
||||
public class ReferenceDescriptor {
|
||||
public string AssemblyName { get; set; }
|
||||
}
|
||||
|
||||
public class CSharpProjectParser {
|
||||
public CSharpProjectDescriptor Parse(Stream stream) {
|
||||
var document = XDocument.Load(XmlReader.Create(stream));
|
||||
return new CSharpProjectDescriptor {
|
||||
SourceFilenames = GetSourceFilenames(document),
|
||||
References = GetReferences(document)
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSourceFilenames(XDocument document) {
|
||||
return document
|
||||
.Elements(ns("Project"))
|
||||
.Elements(ns("ItemGroup"))
|
||||
.Elements(ns("Compile"))
|
||||
.Attributes("Include")
|
||||
.Select(c => c.Value);
|
||||
}
|
||||
|
||||
private IEnumerable<ReferenceDescriptor> GetReferences(XDocument document) {
|
||||
return document
|
||||
.Elements(ns("Project"))
|
||||
.Elements(ns("ItemGroup"))
|
||||
.Elements(ns("Reference"))
|
||||
.Attributes("Include")
|
||||
.Select(c => new ReferenceDescriptor { AssemblyName = c.Value });
|
||||
}
|
||||
|
||||
private static XName ns(string name) {
|
||||
return XName.Get(name, "http://schemas.microsoft.com/developer/msbuild/2003");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web.Compilation;
|
||||
using System.Web.Hosting;
|
||||
@@ -15,45 +13,57 @@ namespace Orchard.Environment.Extensions.Loaders {
|
||||
if (HostingEnvironment.IsHosted == false)
|
||||
return null;
|
||||
|
||||
var codeProvider = CodeDomProvider.CreateProvider("cs");
|
||||
|
||||
var references = GetAssemblyReferenceNames();
|
||||
var options = new CompilerParameters(references.ToArray());
|
||||
|
||||
var locationPath = HostingEnvironment.MapPath(descriptor.Location);
|
||||
var extensionPath = Path.Combine(locationPath, descriptor.Name);
|
||||
|
||||
var fileNames = GetSourceFileNames(extensionPath);
|
||||
var results = codeProvider.CompileAssemblyFromFile(options, fileNames.ToArray());
|
||||
|
||||
return new ExtensionEntry {
|
||||
Descriptor = descriptor,
|
||||
Assembly = results.CompiledAssembly,
|
||||
ExportedTypes = results.CompiledAssembly.GetExportedTypes(),
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetAssemblyReferenceNames() {
|
||||
return BuildManager.GetReferencedAssemblies()
|
||||
.OfType<Assembly>()
|
||||
.Select(x => x.Location)
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSourceFileNames(string path) {
|
||||
foreach (var file in Directory.GetFiles(path, "*.cs")) {
|
||||
yield return file;
|
||||
}
|
||||
|
||||
foreach (var folder in Directory.GetDirectories(path)) {
|
||||
if (Path.GetFileName(folder).StartsWith("."))
|
||||
continue;
|
||||
|
||||
foreach (var file in GetSourceFileNames(folder)) {
|
||||
yield return file;
|
||||
// 1) Try to load the assembly directory
|
||||
// This will look in the "App_Data/Dependencies" directory if
|
||||
// the probing path is correctly configured in Web.config
|
||||
{
|
||||
try {
|
||||
Assembly assembly = Assembly.Load(descriptor.Name);
|
||||
return CreateExtensionEntry(descriptor, assembly);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// The assembly is not in one of the probing directory,
|
||||
// including "App_Data/Dependencies", we need to move on
|
||||
// to other strageties
|
||||
}
|
||||
}
|
||||
|
||||
// 2) look for the assembly in the "Bin" directory
|
||||
{
|
||||
string modulePath = HostingEnvironment.MapPath(descriptor.Location);
|
||||
modulePath = Path.Combine(modulePath, descriptor.Name);
|
||||
string moduleBinary = Path.Combine(modulePath, "bin");
|
||||
moduleBinary = Path.Combine(moduleBinary, descriptor.Name + ".dll");
|
||||
if (File.Exists(moduleBinary)) {
|
||||
// Copy file to dependencies directory
|
||||
string dependenciesPath = HostingEnvironment.MapPath("~/App_Data/Dependencies");
|
||||
if (!Directory.Exists(dependenciesPath)) {
|
||||
Directory.CreateDirectory(dependenciesPath);
|
||||
}
|
||||
string destFile = Path.Combine(dependenciesPath, descriptor.Name + ".dll");
|
||||
File.Copy(moduleBinary, destFile, true);
|
||||
|
||||
// then load the assembly
|
||||
Assembly assembly = Assembly.Load(descriptor.Name);
|
||||
return CreateExtensionEntry(descriptor, assembly);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) look for the csproj in the module directory
|
||||
{
|
||||
string projfileName = Path.Combine(descriptor.Location, descriptor.Name);
|
||||
projfileName = Path.Combine(projfileName, descriptor.Name + ".csproj").Replace('\\', '/');
|
||||
var assembly = BuildManager.GetCompiledAssembly(projfileName);
|
||||
return CreateExtensionEntry(descriptor, assembly);
|
||||
}
|
||||
}
|
||||
|
||||
private static ExtensionEntry CreateExtensionEntry(ExtensionDescriptor descriptor, Assembly assembly) {
|
||||
return new ExtensionEntry {
|
||||
Descriptor = descriptor,
|
||||
Assembly = assembly,
|
||||
ExportedTypes = assembly.GetExportedTypes(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
using System.CodeDom;
|
||||
using System.Web.Compilation;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
public interface IAssemblyBuilder {
|
||||
void AddCodeCompileUnit(CodeCompileUnit compileUnit);
|
||||
}
|
||||
|
||||
public class AspNetAssemblyBuilder : IAssemblyBuilder {
|
||||
private readonly AssemblyBuilder _assemblyBuilder;
|
||||
private readonly BuildProvider _buildProvider;
|
||||
|
||||
public AspNetAssemblyBuilder(AssemblyBuilder assemblyBuilder, BuildProvider buildProvider) {
|
||||
_assemblyBuilder = assemblyBuilder;
|
||||
_buildProvider = buildProvider;
|
||||
}
|
||||
|
||||
public void AddCodeCompileUnit(CodeCompileUnit compileUnit) {
|
||||
_assemblyBuilder.AddCodeCompileUnit(_buildProvider, compileUnit);
|
||||
}
|
||||
}
|
||||
}
|
16
src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs
Normal file
16
src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Compilation;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
public interface IBuildManager {
|
||||
IEnumerable<string> GetReferencedAssemblies();
|
||||
}
|
||||
|
||||
public class AspNetBuildManager : IBuildManager {
|
||||
public IEnumerable<string> GetReferencedAssemblies() {
|
||||
return BuildManager.GetReferencedAssemblies().Cast<string>();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web.Hosting;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Loaders {
|
||||
public interface IVirtualPathProvider {
|
||||
string GetDirectoryName(string virtualPath);
|
||||
string Combine(params string[] paths);
|
||||
Stream OpenFile(string virtualPath);
|
||||
string MapPath(string virtualPath);
|
||||
}
|
||||
|
||||
public class AspNetVirtualPathProvider : IVirtualPathProvider {
|
||||
public string GetDirectoryName(string virtualPath) {
|
||||
return Path.GetDirectoryName(virtualPath);
|
||||
}
|
||||
|
||||
public string Combine(params string[] paths) {
|
||||
return Path.Combine(paths).Replace('\\', '/');
|
||||
}
|
||||
|
||||
public Stream OpenFile(string virtualPath) {
|
||||
return VirtualPathProvider.OpenFile(virtualPath);
|
||||
}
|
||||
|
||||
public string MapPath(string virtualPath) {
|
||||
return HostingEnvironment.MapPath(virtualPath);
|
||||
}
|
||||
}
|
||||
}
|
@@ -119,6 +119,7 @@
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Yaml, Version=1.0.3370.39839, Culture=neutral, PublicKeyToken=187a3d240e44a135, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\yaml\Yaml.dll</HintPath>
|
||||
@@ -135,6 +136,14 @@
|
||||
<Compile Include="Data\DataModule.cs" />
|
||||
<Compile Include="Data\Orderable.cs" />
|
||||
<Compile Include="Environment\DefaultOrchardShell.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\CSharpExtensionBuildProvider.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\CSharpExtensionCompiler.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\CSharpProjectExtensionCompilerMediumTrust.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\CSharpProjectFullTrustCompiler.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\CSharpProjectParser.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\IAssemblyBuilder.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\IBuildManager.cs" />
|
||||
<Compile Include="Environment\Extensions\Loaders\IVirtualPathProvider.cs" />
|
||||
<Compile Include="Environment\IOrchardShell.cs" />
|
||||
<Compile Include="Environment\OrchardStarter.cs" />
|
||||
<Compile Include="Environment\State\DefaultProcessingEngine.cs" />
|
||||
|
Reference in New Issue
Block a user