using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace Semmle.Extraction.CSharp { /// /// Identifies the compiler and framework from the command line arguments. /// --compiler specifies the compiler /// --framework specifies the .net framework /// public class CompilerVersion { const string csc_rsp = "csc.rsp"; readonly string specifiedFramework = null; /// /// The value specified by --compiler, or null. /// public string SpecifiedCompiler { get; private set; } /// /// Why was the candidate exe rejected as a compiler? /// public string SkipReason { get; private set; } /// /// Probes the compiler (if specified). /// /// The command line arguments. public CompilerVersion(Options options) { SpecifiedCompiler = options.CompilerName; specifiedFramework = options.Framework; if (SpecifiedCompiler != null) { if (!File.Exists(SpecifiedCompiler)) { SkipExtractionBecause("the specified file does not exist"); return; } // Reads the file details from the .exe var versionInfo = FileVersionInfo.GetVersionInfo(SpecifiedCompiler); var compilerDir = Path.GetDirectoryName(SpecifiedCompiler); var knownCompilerNames = new Dictionary { { "csc.exe", "Microsoft" }, { "csc2.exe", "Microsoft" }, { "csc.dll", "Microsoft" }, { "mcs.exe", "Novell" } }; var mscorlibExists = File.Exists(Path.Combine(compilerDir, "mscorlib.dll")); if (specifiedFramework == null && mscorlibExists) { specifiedFramework = compilerDir; } if (!knownCompilerNames.TryGetValue(versionInfo.OriginalFilename, out var vendor)) { SkipExtractionBecause("the compiler name is not recognised"); return; } if (versionInfo.LegalCopyright == null || !versionInfo.LegalCopyright.Contains(vendor)) { SkipExtractionBecause($"the compiler isn't copyright {vendor}, but instead {versionInfo.LegalCopyright ?? ""}"); return; } } ArgsWithResponse = AddDefaultResponse(CscRsp, options.CompilerArguments).ToArray(); } void SkipExtractionBecause(string reason) { SkipExtraction = true; SkipReason = reason; } /// /// The directory containing the .Net Framework. /// public string FrameworkPath => specifiedFramework ?? RuntimeEnvironment.GetRuntimeDirectory(); /// /// The file csc.rsp. /// string CscRsp => Path.Combine(FrameworkPath, csc_rsp); /// /// Should we skip extraction? /// Only if csc.exe was specified but it wasn't a compiler. /// public bool SkipExtraction { get; private set; } /// /// Gets additional reference directories - the compiler directory. /// public string AdditionalReferenceDirectories => SpecifiedCompiler != null ? Path.GetDirectoryName(SpecifiedCompiler) : null; /// /// Adds @csc.rsp to the argument list to mimic csc.exe. /// /// The full pathname of csc.rsp. /// The other command line arguments. /// Modified list of arguments. static IEnumerable AddDefaultResponse(string responseFile, IEnumerable args) { return SuppressDefaultResponseFile(args) || !File.Exists(responseFile) ? args : new[] { "@" + responseFile }.Concat(args); } static bool SuppressDefaultResponseFile(IEnumerable args) { return args.Any(arg => new[] { "/noconfig", "-noconfig" }.Contains(arg.ToLowerInvariant())); } public readonly string[] ArgsWithResponse; } }