Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow neon to compile from the source #131

Merged
merged 24 commits into from Nov 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
184 changes: 184 additions & 0 deletions src/Neo.Compiler.MSIL/Compiler.cs
@@ -0,0 +1,184 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.VisualBasic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace Neo.Compiler
{
public class Compiler
{
public class Assembly
{
public readonly byte[] Dll;
public readonly byte[] Pdb;

/// <summary>
/// Constructor
/// </summary>
/// <param name="dll">Library</param>
/// <param name="pdb">PDB</param>
public Assembly(byte[] dll, byte[] pdb)
{
Dll = dll;
Pdb = pdb;
}

/// <summary>
/// Create Assembly
/// </summary>
/// <param name="comp">Compilation</param>
/// <returns>Assembly</returns>
internal static Assembly Create(Compilation comp)
{
using (var streamDll = new MemoryStream())
using (var streamPdb = new MemoryStream())
{
var result = comp.Emit(streamDll, streamPdb);

if (!result.Success)
{
throw new ArgumentException();
}

streamDll.Position = 0;
streamPdb.Position = 0;

return new Assembly(streamDll.ToArray(), streamPdb.ToArray());
}
}
}

/// <summary>
/// Build script
/// </summary>
/// <param name="filename">File name</param>
/// <returns>Assembly</returns>
public static Assembly CompileVBProj(string filename)
{
ExtractFileAndReferences(filename, ".vb", out var files, out var references);
return CompileVBFile(files.ToArray(), references.ToArray());
shargon marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
/// Build script
/// </summary>
/// <param name="filename">File name</param>
/// <returns>Assembly</returns>
public static Assembly CompileCSProj(string filename)
{
ExtractFileAndReferences(filename, ".cs", out var files, out var references);
return CompileCSFile(files.ToArray(), references.ToArray());
}

/// <summary>
/// Extract information in project files
/// </summary>
/// <param name="filename">File name</param>
/// <param name="extension">Extension</param>
/// <param name="files">Files</param>
/// <param name="references">References</param>
private static void ExtractFileAndReferences(string filename, string extension, out List<string> files, out List<string> references)
{
var fileInfo = new FileInfo(filename);

if (!fileInfo.Exists)
{
throw new FileNotFoundException(filename);
}

// Compile csproj source

var reader = XmlReader.Create(filename, new XmlReaderSettings() { XmlResolver = null });
var projDefinition = XDocument.Load(reader);

// Detect references

references = projDefinition
.Element("Project")
.Elements("ItemGroup")
.Elements("PackageReference")
.Select(u => u.Attribute("Include").Value + ".dll")
.ToList();

// Detect hints

var refs = projDefinition
.Element("Project")
.Elements("ItemGroup")
.Elements("Reference")
.Elements("HintPath")
.Select(u => u.Value)
.ToList();

if (refs.Count > 0)
{
references.AddRange(refs);
}

// Detect files

files = projDefinition
.Element("Project")
.Elements("ItemGroup")
.Elements("Compile")
.Select(u => u.Attribute("Update").Value)
.ToList();

files.AddRange(Directory.GetFiles(fileInfo.Directory.FullName, "*" + extension, SearchOption.AllDirectories));
files = files.Distinct().ToList();
reader.Dispose();
}

/// <summary>
/// Build script
/// </summary>
/// <param name="filenames">File names</param>
/// <param name="references">References</param>
/// <returns>Assembly</returns>
public static Assembly CompileVBFile(string[] filenames, string[] references)
{
var tree = filenames.Select(u => VisualBasicSyntaxTree.ParseText(File.ReadAllText(u))).ToArray();
var op = new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release);
return Assembly.Create(VisualBasicCompilation.Create("SmartContract", tree, CreateReferences(references), op));
}

/// <summary>
/// Build script
/// </summary>
/// <param name="filenames">File names</param>
/// <param name="references">References</param>
/// <returns>Assembly</returns>
public static Assembly CompileCSFile(string[] filenames, string[] references)
{
var tree = filenames.Select(u => CSharpSyntaxTree.ParseText(File.ReadAllText(u))).ToArray();
var op = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release);
return Assembly.Create(CSharpCompilation.Create("SmartContract", tree, CreateReferences(references), op));
}

/// <summary>
/// Create references
/// </summary>
/// <param name="references">References</param>
/// <returns>MetadataReferences</returns>
private static MetadataReference[] CreateReferences(params string[] references)
{
var coreDir = Path.GetDirectoryName(typeof(object).Assembly.Location);
var refs = new List<MetadataReference>(new MetadataReference[]
{
MetadataReference.CreateFromFile(Path.Combine(coreDir, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.Numerics.dll")),
MetadataReference.CreateFromFile(typeof(System.ComponentModel.DisplayNameAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Neo.SmartContract.Framework.SmartContract).Assembly.Location),
});
refs.AddRange(references.Select(u => MetadataReference.CreateFromFile(u)));
return refs.ToArray();
}
}
}
7 changes: 6 additions & 1 deletion src/Neo.Compiler.MSIL/Neo.Compiler.MSIL.csproj
Expand Up @@ -22,8 +22,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.3.1" />
<PackageReference Include="Mono.Cecil" Version="0.11.1" />
<PackageReference Include="Neo" Version="3.0.0-CI00212" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Neo.SmartContract.Framework\Neo.SmartContract.Framework.csproj" />
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>
</Project>
133 changes: 106 additions & 27 deletions src/Neo.Compiler.MSIL/Program.cs
Expand Up @@ -20,22 +20,38 @@ public class Program
//控制台输出约定了特别的语法
public static void Main(string[] args)
{
//set console
Console.OutputEncoding = System.Text.Encoding.UTF8;
// Set console
Console.OutputEncoding = Encoding.UTF8;
var log = new DefLogger();
log.Log("Neo.Compiler.MSIL console app v" + Assembly.GetEntryAssembly().GetName().Version);

// Check argmuents
if (args.Length == 0)
{
log.Log("need one param for DLL filename.");
log.Log("Example:neon abc.dll");
log.Log("You need a parameter to specify the DLL or the file name of the project.");
log.Log("Examples: ");
log.Log(" neon mySmartContract.dll");
log.Log(" neon mySmartContract.csproj");

Environment.Exit(-1);
return;
}

var fileInfo = new FileInfo(args[0]);

// Set current directory
if (!fileInfo.Exists)
{
log.Log("Could not find file " + fileInfo.FullName);
Environment.Exit(-1);
return;
}

string filename = args[0];
string onlyname = Path.GetFileNameWithoutExtension(filename);
string filepdb = onlyname + ".pdb";
var path = Path.GetDirectoryName(filename);
Stream fs;
Stream fspdb;
var onlyname = Path.GetFileNameWithoutExtension(fileInfo.Name);
var path = fileInfo.Directory.FullName;

if (!string.IsNullOrEmpty(path))
{
try
Expand All @@ -50,27 +66,84 @@ public static void Main(string[] args)
}
}

ILModule mod = new ILModule(log);
Stream fs;
Stream fspdb = null;

//open file
try
switch (fileInfo.Extension.ToLowerInvariant())
{
fs = File.OpenRead(filename);
case ".csproj":
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
// Compile csproj file

if (File.Exists(filepdb))
{
fspdb = File.OpenRead(filepdb);
}
log.Log("Compiling from csproj project");
var output = Compiler.CompileCSProj(fileInfo.FullName);
fs = new MemoryStream(output.Dll);
fspdb = new MemoryStream(output.Pdb);
break;
}
case ".vbproj":
{
// Compile vbproj file

log.Log("Compiling from vbproj project");
var output = Compiler.CompileVBProj(fileInfo.FullName);
fs = new MemoryStream(output.Dll);
fspdb = new MemoryStream(output.Pdb);
break;
}
case ".cs":
{
// Compile C# files

log.Log("Compiling from c# source");
var output = Compiler.CompileCSFile(new string[] { fileInfo.FullName }, new string[0]);
fs = new MemoryStream(output.Dll);
fspdb = new MemoryStream(output.Pdb);
break;
}
case ".vb":
{
// Compile VB files

log.Log("Compiling from VB source");
var output = Compiler.CompileVBFile(new string[] { fileInfo.FullName }, new string[0]);
fs = new MemoryStream(output.Dll);
fspdb = new MemoryStream(output.Pdb);
break;
shargon marked this conversation as resolved.
Show resolved Hide resolved
}
case ".dll":
{
string filepdb = onlyname + ".pdb";

// Open file
try
{
fs = fileInfo.OpenRead();

if (File.Exists(filepdb))
{
fspdb = File.OpenRead(filepdb);
}
else
{
fspdb = null;
}
}
catch (Exception err)
{
log.Log("Open File Error:" + err.ToString());
return;
}
break;
}
default:
{
log.Log("File format not supported by neon: " + path);
Environment.Exit(-1);
return;
}
}
catch (Exception err)
{
log.Log("Open File Error:" + err.ToString());
return;
}
//load module

ILModule mod = new ILModule(log);

// Load module
try
{
mod.LoadModule(fs, fspdb);
Expand All @@ -84,7 +157,8 @@ public static void Main(string[] args)
int bSucc = 0;
string jsonstr = null;
NeoModule module = null;
//convert and build

// Convert and build
try
{
var conv = new ModuleConverter(log);
Expand Down Expand Up @@ -112,7 +186,9 @@ public static void Main(string[] args)
log.Log("Convert Error:" + err.ToString());
return;
}
//write bytes

// Write bytes

try
{
string bytesname = onlyname + ".nef";
Expand Down Expand Up @@ -140,6 +216,7 @@ public static void Main(string[] args)
log.Log("Write Bytes Error:" + err.ToString());
return;
}

try
{
string abiname = onlyname + ".abi.json";
Expand All @@ -154,6 +231,7 @@ public static void Main(string[] args)
log.Log("Write abi Error:" + err.ToString());
return;
}

try
{
var features = module == null ? ContractFeatures.NoProperty : module.attributes
Expand All @@ -180,6 +258,7 @@ public static void Main(string[] args)
log.Log("Write manifest Error:" + err.ToString());
return;
}

try
{
fs.Dispose();
Expand Down