/// <summary> /// Used to lazily calculate operations in an assembly. /// Assumes that all Types in the Assembly are for operations. /// </summary> private OperationInfo[] InitOperations() { if (Assembly == null) { return(new OperationInfo[0]); } // Parse the assembly headers to find which types are operation or function types. var logger = new QSharpLogger(null); var refs = ProjectManager.LoadReferencedAssemblies(new[] { Location }, d => logger.Log(d), ex => logger.Log(ex)); System.Diagnostics.Debug.Assert(refs.Declarations.Count == 1); var headers = refs.Declarations.Values.First(); var ops = new List <OperationInfo>(); foreach (var header in headers.Callables) { // Find the associated type. var fullName = header.QualifiedName.ToFullName(); var type = Assembly.GetType(fullName); var info = new OperationInfo(type, header); ops.Add(info); } return(ops.ToArray()); }
/// <summary> /// Used to lazily calculate operations in an assembly. /// Assumes that all Types in the Assembly are for operations. /// </summary> private OperationInfo[] InitOperations() { if (Assembly == null) { return(new OperationInfo[0]); } // Parse the assembly headers to find which types are operation or function types. var logger = new QSharpLogger(null); var refs = ProjectManager.LoadReferencedAssemblies(new[] { Location }, d => logger.Log(d), ex => logger.Log(ex), ignoreDllResources: CompilerMetadata.LoadFromCsharp); var callables = refs.SelectMany(pair => pair.Value.Callables); var ops = new List <OperationInfo>(); foreach (var callable in callables) { // Find the associated type. var fullName = callable.QualifiedName.ToFullName(); var type = Assembly.GetType(fullName); var info = new OperationInfo(type, callable); ops.Add(info); } // Makes sure Deprecated operations are pushed to the bottom so that they are resolved second: return(ops .OrderBy(op => op.Header.Attributes.Any(BuiltIn.MarksDeprecation) ? 1 : 0) .ToArray()); }
/// <summary> /// Compiles the given code. /// If the operations defined in this code are already defined /// in existing Snippets, those Snippets are skipped. /// If successful, this updates the AssemblyInfo /// with the new operations found in the Snippet. /// If errors are found during compilation, a `CompilationErrorsException` is triggered /// with the list of errors found. /// If successful, the list of snippets is updated to include those that were part of the /// compilation and it will return a new Snippet with the warnings and Q# elements /// reported by the compiler. /// </summary> public Snippet Compile(string code) { if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentNullException(nameof(code)); } var duration = Stopwatch.StartNew(); // We add exactly one line of boilerplate code at the beginning of each snippet, // so tell the logger to subtract one from all displayed line numbers. var logger = new QSharpLogger(Logger, lineNrOffset: -1); try { var snippets = SelectSnippetsToCompile(code).ToArray(); var assembly = Compiler.BuildSnippets(snippets, _metadata.Value, logger, Path.Combine(Workspace.CacheFolder, "__snippets__.dll")); if (logger.HasErrors) { throw new CompilationErrorsException(logger.Errors.ToArray()); } foreach (var entry in Compiler.IdentifyOpenedNamespaces(code)) { Compiler.AutoOpenNamespaces[entry.Key] = entry.Value; } // populate the original snippet with the results of the compilation: Snippet populate(Snippet s) => new Snippet() { id = string.IsNullOrWhiteSpace(s.id) ? Guid.NewGuid().ToString() : s.id, code = s.code, warnings = logger.Logs .Where(m => m.Source == CompilationUnitManager.GetFileId(s.Uri).Value) .Select(logger.Format) .ToArray(), Elements = assembly?.SyntaxTree? .SelectMany(ns => ns.Elements) .Where(c => c.SourceFile() == CompilationUnitManager.GetFileId(s.Uri).Value) .ToArray() }; AssemblyInfo = assembly; Items = snippets.Select(populate).ToArray(); return(Items.Last()); } finally { duration.Stop(); var status = logger.HasErrors ? "error" : "ok"; var errorIds = logger.ErrorIds.ToArray(); SnippetCompiled?.Invoke(this, new SnippetCompiledEventArgs(status, errorIds, Compiler.AutoOpenNamespaces.Keys.ToArray(), duration.Elapsed)); } }
/// <summary> /// Builds the corresponding .net core assembly from the code in the given files. /// </summary> public AssemblyInfo BuildFiles(string[] files, CompilerMetadata metadatas, QSharpLogger logger, string dllName) { var syntaxTree = BuildQsSyntaxTree(files, metadatas.QsMetadatas, logger); Uri FileUri(string f) => CompilationUnitManager.TryGetUri(NonNullable <string> .New(f), out var uri) ? uri : null; var assembly = BuildAssembly(files.Select(FileUri).ToArray(), syntaxTree, metadatas.RoslynMetadatas, logger, dllName); return(assembly); }
public async Task <ExecutionResult> RunAsync(string input, IChannel channel) { var(name, args) = ParseInput(input); var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) { throw new InvalidOperationException($"Invalid operation name: {name}"); } var snippetsWithNoOverlap = this.Snippets.Items.Where(s => s.Elements.Select(IQSharp.Extensions.ToFullName).Contains(Snippets.SNIPPETS_NAMESPACE + "." + symbol.Name)); var files = new List <Uri>(); files.Add(snippetsWithNoOverlap.FirstOrDefault().Uri); var l = snippetsWithNoOverlap.FirstOrDefault().Elements; Console.WriteLine(l.Count()); foreach (var s in l) { Console.WriteLine(s.ToFullName()); //AssemblyInfo = Compiler.BuildFiles(files, GlobalReferences.CompilerMetadata, logger, CacheDll); } var qsNamespace = new QsCompiler.SyntaxTree.QsNamespace(NonNullable <string> .New(Snippets.SNIPPETS_NAMESPACE), ImmutableArray.Create(l), null); var logger = new QSharpLogger(Logger); var newAssembly = CompilerService.BuildAssembly(files.ToArray(), new[] { qsNamespace }, GlobalReferences.CompilerMetadata.RoslynMetadatas, logger, Path.Combine(Path.GetTempPath(), "__snippets__.dll")); Assembly.LoadFrom(newAssembly.Location); symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) { throw new InvalidOperationException($"Invalid operation name: {name}"); } var aSCIICircuitizer = new ASCIICircuitizer(); var qsim = new CircuitizerSimulator(aSCIICircuitizer); qsim.DisableLogToConsole(); qsim.OnLog += channel.Stdout; var value = await symbol.Operation.RunAsync(qsim, args); var result = qsim.Render(); // TODO : currently we are ignoring the Render result from qsim and rendering a local file instead. var imageHtml = new ImageDataWrapper { imageFileName = @"C:\Users\angarg\Pictures\bad_status.PNG" }; //return imageHtml.ToExecutionResult(); return(result.ToExecutionResult()); }
/// <summary> /// Compiles the given code. /// If the operations defined in this code are already defined /// in existing Snippets, those Snippets are skipped. /// If successful, this updates the AssemblyInfo /// with the new operations found in the Snippet. /// If errors are found during compilation, a `CompilationErrorsException` is triggered /// with the list of errors found. /// If successful, the list of snippets is updated to include those that were part of the /// compilation and it will return a new Snippet with the warnings and Q# elements /// reported by the compiler. /// </summary> public Snippet Compile(string code) { if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentNullException(nameof(code)); } var duration = Stopwatch.StartNew(); var errorCodesToIgnore = new List <QsCompiler.Diagnostics.ErrorCode>() { QsCompiler.Diagnostics.ErrorCode.EntryPointInLibrary, // Ignore any @EntryPoint() attributes found in snippets. }; var logger = new QSharpLogger(Logger, errorCodesToIgnore); try { var snippets = SelectSnippetsToCompile(code).ToArray(); var assembly = Compiler.BuildSnippets(snippets, _metadata.Value, logger, Path.Combine(Workspace.CacheFolder, "__snippets__.dll")); if (logger.HasErrors) { throw new CompilationErrorsException(logger.Errors.ToArray()); } // populate the original snippet with the results of the compilation: Snippet populate(Snippet s) => new Snippet() { id = string.IsNullOrWhiteSpace(s.id) ? Guid.NewGuid().ToString() : s.id, code = s.code, warnings = logger.Logs.Where(m => m.Source == s.Uri.AbsolutePath).Select(logger.Format).ToArray(), Elements = assembly?.SyntaxTree? .SelectMany(ns => ns.Elements) .Where(c => c.SourceFile() == s.Uri.AbsolutePath) .ToArray() }; AssemblyInfo = assembly; Items = snippets.Select(populate).ToArray(); return(Items.Last()); } finally { duration.Stop(); var status = logger.HasErrors ? "error" : "ok"; var errorIds = logger.ErrorIds.ToArray(); SnippetCompiled?.Invoke(this, new SnippetCompiledEventArgs(status, errorIds, duration.Elapsed)); } }
/// <summary> /// Compiles the given code. /// If the operations defined in this code are already defined /// in existing Snippets, those Snippets are skipped. /// If successful, this updates the AssemblyInfo /// with the new operations found in the Snippet. /// If errors are found during compilation, a `CompilationErrorsException` is triggered /// with the list of errors found. /// If successful, the list of snippets is updated to include those that were part of the /// compilation and it will return a new Snippet with the warnings and Q# elements /// reported by the compiler. /// </summary> public Snippet Compile(string code) { if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentNullException(nameof(code)); } var duration = Stopwatch.StartNew(); var logger = new QSharpLogger(Logger); try { var snippets = SelectSnippetsToCompile(code).ToArray(); var references = Workspace.HasErrors ? GlobalReferences.CompilerMetadata : GlobalReferences?.CompilerMetadata.WithAssemblies(Workspace.AssemblyInfo); var assembly = Compiler.BuildSnippets(snippets, references, logger, Path.Combine(Workspace.CacheFolder, "__snippets__.dll")); if (logger.HasErrors) { throw new CompilationErrorsException(logger.Errors.ToArray()); } // populate the original snippet with the results of the compilation: Snippet populate(Snippet s) => new Snippet() { id = string.IsNullOrWhiteSpace(s.id) ? System.Guid.NewGuid().ToString() : s.id, code = s.code, warnings = logger.Logs.Where(m => m.Source == s.Uri.AbsolutePath).Select(logger.Format).ToArray(), Elements = assembly?.SyntaxTree? .SelectMany(ns => ns.Elements) .Where(c => c.SourceFile() == s.Uri.AbsolutePath) .ToArray() }; AssemblyInfo = assembly; Items = snippets.Select(populate).ToArray(); return(Items.Last()); } finally { duration.Stop(); var status = logger.HasErrors ? "error" : "ok"; var errorIds = logger.ErrorIds.ToArray(); SnippetCompiled?.Invoke(this, new SnippetCompiledEventArgs(status, errorIds, duration.Elapsed)); } }
/// <summary> /// Compiles the given Q# code and returns the list of elements found in it. /// The compiler does this on a best effort, so it will return the elements even if the compilation fails. /// </summary> public IEnumerable <QsCompiler.SyntaxTree.QsNamespaceElement> IdentifyElements(string source) { var ns = NonNullable <string> .New(Snippets.SNIPPETS_NAMESPACE); var logger = new QSharpLogger(null); var sources = new Dictionary <Uri, string>() { { new Uri($"file:///temp"), $"namespace {ns.Value} {{ {source} }}" } }.ToImmutableDictionary(); var references = QsReferences.Empty; var loadOptions = new QsCompiler.CompilationLoader.Configuration(); // do not generate functor support var loaded = new QsCompiler.CompilationLoader(_ => sources, _ => references, loadOptions, logger); return(loaded.VerifiedCompilation?.SyntaxTree[ns].Elements); }
/// <summary> /// Builds the corresponding .net core assembly from the code in the given files. /// </summary> public AssemblyInfo BuildFiles(string[] files, CompilerMetadata metadatas, QSharpLogger logger, string dllName) { var sources = ProjectManager.LoadSourceFiles(files, d => logger?.Log(d), ex => logger?.Log(ex)); return(BuildAssembly(sources, metadatas, logger, dllName)); }
/// <summary> /// Builds the Q# syntax tree from the given files/source paris. /// </summary> private static QsCompiler.SyntaxTree.QsNamespace[] BuildQsSyntaxTree(ImmutableDictionary <Uri, string> sources, QsReferences references, QSharpLogger logger) { var loadOptions = new QsCompiler.CompilationLoader.Configuration { GenerateFunctorSupport = true }; var loaded = new QsCompiler.CompilationLoader(_ => sources, _ => references, loadOptions, logger); return(loaded.GeneratedSyntaxTree?.ToArray()); }
/// <summary> /// Builds the Q# syntax tree from the given files. /// The files are given as a list of filenames, as per the format expected by /// the <see cref="Microsoft.Quantum.QsCompiler.CompilationBuilder.ProjectManager.LoadSourceFiles(IEnumerable{string}, Action{VisualStudio.LanguageServer.Protocol.Diagnostic}, Action{Exception})" /> /// method. /// </summary> private static QsCompiler.SyntaxTree.QsNamespace[] BuildQsSyntaxTree(string[] files, QsReferences references, QSharpLogger logger) { var sources = ProjectManager.LoadSourceFiles(files, d => logger?.Log(d), ex => logger?.Log(ex)); return(BuildQsSyntaxTree(sources, references, logger)); }
/// <summary> /// Builds the corresponding .net core assembly from the code in the given Q# Snippets. /// Each snippet code is wrapped inside the 'SNIPPETS_NAMESPACE' namespace and processed as a file /// with the same name as the snippet id. /// </summary> public AssemblyInfo BuildSnippets(Snippet[] snippets, CompilerMetadata metadatas, QSharpLogger logger, string dllName) { string WrapInNamespace(Snippet s) => $"namespace {Snippets.SNIPPETS_NAMESPACE} {{ open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; {s.code} }}"; var sources = snippets.ToDictionary(s => s.Uri, WrapInNamespace); var syntaxTree = BuildQsSyntaxTree(sources.ToImmutableDictionary(), metadatas.QsMetadatas, logger); var assembly = BuildAssembly(sources.Keys.ToArray(), syntaxTree, metadatas.RoslynMetadatas, logger, dllName); return(assembly); }
/// <summary> /// Builds the corresponding .net core assembly from the Q# syntax tree. /// </summary> private static AssemblyInfo BuildAssembly(Uri[] fileNames, QsCompiler.SyntaxTree.QsNamespace[] syntaxTree, IEnumerable <MetadataReference> references, QSharpLogger logger, string targetDll) { if (logger.HasErrors) { return(null); } logger.LogDebug($"Compiling the following Q# files: {string.Join(",", fileNames.Select(f => f.LocalPath))}"); try { // Generate C# simulation code from Q# syntax tree and convert it into C# syntax trees: var trees = new List <SyntaxTree>(); NonNullable <string> GetFileId(Uri uri) => CompilationUnitManager.TryGetFileId(uri, out var id) ? id : NonNullable <string> .New(uri.AbsolutePath); foreach (var file in fileNames) { var sourceFile = GetFileId(file); var code = SimulationCode.generate(sourceFile, syntaxTree); var tree = CSharpSyntaxTree.ParseText(code, encoding: UTF8Encoding.UTF8); trees.Add(tree); logger.LogDebug($"Generated the following C# code for {sourceFile.Value}:\n=============\n{code}\n=============\n"); } // Compile the C# syntax trees: var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug); var compilation = CSharpCompilation.Create( Path.GetFileNameWithoutExtension(targetDll), trees, references, options); // Generate the assembly from the C# compilation: using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (!result.Success) { IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); logger.LogError("IQS000", "Could not compile Roslyn dll from working folder."); foreach (Diagnostic diagnostic in failures) { logger.LogError(diagnostic.Id, diagnostic.GetMessage()); } return(null); } else { logger.LogDebug($"Assembly successfully generated. Caching at {targetDll}."); var data = ms.ToArray(); try { File.WriteAllBytes(targetDll, data); } catch (Exception e) { logger.LogError("IQS001", $"Unable to save assembly cache: {e.Message}."); } return(new AssemblyInfo(Assembly.Load(data), targetDll, syntaxTree)); } } } catch (Exception e) { logger.LogError("IQS002", $"Unexpected error compiling assembly: {e.Message}."); return(null); } }
public AssemblyInfo?BuildEntryPoint(OperationInfo operation, CompilerMetadata metadatas, QSharpLogger logger, string dllName, string?executionTarget = null, AssemblyConstants.RuntimeCapabilities runtimeCapabilities = AssemblyConstants.RuntimeCapabilities.Unknown) { var signature = operation.Header.PrintSignature(); var argumentTuple = SyntaxTreeToQsharp.ArgumentTuple(operation.Header.ArgumentTuple, type => type.ToString(), symbolsOnly: true); var entryPointUri = new Uri(Path.GetFullPath(Path.Combine("/", $"entrypoint.qs"))); var entryPointSnippet = @$ "namespace ENTRYPOINT {{ open {operation.Header.QualifiedName.Namespace.Value};
/// <summary> /// Builds the corresponding .net core assembly from the Q# syntax tree. /// </summary> private AssemblyInfo BuildAssembly(ImmutableDictionary <Uri, string> sources, CompilerMetadata metadata, QSharpLogger logger, string dllName) { logger.LogDebug($"Compiling the following Q# files: {string.Join(",", sources.Keys.Select(f => f.LocalPath))}"); var qsCompilation = this.UpdateCompilation(sources, metadata.QsMetadatas, logger); if (logger.HasErrors) { return(null); } try { // Generate C# simulation code from Q# syntax tree and convert it into C# syntax trees: var trees = new List <SyntaxTree>(); NonNullable <string> GetFileId(Uri uri) => CompilationUnitManager.TryGetFileId(uri, out var id) ? id : NonNullable <string> .New(uri.AbsolutePath); foreach (var file in sources.Keys) { var sourceFile = GetFileId(file); var code = SimulationCode.generate(sourceFile, CodegenContext.Create(qsCompilation.Namespaces)); var tree = CSharpSyntaxTree.ParseText(code, encoding: UTF8Encoding.UTF8); trees.Add(tree); logger.LogDebug($"Generated the following C# code for {sourceFile.Value}:\n=============\n{code}\n=============\n"); } // Compile the C# syntax trees: var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug); var compilation = CSharpCompilation.Create( Path.GetFileNameWithoutExtension(dllName), trees, metadata.RoslynMetadatas, options); // Generate the assembly from the C# compilation: using (var ms = new MemoryStream()) using (var bsonStream = new MemoryStream()) { using var writer = new BsonDataWriter(bsonStream) { CloseOutput = false }; var fromSources = qsCompilation.Namespaces.Select(ns => FilterBySourceFile.Apply(ns, s => s.Value.EndsWith(".qs"))); Json.Serializer.Serialize(writer, new QsCompilation(fromSources.ToImmutableArray(), qsCompilation.EntryPoints)); var resourceDescription = new ResourceDescription ( resourceName: QsCompiler.ReservedKeywords.DotnetCoreDll.ResourceName, dataProvider: () => new MemoryStream(bsonStream.ToArray()), isPublic: true ); var result = compilation.Emit(ms, manifestResources: new[] { resourceDescription }); if (!result.Success) { IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); logger.LogError("IQS000", "Could not compile Roslyn dll from working folder."); foreach (Diagnostic diagnostic in failures) { logger.LogError(diagnostic.Id, diagnostic.GetMessage()); } return(null); } else { logger.LogDebug($"Assembly successfully generated. Caching at {dllName}."); var data = ms.ToArray(); try { File.WriteAllBytes(dllName, data); } catch (Exception e) { logger.LogError("IQS001", $"Unable to save assembly cache: {e.Message}."); } return(new AssemblyInfo(Assembly.Load(data), dllName, fromSources.ToArray())); } } } catch (Exception e) { logger.LogError("IQS002", $"Unexpected error compiling assembly: {e.Message}."); return(null); } }
/// <summary> /// Builds the corresponding .net core assembly from the code in the given Q# Snippets. /// Each snippet code is wrapped inside the 'SNIPPETS_NAMESPACE' namespace and processed as a file /// with the same name as the snippet id. /// </summary> public AssemblyInfo BuildSnippets(Snippet[] snippets, CompilerMetadata metadatas, QSharpLogger logger, string dllName) { string WrapInNamespace(Snippet s) => $"namespace {Snippets.SNIPPETS_NAMESPACE} {{ open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; {s.code} }}"; var sources = snippets.ToImmutableDictionary(s => s.Uri, WrapInNamespace); return(BuildAssembly(sources, metadatas, logger, dllName)); }
/// <summary> /// Compiles the given Q# code and returns the list of elements found in it. /// Removes all currently tracked source files in the CompilationManager and replaces them with the given ones. /// The compiler does this on a best effort, so it will return the elements even if the compilation fails. /// If the given references are not null, reloads the references loaded in by the CompilationManager /// if the keys of the given references differ from the currently loaded ones. /// Returns an enumerable of all namespaces, including the content from both source files and references. /// </summary> private QsCompilation UpdateCompilation(ImmutableDictionary <Uri, string> sources, QsReferences references = null, QSharpLogger logger = null) { var loadOptions = new CompilationLoader.Configuration { GenerateFunctorSupport = true, }; var loaded = new CompilationLoader(_ => sources, _ => references, loadOptions, logger); return(loaded.CompilationOutput); }
public IEntryPoint Generate(string operationName, string?executionTarget, RuntimeCapability?runtimeCapability = null) { Logger?.LogDebug($"Generating entry point: operationName={operationName}, executionTarget={executionTarget}"); var logger = new QSharpLogger(Logger); var compilerMetadata = References.CompilerMetadata; // Clear references to previously-built assemblies WorkspaceAssemblies = Array.Empty <AssemblyInfo>(); SnippetsAssemblyInfo = null; EntryPointAssemblyInfo = null; // Compile the workspace against the provided execution target var workspaceFiles = Workspace.SourceFiles.ToArray(); if (workspaceFiles.Any()) { Logger?.LogDebug($"{workspaceFiles.Length} files found in workspace. Compiling."); var workspaceAssemblies = new List <AssemblyInfo>(); foreach (var project in Workspace.Projects.Where(p => p.SourceFiles.Any())) { try { workspaceAssemblies.Add(Compiler.BuildFiles( project.SourceFiles.ToArray(), compilerMetadata.WithAssemblies(workspaceAssemblies.ToArray()), logger, Path.Combine(Workspace.CacheFolder, $"__entrypoint{project.CacheDllName}"), executionTarget, runtimeCapability)); } catch (Exception e) { logger.LogError( "IQS004", $"Error compiling project {project.ProjectFile} for execution target {executionTarget}: {e.Message}"); } } if (!workspaceAssemblies.Any() || logger.HasErrors) { Logger?.LogError($"Error compiling workspace."); throw new CompilationErrorsException(logger.Errors.ToArray()); } WorkspaceAssemblies = workspaceAssemblies.ToArray(); compilerMetadata = compilerMetadata.WithAssemblies(WorkspaceAssemblies); } // Compile the snippets against the provided execution target var snippets = Snippets.Items.ToArray(); if (snippets.Any()) { Logger?.LogDebug($"{snippets.Length} items found in snippets. Compiling."); SnippetsAssemblyInfo = Compiler.BuildSnippets( snippets, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__snippets__.dll"), executionTarget, runtimeCapability); if (SnippetsAssemblyInfo == null || logger.HasErrors) { Logger?.LogError($"Error compiling snippets."); throw new CompilationErrorsException(logger.Errors.ToArray()); } compilerMetadata = compilerMetadata.WithAssemblies(SnippetsAssemblyInfo); } // Build the entry point assembly var operationInfo = new EntryPointOperationResolver(this).Resolve(operationName); if (operationInfo == null) { Logger?.LogError($"{operationName} is not a recognized Q# operation name."); throw new UnsupportedOperationException(operationName); } EntryPointAssemblyInfo = Compiler.BuildEntryPoint( operationInfo, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__.dll"), executionTarget, runtimeCapability); if (EntryPointAssemblyInfo == null || logger.HasErrors) { Logger?.LogError($"Error compiling entry point for operation {operationName}."); throw new CompilationErrorsException(logger.Errors.ToArray()); } var entryPointOperationInfo = EntryPointAssemblyInfo.Operations.Single(); // Construct the EntryPointInfo<,> object var parameterTypes = entryPointOperationInfo.RoslynParameters.Select(p => p.ParameterType).ToArray(); var typeCount = parameterTypes.Length; Type entryPointInputType = typeCount switch { 0 => typeof(QVoid), 1 => parameterTypes.Single(), _ => PartialMapper.TupleTypes[typeCount].MakeGenericType(parameterTypes) }; Type entryPointOutputType = entryPointOperationInfo.ReturnType; Type entryPointInfoType = typeof(EntryPointInfo <,>).MakeGenericType(new Type[] { entryPointInputType, entryPointOutputType }); var entryPointInfo = entryPointInfoType.GetConstructor(new Type[] { typeof(Type) }) .Invoke(new object[] { entryPointOperationInfo.RoslynType }); return(new EntryPoint(entryPointInfo, entryPointInputType, entryPointOutputType, entryPointOperationInfo)); } }
public IEntryPoint Generate(string operationName, string?executionTarget, RuntimeCapability?runtimeCapability = null) { Logger?.LogDebug($"Generating entry point: operationName={operationName}, executionTarget={executionTarget}"); var logger = new QSharpLogger(Logger); var compilerMetadata = References.CompilerMetadata; // Clear references to previously-built assemblies WorkspaceAssemblies = Array.Empty <AssemblyInfo>(); SnippetsAssemblyInfo = null; EntryPointAssemblyInfo = null; // Compile the workspace against the provided execution target var workspaceFiles = Workspace.SourceFiles.ToArray(); if (workspaceFiles.Any()) { Logger?.LogDebug($"{workspaceFiles.Length} files found in workspace. Compiling."); var workspaceAssemblies = new List <AssemblyInfo>(); foreach (var project in Workspace.Projects.Where(p => p.SourceFiles.Any())) { try { workspaceAssemblies.Add(Compiler.BuildFiles( project.SourceFiles.ToArray(), compilerMetadata.WithAssemblies(workspaceAssemblies.ToArray()), logger, Path.Combine(Workspace.CacheFolder, $"__entrypoint{project.CacheDllName}"))); } catch (Exception e) { logger.LogError( "IQS004", $"Error compiling project {project.ProjectFile} for execution target {executionTarget}: {e.Message}"); } } if (!workspaceAssemblies.Any() || logger.HasErrors) { Logger?.LogError($"Error compiling workspace."); throw new CompilationErrorsException(logger); } WorkspaceAssemblies = workspaceAssemblies.ToArray(); compilerMetadata = compilerMetadata.WithAssemblies(WorkspaceAssemblies); } // Compile the snippets against the provided execution target var snippets = Snippets.Items.ToArray(); if (snippets.Any()) { Logger?.LogDebug($"{snippets.Length} items found in snippets. Compiling."); SnippetsAssemblyInfo = Compiler.BuildSnippets( snippets, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__snippets__.dll")); if (SnippetsAssemblyInfo == null || logger.HasErrors) { Logger?.LogError($"Error compiling snippets."); throw new CompilationErrorsException(logger); } compilerMetadata = compilerMetadata.WithAssemblies(SnippetsAssemblyInfo); } // Build the entry point assembly var operationInfo = new EntryPointOperationResolver(this).Resolve(operationName); if (operationInfo == null) { Logger?.LogError($"{operationName} is not a recognized Q# operation name."); throw new UnsupportedOperationException(operationName); } EntryPointAssemblyInfo = Compiler.BuildEntryPoint( operationInfo, compilerMetadata, logger, Path.Combine(Workspace.CacheFolder, "__entrypoint__.dll"), executionTarget, runtimeCapability); if (EntryPointAssemblyInfo == null || logger.HasErrors) { Logger?.LogError($"Error compiling entry point for operation {operationName}."); throw new CompilationErrorsException(logger); } if (EntryPointAssemblyInfo.Operations.Count() <= 1) { // Entry point assembly contained zero or one operations; this // may indicate that C# code is not being correctly // regenerated. At least two operations (the entry point and // the operation called from the entry point) are expected. Logger?.LogWarning( "Internal error compiling entry point for operation {OperationName}; entry point assembly did not contain the right number of operations. This should never happen, and most likely indicates a bug in IQ#. ", operationName ); } var entryPointOperations = EntryPointAssemblyInfo .Operations .Where(op => op.Header.Attributes.Any( attr => { var qName = attr.TypeId.ValueOr(null); return(qName != null && qName.Name == "EntryPoint" && qName.Namespace == "Microsoft.Quantum.Core"); } )); var entryPointOperationInfo = entryPointOperations .SingleOrDefault(); if (entryPointOperationInfo == null) { throw new Exception($"Entry point assembly contained {entryPointOperations.Count()}, but expected 1."); } // Construct the EntryPointInfo<,> object var parameterTypes = entryPointOperationInfo.RoslynParameters.Select(p => p.ParameterType).ToArray(); var typeCount = parameterTypes.Length; Type entryPointInputType = typeCount switch { 0 => typeof(QVoid), 1 => parameterTypes.Single(), _ => PartialMapper.TupleTypes[typeCount].MakeGenericType(parameterTypes) }; Type entryPointOutputType = entryPointOperationInfo.ReturnType; Type entryPointInfoType = typeof(EntryPointInfo <,>).MakeGenericType(new Type[] { entryPointInputType, entryPointOutputType }); var entryPointInfo = entryPointInfoType.GetConstructor(new Type[] { typeof(Type) }) .Invoke(new object[] { entryPointOperationInfo.RoslynType }); return(new EntryPoint(entryPointInfo, entryPointInputType, entryPointOutputType, entryPointOperationInfo)); } }