The Labs \ Source Viewer \ SSCLI \ System.CodeDom.Compiler \ CodeCompiler

  1. //------------------------------------------------------------------------------
  2. // <copyright file="CodeCompiler.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.CodeDom.Compiler
  16. {
  17.     using System.Text;
  18.    
  19.     using System.Diagnostics;
  20.     using System;
  21.     using Microsoft.Win32;
  22.     using Microsoft.Win32.SafeHandles;
  23.     using System.IO;
  24.     using System.Collections;
  25.     using System.Security;
  26.     using System.Security.Permissions;
  27.     using System.Security.Principal;
  28.     using System.Reflection;
  29.     using System.CodeDom;
  30.     using System.Globalization;
  31.     using System.Runtime.Versioning;
  32.    
  33.     /// <devdoc>
  34.     /// <para>Provides a
  35.     /// base
  36.     /// class for code compilers.</para>
  37.     /// </devdoc>
  38.     [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
  39.     [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
  40.     public abstract class CodeCompiler : CodeGenerator, ICodeCompiler
  41.     {
  42.        
  43.         /// <internalonly/>
  44.         CompilerResults ICodeCompiler.CompileAssemblyFromDom(CompilerParameters options, CodeCompileUnit e)
  45.         {
  46.             if (options == null) {
  47.                 throw new ArgumentNullException("options");
  48.             }
  49.            
  50.             try {
  51.                 return FromDom(options, e);
  52.             }
  53.             finally {
  54.                 options.TempFiles.SafeDelete();
  55.             }
  56.         }
  57.        
  58.         /// <internalonly/>
  59.         [ResourceExposure(ResourceScope.Machine)]
  60.         [ResourceConsumption(ResourceScope.Machine)]
  61.         CompilerResults ICodeCompiler.CompileAssemblyFromFile(CompilerParameters options, string fileName)
  62.         {
  63.             if (options == null) {
  64.                 throw new ArgumentNullException("options");
  65.             }
  66.            
  67.             try {
  68.                 return FromFile(options, fileName);
  69.             }
  70.             finally {
  71.                 options.TempFiles.SafeDelete();
  72.             }
  73.         }
  74.        
  75.         /// <internalonly/>
  76.         CompilerResults ICodeCompiler.CompileAssemblyFromSource(CompilerParameters options, string source)
  77.         {
  78.             if (options == null) {
  79.                 throw new ArgumentNullException("options");
  80.             }
  81.            
  82.             try {
  83.                 return FromSource(options, source);
  84.             }
  85.             finally {
  86.                 options.TempFiles.SafeDelete();
  87.             }
  88.         }
  89.        
  90.         /// <internalonly/>
  91.         CompilerResults ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, string[] sources)
  92.         {
  93.             if (options == null) {
  94.                 throw new ArgumentNullException("options");
  95.             }
  96.            
  97.             try {
  98.                 return FromSourceBatch(options, sources);
  99.             }
  100.             finally {
  101.                 options.TempFiles.SafeDelete();
  102.             }
  103.         }
  104.        
  105.         /// <internalonly/>
  106.         [ResourceExposure(ResourceScope.Machine)]
  107.         [ResourceConsumption(ResourceScope.Machine)]
  108.         CompilerResults ICodeCompiler.CompileAssemblyFromFileBatch(CompilerParameters options, string[] fileNames)
  109.         {
  110.             if (options == null) {
  111.                 throw new ArgumentNullException("options");
  112.             }
  113.             if (fileNames == null)
  114.                 throw new ArgumentNullException("fileNames");
  115.            
  116.             try {
  117.                 // Try opening the files to make sure they exists. This will throw an exception
  118.                 // if it doesn't
  119.                 foreach (string fileName in fileNames) {
  120.                     using (Stream str = File.OpenRead(fileName)) {
  121.                     }
  122.                 }
  123.                
  124.                 return FromFileBatch(options, fileNames);
  125.             }
  126.             finally {
  127.                 options.TempFiles.SafeDelete();
  128.             }
  129.         }
  130.        
  131.         /// <internalonly/>
  132.         CompilerResults ICodeCompiler.CompileAssemblyFromDomBatch(CompilerParameters options, CodeCompileUnit[] ea)
  133.         {
  134.             if (options == null) {
  135.                 throw new ArgumentNullException("options");
  136.             }
  137.            
  138.             try {
  139.                 return FromDomBatch(options, ea);
  140.             }
  141.             finally {
  142.                 options.TempFiles.SafeDelete();
  143.             }
  144.         }
  145.        
  146.         /// <devdoc>
  147.         /// <para>
  148.         /// Gets
  149.         /// or sets the file extension to use for source files.
  150.         /// </para>
  151.         /// </devdoc>
  152.         protected abstract string FileExtension {
  153.             get;
  154.         }
  155.        
  156.         /// <devdoc>
  157.         /// <para>Gets or
  158.         /// sets the name of the compiler executable.</para>
  159.         /// </devdoc>
  160.         protected abstract string CompilerName {
  161.             get;
  162.         }
  163.        
  164.        
  165.         [ResourceExposure(ResourceScope.Machine)]
  166.         [ResourceConsumption(ResourceScope.Machine)]
  167.         internal void Compile(CompilerParameters options, string compilerDirectory, string compilerExe, string arguments, ref string outputFile, ref int nativeReturnValue, string trueArgs)
  168.         {
  169.             string errorFile = null;
  170.             outputFile = options.TempFiles.AddExtension("out");
  171.            
  172.             // We try to execute the compiler with a full path name.
  173.             string fullname = compilerDirectory + compilerExe;
  174.             if (File.Exists(fullname)) {
  175.                 string trueCmdLine = null;
  176.                 if (trueArgs != null)
  177.                     trueCmdLine = "\"" + fullname + "\" " + trueArgs;
  178.                 nativeReturnValue = Executor.ExecWaitWithCapture(options.SafeUserToken, "\"" + fullname + "\" " + arguments, Environment.CurrentDirectory, options.TempFiles, ref outputFile, ref errorFile, trueCmdLine);
  179.             }
  180.             else {
  181.                 throw new InvalidOperationException(SR.GetString(SR.CompilerNotFound, fullname));
  182.             }
  183.         }
  184.        
  185.         /// <devdoc>
  186.         /// <para>
  187.         /// Compiles the specified compile unit and options, and returns the results
  188.         /// from the compilation.
  189.         /// </para>
  190.         /// </devdoc>
  191.         protected virtual CompilerResults FromDom(CompilerParameters options, CodeCompileUnit e)
  192.         {
  193.             if (options == null) {
  194.                 throw new ArgumentNullException("options");
  195.             }
  196.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  197.            
  198.             CodeCompileUnit[] units = new CodeCompileUnit[1];
  199.             units[0] = e;
  200.             return FromDomBatch(options, units);
  201.         }
  202.        
  203.         /// <devdoc>
  204.         /// <para>
  205.         /// Compiles the specified file using the specified options, and returns the
  206.         /// results from the compilation.
  207.         /// </para>
  208.         /// </devdoc>
  209.         [ResourceExposure(ResourceScope.Machine)]
  210.         [ResourceConsumption(ResourceScope.Machine)]
  211.         protected virtual CompilerResults FromFile(CompilerParameters options, string fileName)
  212.         {
  213.             if (options == null) {
  214.                 throw new ArgumentNullException("options");
  215.             }
  216.             if (fileName == null)
  217.                 throw new ArgumentNullException("fileName");
  218.            
  219.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  220.            
  221.             // Try opening the file to make sure it exists. This will throw an exception
  222.             // if it doesn't
  223.             using (Stream str = File.OpenRead(fileName)) {
  224.             }
  225.            
  226.             string[] filenames = new string[1];
  227.             filenames[0] = fileName;
  228.             return FromFileBatch(options, filenames);
  229.         }
  230.        
  231.         /// <devdoc>
  232.         /// <para>
  233.         /// Compiles the specified source code using the specified options, and
  234.         /// returns the results from the compilation.
  235.         /// </para>
  236.         /// </devdoc>
  237.         protected virtual CompilerResults FromSource(CompilerParameters options, string source)
  238.         {
  239.             if (options == null) {
  240.                 throw new ArgumentNullException("options");
  241.             }
  242.            
  243.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  244.            
  245.             string[] sources = new string[1];
  246.             sources[0] = source;
  247.            
  248.             return FromSourceBatch(options, sources);
  249.         }
  250.        
  251.         /// <devdoc>
  252.         /// <para>
  253.         /// Compiles the specified compile units and
  254.         /// options, and returns the results from the compilation.
  255.         /// </para>
  256.         /// </devdoc>
  257.         protected virtual CompilerResults FromDomBatch(CompilerParameters options, CodeCompileUnit[] ea)
  258.         {
  259.             if (options == null) {
  260.                 throw new ArgumentNullException("options");
  261.             }
  262.             if (ea == null)
  263.                 throw new ArgumentNullException("ea");
  264.            
  265.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  266.            
  267.             string[] filenames = new string[ea.Length];
  268.            
  269.             CompilerResults results = null;
  270.            
  271.             for (int i = 0; i < ea.Length; i++) {
  272.                 if (ea[i] == null)
  273.                     continue;
  274.                 // the other two batch methods just work if one element is null, so we'll match that.
  275.                 ResolveReferencedAssemblies(options, ea[i]);
  276.                 filenames[i] = options.TempFiles.AddExtension(i + FileExtension);
  277.                 Stream temp = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read);
  278.                 try {
  279.                     using (StreamWriter sw = new StreamWriter(temp, Encoding.UTF8)) {
  280.                         ((ICodeGenerator)this).GenerateCodeFromCompileUnit(ea[i], sw, Options);
  281.                         sw.Flush();
  282.                     }
  283.                 }
  284.                 finally {
  285.                     temp.Close();
  286.                 }
  287.             }
  288.            
  289.             results = FromFileBatch(options, filenames);
  290.             return results;
  291.         }
  292.        
  293.         /// <devdoc>
  294.         /// <para>
  295.         /// Because CodeCompileUnit and CompilerParameters both have a referenced assemblies
  296.         /// property, they must be reconciled. However, because you can compile multiple
  297.         /// compile units with one set of options, it will simply merge them.
  298.         /// </para>
  299.         /// </devdoc>
  300.         private void ResolveReferencedAssemblies(CompilerParameters options, CodeCompileUnit e)
  301.         {
  302.             if (e.ReferencedAssemblies.Count > 0) {
  303.                 foreach (string assemblyName in e.ReferencedAssemblies) {
  304.                     if (!options.ReferencedAssemblies.Contains(assemblyName)) {
  305.                         options.ReferencedAssemblies.Add(assemblyName);
  306.                     }
  307.                 }
  308.             }
  309.         }
  310.        
  311.         /// <devdoc>
  312.         /// <para>
  313.         /// Compiles the specified files using the specified options, and returns the
  314.         /// results from the compilation.
  315.         /// </para>
  316.         /// </devdoc>
  317.         [ResourceExposure(ResourceScope.Machine)]
  318.         [ResourceConsumption(ResourceScope.Machine)]
  319.         protected virtual CompilerResults FromFileBatch(CompilerParameters options, string[] fileNames)
  320.         {
  321.             if (options == null) {
  322.                 throw new ArgumentNullException("options");
  323.             }
  324.             if (fileNames == null)
  325.                 throw new ArgumentNullException("fileNames");
  326.            
  327.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  328.            
  329.             string outputFile = null;
  330.             int retValue = 0;
  331.            
  332.             CompilerResults results = new CompilerResults(options.TempFiles);
  333.             SecurityPermission perm1 = new SecurityPermission(SecurityPermissionFlag.ControlEvidence);
  334.             perm1.Assert();
  335.             try {
  336.                 results.Evidence = options.Evidence;
  337.             }
  338.             finally {
  339.                 SecurityPermission.RevertAssert();
  340.             }
  341.             bool createdEmptyAssembly = false;
  342.            
  343.             if (options.OutputAssembly == null || options.OutputAssembly.Length == 0) {
  344.                 string extension = (options.GenerateExecutable) ? "exe" : "dll";
  345.                 options.OutputAssembly = results.TempFiles.AddExtension(extension, !options.GenerateInMemory);
  346.                
  347.                 // Create an empty assembly. This is so that the file will have permissions that
  348.                 // we can later access with our current credential. If we don't do this, the compiler
  349.                 // could end up creating an assembly that we cannot open
  350.                 new FileStream(options.OutputAssembly, FileMode.Create, FileAccess.ReadWrite).Close();
  351.                 createdEmptyAssembly = true;
  352.             }
  353.            
  354.             results.TempFiles.AddExtension("ildb");
  355.            
  356.            
  357.             string args = CmdArgsFromParameters(options) + " " + JoinStringArray(fileNames, " ");
  358.            
  359.             // Use a response file if the compiler supports it
  360.             string responseFileArgs = GetResponseFileCmdArgs(options, args);
  361.             string trueArgs = null;
  362.             if (responseFileArgs != null) {
  363.                 trueArgs = args;
  364.                 args = responseFileArgs;
  365.             }
  366.            
  367.             Compile(options, Executor.GetRuntimeInstallDirectory(), CompilerName, args, ref outputFile, ref retValue, trueArgs);
  368.            
  369.             results.NativeCompilerReturnValue = retValue;
  370.            
  371.             // only look for errors/warnings if the compile failed or the caller set the warning level
  372.             if (retValue != 0 || options.WarningLevel > 0) {
  373.                
  374.                 FileStream outputStream = new FileStream(outputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  375.                 try {
  376.                     if (outputStream.Length > 0) {
  377.                         // The output of the compiler is in UTF8
  378.                         StreamReader sr = new StreamReader(outputStream, Encoding.UTF8);
  379.                         string line;
  380.                         do {
  381.                             line = sr.ReadLine();
  382.                             if (line != null) {
  383.                                 results.Output.Add(line);
  384.                                
  385.                                 ProcessCompilerOutputLine(results, line);
  386.                             }
  387.                         }
  388.                         while (line != null);
  389.                     }
  390.                 }
  391.                 finally {
  392.                     outputStream.Close();
  393.                 }
  394.                
  395.                 // Delete the empty assembly if we created one
  396.                 if (retValue != 0 && createdEmptyAssembly)
  397.                     File.Delete(options.OutputAssembly);
  398.             }
  399.            
  400.             if (!results.Errors.HasErrors && options.GenerateInMemory) {
  401.                 FileStream fs = new FileStream(options.OutputAssembly, FileMode.Open, FileAccess.Read, FileShare.Read);
  402.                 try {
  403.                     int fileLen = (int)fs.Length;
  404.                     byte[] b = new byte[fileLen];
  405.                     fs.Read(b, 0, fileLen);
  406.                     SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.ControlEvidence);
  407.                     perm.Assert();
  408.                     try {
  409.                         results.CompiledAssembly = Assembly.Load(b, null, options.Evidence);
  410.                     }
  411.                     finally {
  412.                         SecurityPermission.RevertAssert();
  413.                     }
  414.                 }
  415.                 finally {
  416.                     fs.Close();
  417.                 }
  418.             }
  419.             else {
  420.                
  421.                 results.PathToAssembly = options.OutputAssembly;
  422.             }
  423.            
  424.             return results;
  425.         }
  426.        
  427.         /// <devdoc>
  428.         /// <para>Processes the specified line from the specified <see cref='System.CodeDom.Compiler.CompilerResults'/> .</para>
  429.         /// </devdoc>
  430.         protected abstract void ProcessCompilerOutputLine(CompilerResults results, string line);
  431.        
  432.         /// <devdoc>
  433.         /// <para>
  434.         /// Gets the command arguments from the specified <see cref='System.CodeDom.Compiler.CompilerParameters'/>.
  435.         /// </para>
  436.         /// </devdoc>
  437.         protected abstract string CmdArgsFromParameters(CompilerParameters options);
  438.        
  439.         /// <devdoc>
  440.         /// <para>[To be supplied.]</para>
  441.         /// </devdoc>
  442.         protected virtual string GetResponseFileCmdArgs(CompilerParameters options, string cmdArgs)
  443.         {
  444.            
  445.             string responseFileName = options.TempFiles.AddExtension("cmdline");
  446.            
  447.             Stream temp = new FileStream(responseFileName, FileMode.Create, FileAccess.Write, FileShare.Read);
  448.             try {
  449.                 using (StreamWriter sw = new StreamWriter(temp, Encoding.UTF8)) {
  450.                     sw.Write(cmdArgs);
  451.                     sw.Flush();
  452.                 }
  453.             }
  454.             finally {
  455.                 temp.Close();
  456.             }
  457.            
  458.             return "@\"" + responseFileName + "\"";
  459.         }
  460.        
  461.         /// <devdoc>
  462.         /// <para>
  463.         /// Compiles the specified source code strings using the specified options, and
  464.         /// returns the results from the compilation.
  465.         /// </para>
  466.         /// </devdoc>
  467.         protected virtual CompilerResults FromSourceBatch(CompilerParameters options, string[] sources)
  468.         {
  469.             if (options == null) {
  470.                 throw new ArgumentNullException("options");
  471.             }
  472.             if (sources == null)
  473.                 throw new ArgumentNullException("sources");
  474.            
  475.             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  476.            
  477.             string[] filenames = new string[sources.Length];
  478.            
  479.             CompilerResults results = null;
  480.             for (int i = 0; i < sources.Length; i++) {
  481.                 string name = options.TempFiles.AddExtension(i + FileExtension);
  482.                 Stream temp = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read);
  483.                 try {
  484.                     using (StreamWriter sw = new StreamWriter(temp, Encoding.UTF8)) {
  485.                         sw.Write(sources[i]);
  486.                         sw.Flush();
  487.                     }
  488.                 }
  489.                 finally {
  490.                     temp.Close();
  491.                 }
  492.                 filenames[i] = name;
  493.             }
  494.             results = FromFileBatch(options, filenames);
  495.            
  496.             return results;
  497.         }
  498.        
  499.         /// <devdoc>
  500.         /// <para>Joins the specified string arrays.</para>
  501.         /// </devdoc>
  502.         protected static string JoinStringArray(string[] sa, string separator)
  503.         {
  504.             if (sa == null || sa.Length == 0)
  505.                 return String.Empty;
  506.            
  507.             if (sa.Length == 1) {
  508.                 return "\"" + sa[0] + "\"";
  509.             }
  510.            
  511.             StringBuilder sb = new StringBuilder();
  512.             for (int i = 0; i < sa.Length - 1; i++) {
  513.                 sb.Append("\"");
  514.                 sb.Append(sa[i]);
  515.                 sb.Append("\"");
  516.                 sb.Append(separator);
  517.             }
  518.             sb.Append("\"");
  519.             sb.Append(sa[sa.Length - 1]);
  520.             sb.Append("\"");
  521.            
  522.             return sb.ToString();
  523.         }
  524.     }
  525. }

Developer Fusion