mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Autofac.Features.Metadata;
|
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
|
|
||||||
namespace Orchard.Commands.Builtin {
|
namespace Orchard.Commands.Builtin {
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
namespace Orchard.Localization {
|
using System;
|
||||||
public class LocalizedString {
|
|
||||||
|
namespace Orchard.Localization {
|
||||||
|
public class LocalizedString : MarshalByRefObject {
|
||||||
private readonly string _localized;
|
private readonly string _localized;
|
||||||
|
|
||||||
public LocalizedString(string localized) {
|
public LocalizedString(string localized) {
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Web.Hosting;
|
using System.Web.Hosting;
|
||||||
|
|
||||||
namespace Orchard.Host {
|
namespace Orchard.Host {
|
||||||
@@ -19,11 +18,11 @@ namespace Orchard.Host {
|
|||||||
//TODO
|
//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();
|
var agent = Activator.CreateInstance("Orchard.Framework", "Orchard.Commands.CommandHostAgent").Unwrap();
|
||||||
int result = (int)agent.GetType().GetMethod("RunSingleCommand").Invoke(agent, new object[] {
|
int result = (int)agent.GetType().GetMethod("RunSingleCommand").Invoke(agent, new object[] {
|
||||||
Console.In,
|
input,
|
||||||
Console.Out,
|
output,
|
||||||
args.Tenant,
|
args.Tenant,
|
||||||
args.Arguments.ToArray(),
|
args.Arguments.ToArray(),
|
||||||
args.Switches});
|
args.Switches});
|
||||||
|
@@ -48,6 +48,7 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="OrchardHost.cs" />
|
||||||
<Compile Include="Parameters\ICommandParametersParser.cs" />
|
<Compile Include="Parameters\ICommandParametersParser.cs" />
|
||||||
<Compile Include="IOrchardParametersParser.cs" />
|
<Compile Include="IOrchardParametersParser.cs" />
|
||||||
<Compile Include="OrchardParameters.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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Orchard {
|
namespace Orchard {
|
||||||
public class OrchardParameters : MarshalByRefObject {
|
public class OrchardParameters : MarshalByRefObject {
|
||||||
@@ -11,6 +9,5 @@ namespace Orchard {
|
|||||||
public string Tenant { get; set; }
|
public string Tenant { get; set; }
|
||||||
public IEnumerable<string> Arguments { get; set; }
|
public IEnumerable<string> Arguments { get; set; }
|
||||||
public IDictionary<string, string> Switches { get; set; }
|
public IDictionary<string, string> Switches { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Orchard.Parameters {
|
namespace Orchard.Parameters {
|
||||||
public class CommandParametersParser : ICommandParametersParser {
|
public class CommandParametersParser : ICommandParametersParser {
|
||||||
@@ -12,9 +10,17 @@ namespace Orchard.Parameters {
|
|||||||
};
|
};
|
||||||
|
|
||||||
foreach (var arg in args) {
|
foreach (var arg in args) {
|
||||||
|
// Switch?
|
||||||
if (arg[0] == '/') {
|
if (arg[0] == '/') {
|
||||||
var switchKeyValue = arg.Substring(1).Split(':');
|
int index = arg.IndexOf(':');
|
||||||
result.Switches.Add(switchKeyValue[0], switchKeyValue.Length >= 2 ? switchKeyValue[1] : string.Empty);
|
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 {
|
else {
|
||||||
result.Arguments.Add(arg);
|
result.Arguments.Add(arg);
|
||||||
|
@@ -1,106 +1,9 @@
|
|||||||
using System;
|
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 {
|
namespace Orchard {
|
||||||
class Program {
|
public class Program {
|
||||||
private readonly string[] _args;
|
public static int Main(string[] args) {
|
||||||
|
return new OrchardHost(Console.In, Console.Out, args).Run();
|
||||||
private Program(string[] args) {
|
}
|
||||||
_args = args;
|
|
||||||
}
|
|
||||||
|
|
||||||
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