private static string GetLanguageName(CompilerInvocationInfo invocationInfo) { return(invocationInfo.Tool switch { "csc" => LanguageNames.CSharp, "vbc" => LanguageNames.VisualBasic, _ => throw new NotSupportedException($"Tool '{invocationInfo.Tool}' is not supported."), });
public static async Task <CompilerInvocation> CreateFromInvocationInfoAsync(CompilerInvocationInfo invocationInfo) { // We will use a Workspace to simplify the creation of the compilation, but will be careful not to return the Workspace instance from this class. // We will still provide the language services which are used by the generator itself, but we don't tie it to a Workspace object so we can // run this as an in-proc source generator if one day desired. var workspace = new AdhocWorkspace(); var languageName = GetLanguageName(invocationInfo); var languageServices = workspace.Services.GetLanguageServices(languageName); var mapPath = GetPathMapper(invocationInfo); var splitCommandLine = CommandLineParser.SplitCommandLineIntoArguments(invocationInfo.Arguments, removeHashComments: false).ToList(); // Unfortunately for us there are a few paths that get directly read by the command line parse which we need to remap, // such as /ruleset files. So let's go through and process them now. for (var i = 0; i < splitCommandLine.Count; i++) { const string RuleSetSwitch = "/ruleset:"; if (splitCommandLine[i].StartsWith(RuleSetSwitch, StringComparison.Ordinal)) { var rulesetPath = splitCommandLine[i].Substring(RuleSetSwitch.Length); var quoted = rulesetPath.Length > 2 && rulesetPath.StartsWith("\"", StringComparison.Ordinal) && rulesetPath.EndsWith("\"", StringComparison.Ordinal); if (quoted) { rulesetPath = rulesetPath.Substring(1, rulesetPath.Length - 2); } rulesetPath = mapPath(rulesetPath); if (quoted) { rulesetPath = "\"" + rulesetPath + "\""; } splitCommandLine[i] = RuleSetSwitch + rulesetPath; } } var commandLineParserService = languageServices.GetRequiredService <ICommandLineParserService>(); var parsedCommandLine = commandLineParserService.Parse(splitCommandLine, Path.GetDirectoryName(invocationInfo.ProjectFilePath), isInteractive: false, sdkDirectory: null); var analyzerLoader = new DefaultAnalyzerAssemblyLoader(); var projectId = ProjectId.CreateNewId(invocationInfo.ProjectFilePath); var projectInfo = ProjectInfo.Create( projectId, VersionStamp.Default, name: Path.GetFileNameWithoutExtension(invocationInfo.ProjectFilePath), assemblyName: parsedCommandLine.CompilationName !, language: languageName, filePath: invocationInfo.ProjectFilePath, outputFilePath: parsedCommandLine.OutputFileName, parsedCommandLine.CompilationOptions, parsedCommandLine.ParseOptions, parsedCommandLine.SourceFiles.Select(s => CreateDocumentInfo(unmappedPath: s.Path)), metadataReferences: parsedCommandLine.MetadataReferences.Select(r => MetadataReference.CreateFromFile(mapPath(r.Reference), r.Properties)), additionalDocuments: parsedCommandLine.AdditionalFiles.Select(f => CreateDocumentInfo(unmappedPath: f.Path)), analyzerReferences: parsedCommandLine.AnalyzerReferences.Select(r => new AnalyzerFileReference(r.FilePath, analyzerLoader))) .WithAnalyzerConfigDocuments(parsedCommandLine.AnalyzerConfigPaths.Select(CreateDocumentInfo)); var solution = workspace.CurrentSolution.AddProject(projectInfo); var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); var options = GeneratorOptions.Default; return(new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, options)); // Local methods: DocumentInfo CreateDocumentInfo(string unmappedPath) { var mappedPath = mapPath(unmappedPath); return(DocumentInfo.Create( DocumentId.CreateNewId(projectId, mappedPath), name: mappedPath, filePath: mappedPath, loader: new FileTextLoader(mappedPath, parsedCommandLine.Encoding))); } }