/// <inheritdoc/> public bool Transformation(QsCompilation compilation, out QsCompilation transformed) { // random "diagnostic" to check if diagnostics loading works this.GeneratedDiagnostics = new List <IRewriteStep.Diagnostic>() { new IRewriteStep.Diagnostic { Severity = CodeAnalysis.DiagnosticSeverity.Info, Message = "Invokation of the Q# compiler extension for C# generation to demonstrate execution on the simulation framework.", } }; var success = true; var outputFolder = this.AssemblyConstants.TryGetValue(ReservedKeywords.AssemblyConstants.OutputPath, out var path) ? path : null; var allSources = GetSourceFiles.Apply(compilation.Namespaces) // also generate the code for referenced libraries... // ... except when they are one of the packages that currently still already contains the C# code (temporary workaround): .Where(s => !Path.GetFileName(s).StartsWith("Microsoft.Quantum")); foreach (var source in allSources) { var content = SimulationCode.generate(source, CodegenContext.Create(compilation.Namespaces)); try { CompilationLoader.GeneratedFile(source, outputFolder ?? this.Name, ".g.cs", content); } catch { success = false; } } transformed = compilation; return(success); }
public bool Transformation(QsCompilation compilation, out QsCompilation transformed) { var context = CodegenContext.Create(compilation, _assemblyConstants); var sources = GetSourceFiles.Apply(compilation.Namespaces); foreach (var source in sources.Where(s => !s.Value.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))) { var content = SimulationCode.generate(source, context); GeneratedFiles.Add(source.Value, content); } if (!compilation.EntryPoints.IsEmpty) { var callable = context.allCallables.First(c => c.Key == compilation.EntryPoints.First()).Value; var content = EntryPoint.generate(context, callable); NonNullable <string> entryPointName = NonNullable <string> .New(callable.SourceFile.Value + ".EntryPoint"); GeneratedFiles.Add(entryPointName.Value, content); } transformed = compilation; return(true); }
/// <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 bool Transformation(QsCompilation compilation, [NotNullWhen(true)] out QsCompilation?transformed) { // we do not change the Q# syntax tree transformed = compilation; // global callables var globalCallables = compilation.Namespaces.GlobalCallableResolutions(); // collect all callables that have an substitution attribute var globals = globalCallables.Where(p => p.Value.Source.CodeFile.EndsWith(".qs")) .Where(p => p.Value.Attributes.Any(HasSubstitutionAttribute)); if (!globals.Any()) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Info, Message = "AutoSubstitution: no operations have @SubstitutableOnTarget attribute", Stage = IRewriteStep.Stage.Transformation }); return(true); } // no need to generate any C# file, if there is no substitution attribute, or if we cannot retrieve the output path if (!AssemblyConstants.TryGetValue(Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants.OutputPath, out var outputPath)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = "AutoSubstitution: cannot determine output path for generated C# code", Stage = IRewriteStep.Stage.Transformation }); return(false); } diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Info, Message = $"AutoSubstitution: Generating file __AutoSubstitution__.g.cs in {outputPath}", Stage = IRewriteStep.Stage.Transformation }); using var writer = new StreamWriter(Path.Combine(outputPath, "__AutoSubstitution__.g.cs")); var context = CodegenContext.Create(compilation, AssemblyConstants); var generator = new CodeGenerator(context); foreach (var(key, callable) in globals) { var attributeArguments = callable.Attributes.Where(HasSubstitutionAttribute).Select(GetSubstitutionAttributeArguments); foreach (var(alternativeOperation, _) in attributeArguments) { var period = alternativeOperation.LastIndexOf('.'); if (period == -1) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: name of alternative operation in {key.Namespace}.{key.Name} must be completely specified (including namespace)", Stage = IRewriteStep.Stage.Transformation }); return(false); } var qualifiedName = new QsQualifiedName(alternativeOperation.Substring(0, period), alternativeOperation.Substring(period + 1)); if (!globalCallables.TryGetValue(qualifiedName, out var alternativeCallable)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: cannot find alternative operation `{alternativeOperation}`", Stage = IRewriteStep.Stage.Transformation }); return(false); } var callableSignature = callable.Signature; var alternativeSignature = alternativeCallable.Signature; if (!callableSignature.ArgumentType.Equals(alternativeSignature.ArgumentType) || !callableSignature.ReturnType.Equals(alternativeSignature.ReturnType)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: signature of `{alternativeOperation}` does not match the one of {key.Namespace}.{key.Name}", Stage = IRewriteStep.Stage.Transformation }); return(false); } if (!GetSpecializationKinds(callable).IsSubsetOf(GetSpecializationKinds(alternativeCallable))) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: specializations of `{alternativeOperation}` must be a superset of specializations of {key.Namespace}.{key.Name}", Stage = IRewriteStep.Stage.Transformation }); return(false); } } generator.AddCallable(key, callable, attributeArguments); } generator.WriteTo(writer); return(true); }