andrew4582

C# Script Compiler

Nov 12th, 2010
322
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.00 KB | None | 0 0
  1. #region Copyright © 2005 Paul Welter. All rights reserved.
  2. /*
  3. Copyright © 2005 Paul Welter. All rights reserved.
  4.  
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions
  7. are met:
  8.  
  9. 1. Redistributions of source code must retain the above copyright
  10.    notice, this list of conditions and the following disclaimer.
  11. 2. Redistributions in binary form must reproduce the above copyright
  12.    notice, this list of conditions and the following disclaimer in the
  13.    documentation and/or other materials provided with the distribution.
  14. 3. The name of the author may not be used to endorse or promote products
  15.    derived from this software without specific prior written permission.
  16.  
  17. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
  18. IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22. NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  *
  28.  * James Higgs ([email protected])
  29. */
  30. #endregion
  31.  
  32. // $Id: Script.cs 275 2006-12-10 19:35:49Z joshuaflanagan $
  33.  
  34. using System;
  35. using System.CodeDom;
  36. using System.CodeDom.Compiler;
  37. using System.Collections.Generic;
  38. using System.Globalization;
  39. using System.IO;
  40. using System.Reflection;
  41. using System.Text;
  42. using System.Xml;
  43. using Microsoft.Build.Utilities;
  44. using Microsoft.Build.Framework;
  45.  
  46. namespace MSBuild.Community.Tasks
  47. {
  48.     /// <summary>
  49.     /// Executes code contained within the task.
  50.     /// </summary>
  51.     /// <include file='AdditionalDocumentation.xml' path='docs/task[@name="Script"]/*'/>
  52.     public class Script : Task
  53.     {
  54.         #region Fields
  55.         private static readonly string[] _defaultNamespaces = {
  56.             "System",
  57.             "System.Collections.Generic",
  58.             "System.IO",
  59.             "System.Text",
  60.             "System.Text.RegularExpressions"
  61.         };
  62.         private string _rootClassName = "msbc" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
  63.  
  64.         #endregion Fields
  65.  
  66.         #region Input Parameters
  67.         private ITaskItem[] _references;
  68.  
  69.         /// <summary>
  70.         /// The required references
  71.         /// </summary>
  72.         public ITaskItem[] References
  73.         {
  74.             get { return _references; }
  75.             set { _references = value; }
  76.         }
  77.  
  78.         private ITaskItem[] _imports;
  79.  
  80.         /// <summary>
  81.         /// The namespaces to import.
  82.         /// </summary>
  83.         public ITaskItem[] Imports
  84.         {
  85.             get { return _imports; }
  86.             set { _imports = value; }
  87.         }
  88.  
  89.         string _language = "C#";
  90.  
  91.         /// <summary>
  92.         /// The language of the script block (defaults to C#).
  93.         /// </summary>
  94.         /// <remarks><para>The supported languages are:</para>
  95.         /// <list type="bullet">
  96.         /// <item><description>Visual Basic.NET (VB, vb, VISUALBASIC)</description></item>
  97.         /// <item><description>C# (C#, c#, CSHARP)</description></item>
  98.         /// <item><description>JavaScript (JS, js, JSCRIPT)</description></item>
  99.         /// <item><description>J# (VJS, vjs, JSHARP)</description></item>
  100.         /// </list> or, proviude the fully-qualified name for a class implementing
  101.         /// <see cref="System.CodeDom.Compiler.CodeDomProvider" />.</remarks>
  102.         [Required]
  103.         public string Language
  104.         {
  105.             get { return _language; }
  106.             set { _language = value; }
  107.         }
  108.  
  109.         private string _mainClass = string.Empty;
  110.  
  111.         /// <summary>
  112.         /// The name of the main class containing the static <c>ScriptMain</c>
  113.         /// entry point.
  114.         /// </summary>
  115.         public string MainClass
  116.         {
  117.             get { return _mainClass; }
  118.             set { _mainClass = value; }
  119.         }
  120.  
  121.         private string _code;
  122.  
  123.         /// <summary>
  124.         /// The code to compile and execute
  125.         /// </summary>
  126.         /// <remarks>
  127.         /// The code must include a static (Shared in VB) method named ScriptMain.
  128.         /// It cannot accept any parameters. If you define the method return a <see cref="String"/>,
  129.         /// the returned value will be available in the <see cref="ReturnValue"/> output property.
  130.         /// </remarks>
  131.         public string Code
  132.         {
  133.             get { return _code; }
  134.             set { _code = value; }
  135.         }
  136.  
  137.         #endregion Input Parameters
  138.  
  139.         string _returnValue;
  140.        
  141.         /// <summary>
  142.         /// The string returned from the custom ScriptMain method.
  143.         /// </summary>
  144.         [Output]
  145.         public string ReturnValue
  146.         {
  147.             get { return _returnValue; }
  148.         }
  149.  
  150.  
  151.         #region Task Overrides
  152.         /// <summary>
  153.         /// Executes the task.
  154.         /// </summary>
  155.         /// <returns><see langword="true"/> if the task ran successfully;
  156.         /// otherwise <see langword="false"/>.</returns>
  157.         public override bool Execute()
  158.         {
  159.             // create compiler info for user-specified language
  160.             CompilerInfo compilerInfo = CreateCompilerInfo(Language);
  161.  
  162.             CodeDomProvider provider = compilerInfo.Provider;
  163.             CompilerParameters options = new CompilerParameters();
  164.             options.GenerateExecutable = false;
  165.             options.GenerateInMemory = true;
  166.             options.MainClass = MainClass;
  167.  
  168.             // add all available assemblies.
  169.             foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
  170.             {
  171.                 try
  172.                 {
  173.                     if (!string.IsNullOrEmpty(asm.Location))
  174.                     {
  175.                         options.ReferencedAssemblies.Add(asm.Location);
  176.                     }
  177.                 }
  178.                 catch (NotSupportedException)
  179.                 {
  180.                     // Ignore - this error is sometimes thrown by asm.Location
  181.                     // for certain dynamic assemblies
  182.                 }
  183.             }
  184.  
  185.             // add (and load) assemblies specified by user
  186.             if (References != null)
  187.             {
  188.                 foreach (ITaskItem item in References)
  189.                 {
  190.                     string assemblyFile = item.ItemSpec;
  191.                     // load assemblies into current AppDomain to ensure assemblies
  192.                     // are available when executing the emitted assembly
  193.                     Assembly loadedAssembly;
  194.                     if (isAssemblyFilePath(assemblyFile))
  195.                     {
  196.                         loadedAssembly = Assembly.LoadFrom(assemblyFile);
  197.                     }
  198.                     else
  199.                     {
  200.                         loadedAssembly = Assembly.Load(assemblyFile);
  201.                     }
  202.  
  203.                     // make assembly available to compiler
  204.                     options.ReferencedAssemblies.Add(loadedAssembly.Location);
  205.                 }
  206.             }
  207.  
  208.             // generate the code
  209.             CodeCompileUnit compileUnit = compilerInfo.GenerateCode(_rootClassName,
  210.                 Code, _imports);
  211.  
  212.             StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
  213.  
  214.             compilerInfo.Provider.GenerateCodeFromCompileUnit(compileUnit, sw, null);
  215.             string code = sw.ToString();
  216.  
  217.             Log.LogMessage(MessageImportance.Low, "Generated code:\n{0}", code);
  218.  
  219.             CompilerResults results = provider.CompileAssemblyFromDom(options, compileUnit);
  220.  
  221.             Assembly compiled = null;
  222.             if (results.Errors.Count > 0)
  223.             {
  224.                 string errors = "There were compiler errors:" + Environment.NewLine;
  225.                 foreach (CompilerError err in results.Errors)
  226.                 {
  227.                     errors += err.ToString() + Environment.NewLine;
  228.                 }
  229.                 errors += code;
  230.                 throw new Exception(errors);
  231.             }
  232.             else
  233.             {
  234.                 compiled = results.CompiledAssembly;
  235.             }
  236.  
  237.             string mainClass = _rootClassName;
  238.             if (!string.IsNullOrEmpty(MainClass))
  239.             {
  240.                 mainClass += "+" + MainClass;
  241.             }
  242.  
  243.             Type mainType = compiled.GetType(mainClass);
  244.             if (mainType == null)
  245.             {
  246.                 //TODO:
  247.                 throw new Exception("Failed - something to do with MainClass");
  248.             }
  249.  
  250.             MethodInfo entry = mainType.GetMethod("ScriptMain");
  251.             // check for task or function definitions.
  252.             if (entry == null)
  253.             {
  254.                 throw new Exception("Could not find an entry point. You must create a static (Shared) method named ScriptMain.");
  255.             }
  256.  
  257.             if (!entry.IsStatic)
  258.             {
  259.                 throw new Exception("Could not find a static (Shared) entry point");
  260.             }
  261.  
  262.             ParameterInfo[] entryParams = entry.GetParameters();
  263.  
  264.             if (entryParams.Length != 0)
  265.             {
  266.                 throw new Exception("Invalid signatiure for ScriptMain");
  267.             }
  268.  
  269.             /*
  270.              * TODO: if we need to change the sig of the entry point...
  271.             if (entryParams[0].ParameterType.FullName != typeof(Project).FullName)
  272.             {
  273.                 throw new Exception("The entry point has the wrong signature");
  274.             }
  275.              * */
  276.  
  277.             // invoke Main method
  278.             object invokeResult = null;
  279.             try
  280.             {
  281.                 invokeResult = entry.Invoke(null, new object[] { });
  282.             }
  283.             catch (TargetInvocationException targetInvocationException)
  284.             {
  285.                 throw targetInvocationException.InnerException;
  286.             }
  287.             _returnValue = invokeResult as string;
  288.  
  289.             return true;
  290.         }
  291.  
  292.         private static bool isAssemblyFilePath(string assemblyFile)
  293.         {
  294.             if (assemblyFile.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) ||
  295.                 assemblyFile.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
  296.             {
  297.                 return true;
  298.             }
  299.             return false;
  300.         }
  301.  
  302.         #endregion Task Overrides
  303.  
  304.         #region Private Static Methods
  305.  
  306.         private static CodeDomProvider CreateCodeDomProvider(string typeName, string assemblyName)
  307.         {
  308.             Assembly providerAssembly = Assembly.Load(assemblyName);
  309.             if (providerAssembly == null)
  310.             {
  311.                 //TODO:
  312.                 throw new Exception("Invalid CodeDomProvider");
  313.             }
  314.  
  315.             Type providerType = providerAssembly.GetType(typeName, true, true);
  316.             return CreateCodeDomProvider(providerType);
  317.         }
  318.  
  319.         private static CodeDomProvider CreateCodeDomProvider(string assemblyQualifiedTypeName)
  320.         {
  321.             Type providerType = Type.GetType(assemblyQualifiedTypeName, true, true);
  322.             return CreateCodeDomProvider(providerType);
  323.         }
  324.  
  325.         private static CodeDomProvider CreateCodeDomProvider(Type providerType)
  326.         {
  327.             object provider = Activator.CreateInstance(providerType);
  328.             if (!(provider is CodeDomProvider))
  329.             {
  330.                 //TODO:
  331.                 throw new Exception("Invalid CodeDomProvider");
  332.             }
  333.             return (CodeDomProvider)provider;
  334.         }
  335.  
  336.         #endregion Private Static Methods
  337.  
  338.         #region Private Methods
  339.         private CompilerInfo CreateCompilerInfo(string language)
  340.         {
  341.             CodeDomProvider provider = null;
  342.             LanguageId languageId;
  343.  
  344.             switch (language)
  345.             {
  346.                 case "vb":
  347.                 case "VB":
  348.                 case "VISUALBASIC":
  349.                     languageId = LanguageId.VisualBasic;
  350.                     provider = CreateCodeDomProvider(typeof(Microsoft.VisualBasic.VBCodeProvider));
  351.                     break;
  352.                 case "c#":
  353.                 case "C#":
  354.                 case "CSHARP":
  355.                     languageId = LanguageId.CSharp;
  356.                     provider = CreateCodeDomProvider(typeof(Microsoft.CSharp.CSharpCodeProvider));
  357.                     break;
  358.                 case "js":
  359.                 case "JS":
  360.                 case "JSCRIPT":
  361.                     languageId = LanguageId.JScript;
  362.                     provider = CreateCodeDomProvider(
  363.                         "Microsoft.JScript.JScriptCodeProvider",
  364.                         "Microsoft.JScript, Culture=neutral");
  365.                     break;
  366.                 case "vjs":
  367.                 case "VJS":
  368.                 case "JSHARP":
  369.                     languageId = LanguageId.JSharp;
  370.                     provider = CreateCodeDomProvider(
  371.                         "Microsoft.VJSharp.VJSharpCodeProvider",
  372.                         "VJSharpCodeProvider, Culture=neutral");
  373.                     break;
  374.                 default:
  375.                     // if its not one of the above then it must be a fully
  376.                     // qualified provider class name
  377.                     languageId = LanguageId.Other;
  378.                     provider = CreateCodeDomProvider(language);
  379.                     break;
  380.             }
  381.  
  382.             return new CompilerInfo(languageId, provider);
  383.         }
  384.  
  385.         #endregion Private Methods
  386.  
  387.         #region Enums
  388.         internal enum LanguageId : int
  389.         {
  390.             CSharp = 1,
  391.             VisualBasic = 2,
  392.             JScript = 3,
  393.             JSharp = 4,
  394.             Other = 5
  395.         }
  396.  
  397.         #endregion Enums
  398.  
  399.         #region Nested Internal Class
  400.         internal class CompilerInfo
  401.         {
  402.             private LanguageId _lang;
  403.             public readonly CodeDomProvider Provider;
  404.  
  405.             public CompilerInfo(LanguageId languageId, CodeDomProvider provider)
  406.             {
  407.                 _lang = languageId;
  408.                 Provider = provider;
  409.  
  410.             }
  411.  
  412.  
  413.             public CodeCompileUnit GenerateCode(string typeName, string codeBody,
  414.                 ITaskItem[] imports)
  415.             {
  416.                 CodeCompileUnit compileUnit = new CodeCompileUnit();
  417.  
  418.                 CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeName);
  419.                 typeDecl.IsClass = true;
  420.                 typeDecl.TypeAttributes = TypeAttributes.Public;
  421.  
  422.                 // pump in the user specified code as a snippet
  423.                 CodeSnippetTypeMember literalMember =
  424.                     new CodeSnippetTypeMember(codeBody);
  425.                 typeDecl.Members.Add(literalMember);
  426.  
  427.                 CodeNamespace nspace = new CodeNamespace();
  428.  
  429.                 //Add default imports
  430.                 foreach (string nameSpace in Script._defaultNamespaces)
  431.                 {
  432.                     nspace.Imports.Add(new CodeNamespaceImport(nameSpace));
  433.                 }
  434.                 if (imports != null)
  435.                 {
  436.                     foreach (ITaskItem item in imports)
  437.                     {
  438.                         string nameSpace = item.ItemSpec;
  439.                         nspace.Imports.Add(new CodeNamespaceImport(nameSpace));
  440.                     }
  441.                 }
  442.                 compileUnit.Namespaces.Add(nspace);
  443.                 nspace.Types.Add(typeDecl);
  444.  
  445.                 return compileUnit;
  446.             }
  447.         }
  448.  
  449.         #endregion Nested Internal Class
  450.     }
  451. }
Advertisement
Add Comment
Please, Sign In to add comment