mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
Refactoring of orchard.exe
Added "Usage" help screen for "orchard.exe /?" Better support for switches parsing Encapsulated Console.In and Console.Out Extracted "OrchardHost" class --HG-- branch : dev
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autofac.Features.Metadata;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Commands.Builtin {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace Orchard.Localization {
|
||||
public class LocalizedString {
|
||||
using System;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
public class LocalizedString : MarshalByRefObject {
|
||||
private readonly string _localized;
|
||||
|
||||
public LocalizedString(string localized) {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Hosting;
|
||||
|
||||
namespace Orchard.Host {
|
||||
@@ -19,11 +18,11 @@ namespace Orchard.Host {
|
||||
//TODO
|
||||
}
|
||||
|
||||
public int RunCommand(OrchardParameters args) {
|
||||
public int RunCommand(TextReader input, TextWriter output, OrchardParameters args) {
|
||||
var agent = Activator.CreateInstance("Orchard.Framework", "Orchard.Commands.CommandHostAgent").Unwrap();
|
||||
int result = (int)agent.GetType().GetMethod("RunSingleCommand").Invoke(agent, new object[] {
|
||||
Console.In,
|
||||
Console.Out,
|
||||
input,
|
||||
output,
|
||||
args.Tenant,
|
||||
args.Arguments.ToArray(),
|
||||
args.Switches});
|
||||
|
@@ -48,6 +48,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="OrchardHost.cs" />
|
||||
<Compile Include="Parameters\ICommandParametersParser.cs" />
|
||||
<Compile Include="IOrchardParametersParser.cs" />
|
||||
<Compile Include="OrchardParameters.cs" />
|
||||
|
176
src/Tools/Orchard/OrchardHost.cs
Normal file
176
src/Tools/Orchard/OrchardHost.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Host;
|
||||
using Orchard.Parameters;
|
||||
|
||||
namespace Orchard {
|
||||
class OrchardHost {
|
||||
private readonly TextReader _input;
|
||||
private readonly TextWriter _output;
|
||||
private readonly string[] _args;
|
||||
private OrchardParameters _arguments;
|
||||
|
||||
public OrchardHost(TextReader input, TextWriter output, string[] args) {
|
||||
_input = input;
|
||||
_output = output;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
private bool Verbose {
|
||||
get { return _arguments != null && _arguments.Verbose; }
|
||||
}
|
||||
|
||||
public int Run() {
|
||||
try {
|
||||
return DoRun();
|
||||
}
|
||||
catch (Exception e) {
|
||||
_output.WriteLine("Error:");
|
||||
for (; e != null; e = e.InnerException) {
|
||||
_output.WriteLine(" {0}", e.Message);
|
||||
if (Verbose) {
|
||||
_output.WriteLine(" Stacktrace:");
|
||||
_output.WriteLine("{0}", e.StackTrace);
|
||||
_output.WriteLine();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int DoRun() {
|
||||
_arguments = new OrchardParametersParser().Parse(new CommandParametersParser().Parse(_args));
|
||||
if (!_arguments.Arguments.Any() || _arguments.Switches.ContainsKey("?")) {
|
||||
return GeneralHelp();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_arguments.VirtualPath))
|
||||
_arguments.VirtualPath = "/";
|
||||
LogInfo("Virtual path: \"{0}\"", _arguments.VirtualPath);
|
||||
|
||||
if (string.IsNullOrEmpty(_arguments.WorkingDirectory))
|
||||
_arguments.WorkingDirectory = Environment.CurrentDirectory;
|
||||
LogInfo("Working directory: \"{0}\"", _arguments.WorkingDirectory);
|
||||
|
||||
LogInfo("Detecting orchard installation root directory...");
|
||||
var orchardDirectory = GetOrchardDirectory(_arguments.WorkingDirectory);
|
||||
LogInfo("Orchard root directory: \"{0}\"", orchardDirectory.FullName);
|
||||
|
||||
LogInfo("Creating ASP.NET AppDomain for command agent...");
|
||||
var host = (CommandHost)CreateWorkerAppDomainWithHost(_arguments.VirtualPath, orchardDirectory.FullName, typeof(CommandHost));
|
||||
|
||||
LogInfo("Executing command in ASP.NET AppDomain...");
|
||||
var result = host.RunCommand(_input, _output, _arguments);
|
||||
LogInfo("Return code for command: {0}", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int GeneralHelp() {
|
||||
_output.WriteLine("Executes Orchard commands from a Orchard installation directory.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("Usage:");
|
||||
_output.WriteLine(" orchard.exe arg1 ... argn /switch1[:value1] ... /switchn[:valuen]");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" arg1 ... argn");
|
||||
_output.WriteLine(" Specify which command to execute and optional arguments");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /switch1[:value1] ... /switchn[:valuen]");
|
||||
_output.WriteLine(" Specify switches to apply to the command. Available switches generally ");
|
||||
_output.WriteLine(" depend on the command executed, with the exception of a few built-in ones.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" Built-in commands");
|
||||
_output.WriteLine(" =================");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" help commands");
|
||||
_output.WriteLine(" Display the list of available commands.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" help <command-name>");
|
||||
_output.WriteLine(" Display help for a given command.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" Built-in switches");
|
||||
_output.WriteLine(" =================");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /WorkingDirectory");
|
||||
_output.WriteLine(" /wd");
|
||||
_output.WriteLine(" Specifies the orchard installation directory. The current directory is the default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Verbose");
|
||||
_output.WriteLine(" /v");
|
||||
_output.WriteLine(" Turn on verbose output");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /VirtualPath");
|
||||
_output.WriteLine(" /vp");
|
||||
_output.WriteLine(" Virtual path to pass to the WebHost. Empty (i.e. root path) by default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Tenant:tenant-name");
|
||||
_output.WriteLine(" /t:tenant-name");
|
||||
_output.WriteLine(" Specifies which tenant to run the command into. \"Default\" tenant by default.");
|
||||
_output.WriteLine("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void LogInfo(string format, params object[] args) {
|
||||
if (Verbose) {
|
||||
_output.Write("{0}: ", DateTime.Now);
|
||||
_output.WriteLine(format, args);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// We look for
|
||||
// 1) .\web.config
|
||||
// 2) .\bin\Orchard.Framework.dll
|
||||
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(
|
||||
string.Format("Directory \"{0}\" doesn't seem to contain an Orchard installation", new DirectoryInfo(directory).FullName));
|
||||
}
|
||||
|
||||
private static 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[] { hostType.Assembly.FullName, hostType.Assembly.Location });
|
||||
|
||||
// create Host in the worker app domain
|
||||
return appManager.CreateObject(appId, hostType, virtualPath, physicalPath, false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Orchard {
|
||||
public class OrchardParameters : MarshalByRefObject {
|
||||
@@ -11,6 +9,5 @@ namespace Orchard {
|
||||
public string Tenant { get; set; }
|
||||
public IEnumerable<string> Arguments { get; set; }
|
||||
public IDictionary<string, string> Switches { get; set; }
|
||||
|
||||
}
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Orchard.Parameters {
|
||||
public class CommandParametersParser : ICommandParametersParser {
|
||||
@@ -12,9 +10,17 @@ namespace Orchard.Parameters {
|
||||
};
|
||||
|
||||
foreach (var arg in args) {
|
||||
// Switch?
|
||||
if (arg[0] == '/') {
|
||||
var switchKeyValue = arg.Substring(1).Split(':');
|
||||
result.Switches.Add(switchKeyValue[0], switchKeyValue.Length >= 2 ? switchKeyValue[1] : string.Empty);
|
||||
int index = arg.IndexOf(':');
|
||||
var switchName = (index < 0 ? arg.Substring(1) : arg.Substring(1, index - 1));
|
||||
var switchValue = (index < 0 || index >= arg.Length ? string.Empty : arg.Substring(index + 1));
|
||||
|
||||
if (string.IsNullOrEmpty(switchName)) {
|
||||
throw new ArgumentException(string.Format("Invalid switch syntax: \"{0}\". Valid syntax is /<switchName>[:<switchValue>].", arg));
|
||||
}
|
||||
|
||||
result.Switches.Add(switchName, switchValue);
|
||||
}
|
||||
else {
|
||||
result.Arguments.Add(arg);
|
||||
|
@@ -1,106 +1,9 @@
|
||||
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;
|
||||
using Orchard.Parameters;
|
||||
|
||||
namespace Orchard {
|
||||
class Program {
|
||||
private readonly string[] _args;
|
||||
|
||||
private Program(string[] args) {
|
||||
_args = args;
|
||||
public class Program {
|
||||
public static int Main(string[] args) {
|
||||
return new OrchardHost(Console.In, Console.Out, args).Run();
|
||||
}
|
||||
|
||||
static int Main(string[] args) {
|
||||
return new Program(args).Run();
|
||||
}
|
||||
|
||||
public int Run() {
|
||||
// Parse command line arguments
|
||||
var arguments = new OrchardParametersParser().Parse(new CommandParametersParser().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);
|
||||
}
|
||||
|
||||
if (arguments.Verbose) {
|
||||
Console.WriteLine("Creating ASP.NET AppDomain for command agent");
|
||||
}
|
||||
var host = (CommandHost)CreateWorkerAppDomainWithHost(arguments.VirtualPath, orchardDirectory.FullName, typeof(CommandHost));
|
||||
|
||||
|
||||
if (arguments.Verbose) {
|
||||
Console.WriteLine("Executing command in ASP.NET AppDomain");
|
||||
}
|
||||
return host.RunCommand(arguments);
|
||||
}
|
||||
|
||||
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(
|
||||
string.Format("Directory \"{0}\" doesn't seem to contain an Orchard installation", directory));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user