mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-20 12:58:39 +00:00
298 lines
No EOL
9.3 KiB
C#
298 lines
No EOL
9.3 KiB
C#
using Cosmos.Build.Common;
|
|
using Cosmos.Assembler;
|
|
using Cosmos.Assembler.x86;
|
|
using Cosmos.IL2CPU.X86;
|
|
using Cosmos.IL2CPU;
|
|
using Microsoft.Win32;
|
|
using Microsoft.Build.Utilities;
|
|
using Microsoft.Build.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Text;
|
|
using Cosmos.Debug.Common;
|
|
|
|
namespace Cosmos.Build.MSBuild {
|
|
// Class is separated from MSBuild task so we can call it from debugging and standalone applications.
|
|
public class IL2CPUTask {
|
|
|
|
// FIX THIS: when the kernel class changes, fix the name below
|
|
const string FULLASSEMBLYNAME_KERNEL = "Cosmos.System.Kernel";
|
|
|
|
public Action<string> OnLogMessage;
|
|
public Action<string> OnLogError;
|
|
public Action<string> OnLogWarning;
|
|
public Action<Exception> OnLogException;
|
|
|
|
protected void LogMessage(string aMsg) {
|
|
if (OnLogMessage != null) {
|
|
OnLogMessage(aMsg);
|
|
}
|
|
}
|
|
|
|
protected void LogWarning(string aMsg) {
|
|
if (OnLogWarning != null) {
|
|
OnLogWarning(aMsg);
|
|
}
|
|
}
|
|
|
|
protected void LogError(string aMsg) {
|
|
if (OnLogError != null) {
|
|
OnLogError(aMsg);
|
|
}
|
|
}
|
|
|
|
protected void LogException(Exception e) {
|
|
if (OnLogException != null) {
|
|
OnLogException(e);
|
|
}
|
|
}
|
|
|
|
protected static void CheckFirstTime() {
|
|
var xSearchDirs = new List<string>();
|
|
xSearchDirs.Add(Path.GetDirectoryName(typeof(IL2CPU).Assembly.Location));
|
|
|
|
using (var xReg = Registry.LocalMachine.OpenSubKey("Software\\Cosmos", false)) {
|
|
var xPath = (string)xReg.GetValue(null);
|
|
xSearchDirs.Add(xPath);
|
|
xSearchDirs.Add(Path.Combine(xPath, "Kernel"));
|
|
}
|
|
mSearchDirs = xSearchDirs.ToArray();
|
|
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
|
}
|
|
|
|
protected static string[] mSearchDirs = new string[0];
|
|
|
|
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
|
|
var xShortName = args.Name;
|
|
if (xShortName.Contains(',')) {
|
|
xShortName = xShortName.Substring(0, xShortName.IndexOf(','));
|
|
// TODO: remove following statement if it proves unnecessary
|
|
if (xShortName.Contains(',')) {
|
|
throw new Exception("Algo error");
|
|
}
|
|
}
|
|
foreach (var xDir in mSearchDirs) {
|
|
var xPath = Path.Combine(xDir, xShortName + ".dll");
|
|
if (File.Exists(xPath)) {
|
|
return Assembly.LoadFrom(xPath);
|
|
}
|
|
xPath = Path.Combine(xDir, xShortName + ".exe");
|
|
if (File.Exists(xPath)) {
|
|
return Assembly.LoadFrom(xPath);
|
|
}
|
|
}
|
|
if (mStaticLog != null) {
|
|
mStaticLog(string.Format("Assembly '{0}' not resolved!", args.Name));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected static Action<string> mStaticLog = null;
|
|
|
|
public string DebugMode {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string TraceAssemblies {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public byte DebugCom {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool UseNAsm {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ITaskItem[] References {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string OutputFilename {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool EnableLogging {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool EmitDebugSymbols {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool IgnoreDebugStubAttribute
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected bool Initialize()
|
|
{
|
|
CheckFirstTime();
|
|
if (References != null) {
|
|
var xSearchPaths = new List<string>(mSearchDirs);
|
|
foreach (var xRef in References) {
|
|
if (xRef.MetadataNames.OfType<string>().Contains("FullPath")) {
|
|
var xName = xRef.GetMetadata("FullPath");
|
|
var xDir = Path.GetDirectoryName(xName);
|
|
if (!xSearchPaths.Contains(xDir)) {
|
|
xSearchPaths.Insert(0, xDir);
|
|
}
|
|
if (xName.Length > 0) {
|
|
if (File.Exists(xName))
|
|
Assembly.LoadFile(xName);
|
|
else
|
|
LogWarning(string.Format("File {0} does not exist!", xName));
|
|
}
|
|
}
|
|
}
|
|
mSearchDirs = xSearchPaths.ToArray();
|
|
}
|
|
if (String.IsNullOrEmpty(DebugMode)) {
|
|
mDebugMode = Cosmos.Build.Common.DebugMode.None;
|
|
} else {
|
|
if (!Enum.GetNames(typeof(DebugMode)).Contains(DebugMode, StringComparer.InvariantCultureIgnoreCase)) {
|
|
LogError("Invalid DebugMode specified");
|
|
return false;
|
|
}
|
|
mDebugMode = (DebugMode)Enum.Parse(typeof(DebugMode), DebugMode);
|
|
}
|
|
if (String.IsNullOrEmpty(TraceAssemblies)) {
|
|
mTraceAssemblies = Cosmos.Build.Common.TraceAssemblies.User;
|
|
} else {
|
|
if (!Enum.GetNames(typeof(TraceAssemblies)).Contains(TraceAssemblies, StringComparer.InvariantCultureIgnoreCase)) {
|
|
LogError("Invalid TraceAssemblies specified");
|
|
return false;
|
|
}
|
|
mTraceAssemblies = (TraceAssemblies)Enum.Parse(typeof(TraceAssemblies), TraceAssemblies);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected DebugMode mDebugMode = Cosmos.Build.Common.DebugMode.None;
|
|
protected TraceAssemblies mTraceAssemblies = Cosmos.Build.Common.TraceAssemblies.All;
|
|
protected void LogTime(string message) {
|
|
//
|
|
}
|
|
public bool Execute() {
|
|
try {
|
|
LogMessage("Executing IL2CPU on assembly");
|
|
if (!Initialize()) {
|
|
return false;
|
|
}
|
|
|
|
LogTime("Engine execute started");
|
|
// find the kernel's entry point now. we are looking for a public class Kernel, with public static void Boot()
|
|
var xInitMethod = RetrieveEntryPoint();
|
|
if (xInitMethod == null) {
|
|
return false;
|
|
}
|
|
var xOutputFilename = Path.Combine(Path.GetDirectoryName(OutputFilename), Path.GetFileNameWithoutExtension(OutputFilename));
|
|
if (mDebugMode == Common.DebugMode.None) {
|
|
DebugCom = 0;
|
|
}
|
|
var xAsm = new AppAssemblerNasm(DebugCom);
|
|
using (var xDebugInfo = new DebugInfo()) {
|
|
xDebugInfo.CreateCPDB(xOutputFilename + ".cpdb");
|
|
xAsm.DebugInfo = xDebugInfo;
|
|
xAsm.DebugMode = mDebugMode;
|
|
xAsm.TraceAssemblies = mTraceAssemblies;
|
|
xAsm.IgnoreDebugStubAttribute = IgnoreDebugStubAttribute;
|
|
if (this.DebugMode.ToLower() == "none") {
|
|
xAsm.ShouldOptimize = true;
|
|
}
|
|
#if OUTPUT_ELF
|
|
xAsm.EmitELF = true;
|
|
#endif
|
|
|
|
var xNasmAsm = (AssemblerNasm)xAsm.Assembler;
|
|
xAsm.Assembler.Initialize();
|
|
using (var xScanner = new ILScanner(xAsm)) {
|
|
xScanner.TempDebug += x => LogMessage(x);
|
|
if (EnableLogging) {
|
|
xScanner.EnableLogging(xOutputFilename + ".log.html");
|
|
}
|
|
// TODO: shouldn't be here?
|
|
xScanner.QueueMethod(xInitMethod.DeclaringType.BaseType.GetMethod("Start"));
|
|
xScanner.Execute(xInitMethod);
|
|
|
|
using (var xOut = new StreamWriter(OutputFilename, false)) {
|
|
if (EmitDebugSymbols) {
|
|
xNasmAsm.FlushText(xOut);
|
|
xAsm.FinalizeDebugInfo();
|
|
} else {
|
|
xAsm.Assembler.FlushText(xOut);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LogTime("Engine execute finished");
|
|
return true;
|
|
} catch (Exception e) {
|
|
LogException(e);
|
|
LogMessage("Loaded assemblies: ");
|
|
foreach (var xAsm in AppDomain.CurrentDomain.GetAssemblies()) {
|
|
// HACK: find another way to skip dynamic assemblies (which belong to dynamic methods)
|
|
try {
|
|
LogMessage(xAsm.Location);
|
|
} catch {
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected MethodBase RetrieveEntryPoint() {
|
|
Type xFoundType = null;
|
|
foreach (var xRef in References) {
|
|
if (xRef.MetadataNames.OfType<string>().Contains("FullPath")) {
|
|
var xFile = xRef.GetMetadata("FullPath");
|
|
if (File.Exists(xFile)) {
|
|
var xAssembly = Assembly.LoadFile(xFile);
|
|
foreach (var xType in xAssembly.GetExportedTypes()) {
|
|
if (xType.IsGenericTypeDefinition) {
|
|
continue;
|
|
}
|
|
if (xType.IsAbstract) {
|
|
continue;
|
|
}
|
|
if (xType.BaseType.FullName == FULLASSEMBLYNAME_KERNEL) {
|
|
// found kernel?
|
|
if (xFoundType != null) {
|
|
// already a kernel found, which is not supported.
|
|
LogError(string.Format("Two kernels found! '{0}' and '{1}'", xType.AssemblyQualifiedName, xFoundType.AssemblyQualifiedName));
|
|
return null;
|
|
}
|
|
xFoundType = xType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (xFoundType == null) {
|
|
LogError("No Kernel found!");
|
|
return null;
|
|
}
|
|
var xCtor = xFoundType.GetConstructor(Type.EmptyTypes);
|
|
if (xCtor == null) {
|
|
LogError("Kernel has no public default constructor");
|
|
return null;
|
|
}
|
|
return xCtor;
|
|
}
|
|
}
|
|
} |