Initial version of "Orchard.exe"

* Understands "/workingdirectory", "/verbose", "/virtualpath" switches
* Starts up a WebHost instance in a separate AppDomain

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-04-07 16:48:43 -07:00
parent 011ebe9ffe
commit d3cd35600b
11 changed files with 338 additions and 0 deletions

View File

@@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Futures.Widgets", "Orchard.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Specs", "Orchard.Specs\Orchard.Specs.csproj", "{7354DF37-934B-46CF-A13C-455D5F5F5413}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard", "Tools\Orchard\Orchard.csproj", "{33B1BC8D-E292-4972-A363-22056B207156}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -147,6 +149,10 @@ Global
{7354DF37-934B-46CF-A13C-455D5F5F5413}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7354DF37-934B-46CF-A13C-455D5F5F5413}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7354DF37-934B-46CF-A13C-455D5F5F5413}.Release|Any CPU.Build.0 = Release|Any CPU
{33B1BC8D-E292-4972-A363-22056B207156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33B1BC8D-E292-4972-A363-22056B207156}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33B1BC8D-E292-4972-A363-22056B207156}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33B1BC8D-E292-4972-A363-22056B207156}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -170,6 +176,7 @@ Global
{8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{5E5E7A21-C7B2-44D8-8593-2F9541AE041D} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
{4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
{33B1BC8D-E292-4972-A363-22056B207156} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
{E65E5633-C0FF-453C-A906-481C14F969D6} = {E75A4CE4-CAA6-41E4-B951-33ACC60DC77C}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,7 @@
using System.Collections.Generic;
namespace Orchard.Arguments {
public interface IParser {
ParserResult Parse(IEnumerable<string> args);
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Orchard.Arguments {
public class Parser : IParser {
public ParserResult Parse(IEnumerable<string> args) {
ParserResult result = new ParserResult();
IEnumerator<string> e = args.GetEnumerator();
while (e.MoveNext()) {
if (e.Current[0] == '/') {
var s = ParseSwitch(e);
result.Switches.Add(s);
}
else {
result.Arguments.Add(e.Current);
}
}
return result;
}
private Switch ParseSwitch(IEnumerator<string> enumerator) {
string sw = enumerator.Current.Substring(1);
string[] args = sw.Split(':');
return new Switch {
Name = args[0],
Value = args.Length >= 2 ? args[1] : string.Empty
};
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Orchard.Arguments {
public class ParserResult {
public ParserResult() {
this.Arguments = new List<string>();
this.Switches = new List<Switch>();
}
public IList<string> Arguments { get; set; }
public IList<Switch> Switches { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
namespace Orchard.Arguments {
public class Switch {
public string Name { get; set; }
public string Value { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Hosting;
namespace Orchard.Host {
class CommandHost : MarshalByRefObject, IRegisteredObject {
public CommandHost() {
HostingEnvironment.RegisterObject(this);
}
public override object InitializeLifetimeService() {
// never expire the license
return null;
}
public void Stop(bool immediate) {
//TODO
}
}
}

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{33B1BC8D-E292-4972-A363-22056B207156}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard</RootNamespace>
<AssemblyName>Orchard</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Arguments\IParser.cs" />
<Compile Include="OrchardArguments.cs" />
<Compile Include="Arguments\Parser.cs" />
<Compile Include="Arguments\ParserResult.cs" />
<Compile Include="Host\CommandHost.cs" />
<Compile Include="OrchardArgumentsParser.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Arguments\Switch.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Orchard {
public class OrchardArguments {
public bool Verbose { get; set; }
public string VirtualPath { get; set; }
public string WorkingDirectory { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Arguments;
namespace Orchard {
public interface IOrchardArgumentsParser {
OrchardArguments Parse(ParserResult arguments);
}
public class OrchardArgumentsParser : IOrchardArgumentsParser {
public OrchardArguments Parse(ParserResult arguments) {
OrchardArguments result = new OrchardArguments();
foreach (var sw in arguments.Switches) {
switch (sw.Name.ToLowerInvariant()) {
case "wd":
case "workingdirectory":
result.WorkingDirectory = sw.Value;
break;
case "v":
case "verbose":
result.Verbose = bool.Parse(sw.Value);
break;
case "vp":
case "virtualpath":
result.VirtualPath = sw.Value;
break;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Hosting;
using Orchard.Host;
namespace Orchard {
class Program {
private readonly string[] _args;
private Program(string[] args) {
_args = args;
}
static void Main(string[] args) {
new Program(args).Run();
}
public void Run() {
// Parse command line arguments
var arguments = new OrchardArgumentsParser().Parse(new Orchard.Arguments.Parser().Parse(_args));
if (string.IsNullOrEmpty(arguments.VirtualPath))
arguments.VirtualPath = "/";
if (string.IsNullOrEmpty(arguments.WorkingDirectory))
arguments.WorkingDirectory = Environment.CurrentDirectory;
if (arguments.Verbose) {
Console.WriteLine("Virtual path: {0}", arguments.VirtualPath);
Console.WriteLine("Working directory: {0}", arguments.WorkingDirectory);
}
var orchardDirectory = GetOrchardDirectory(arguments.WorkingDirectory);
if (arguments.Verbose) {
Console.WriteLine("Detected and using orchard directory \"{0}\"", orchardDirectory.FullName);
}
object host = CreateWorkerAppDomainWithHost(arguments.VirtualPath, orchardDirectory.FullName, typeof(CommandHost));
}
private DirectoryInfo GetOrchardDirectory(string directory) {
for (var directoryInfo = new DirectoryInfo(directory); directoryInfo != null; directoryInfo = directoryInfo.Parent) {
if (!directoryInfo.Exists) {
throw new ApplicationException(string.Format("Directory \"{0}\" does not exist", directoryInfo.FullName));
}
var webConfigFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "web.config"));
if (!webConfigFileInfo.Exists)
continue;
var binDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "bin"));
if (!binDirectoryInfo.Exists)
continue;
var orchardFrameworkFileInfo = new FileInfo(Path.Combine(binDirectoryInfo.FullName, "Orchard.Framework.dll"));
if (!orchardFrameworkFileInfo.Exists)
continue;
return directoryInfo;
}
throw new ApplicationException("Directory \"{0}\" doesn't seem to contain an Orchard installation");
}
public object CreateWorkerAppDomainWithHost(string virtualPath, string physicalPath, Type hostType) {
// this creates worker app domain in a way that host doesn't need to be in GAC or bin
// using BuildManagerHost via private reflection
string uniqueAppString = string.Concat(virtualPath, physicalPath).ToLowerInvariant();
string appId = (uniqueAppString.GetHashCode()).ToString("x", CultureInfo.InvariantCulture);
// create BuildManagerHost in the worker app domain
var appManager = ApplicationManager.GetApplicationManager();
var buildManagerHostType = typeof(HttpRuntime).Assembly.GetType("System.Web.Compilation.BuildManagerHost");
var buildManagerHost = appManager.CreateObject(appId, buildManagerHostType, virtualPath, physicalPath, false);
// call BuildManagerHost.RegisterAssembly to make Host type loadable in the worker app domain
buildManagerHostType.InvokeMember(
"RegisterAssembly",
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic,
null,
buildManagerHost,
new object[2] { hostType.Assembly.FullName, hostType.Assembly.Location });
// create Host in the worker app domain
return appManager.CreateObject(appId, hostType, virtualPath, physicalPath, false);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Orchard")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5ee627dd-b767-441e-b8d0-ec7d26faac49")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]