/// <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 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)); } }
/// <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); } }
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)); } }