/// <summary> /// Builds the compilation for the Q# code or Q# snippet and referenced assemblies defined by the given options. /// Generates formatted Q# code for each source file in the compilation. /// Returns a suitable error code if some of the source files or references could not be found or loaded, or if the Q# generation failed. /// Compilation errors are not reflected in the return code, but are logged using the given logger. /// Throws an ArgumentNullException if any of the given arguments is null. /// </summary> public static int Run(FormatOptions options, ConsoleLogger logger) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } ImmutableDictionary <Uri, string> LoadSources(SourceFileLoader loadFromDisk) => options.LoadSourcesOrSnippet(logger)(loadFromDisk) .ToImmutableDictionary(entry => entry.Key, entry => UpdateArrayLiterals(entry.Value)); // manually replace array literals var loaded = new CompilationLoader(LoadSources, options.References, logger: logger); // no rewrite steps, no generation if (ReturnCode.Status(loaded) == ReturnCode.UNRESOLVED_FILES) { return(ReturnCode.UNRESOLVED_FILES); // ignore compilation errors } // TODO: a lot of the formatting logic defined here and also in the routines above // is supposed to move into the compilation manager in order to be available for the language server to provide formatting var success = true; foreach (var file in loaded.VerifiedCompilation.SourceFiles) { var verbosity = logger.Verbosity; if (Options.IsCodeSnippet(file) && logger.Verbosity < DiagnosticSeverity.Information) { logger.Verbosity = DiagnosticSeverity.Information; } if (!GenerateFormattedQsFile(loaded.VerifiedCompilation, file, options.OutputFolder, logger)) { success = false; } logger.Verbosity = verbosity; } return(success ? ReturnCode.SUCCESS : ReturnCode.CODE_GENERATION_ERRORS); }
private QsCompilation UpdateCompilation( ImmutableDictionary <Uri, string> sources, QsReferences?references = null, QSharpLogger?logger = null, bool compileAsExecutable = false, string?executionTarget = null, AssemblyConstants.RuntimeCapabilities runtimeCapabilities = AssemblyConstants.RuntimeCapabilities.Unknown) { var loadOptions = new CompilationLoader.Configuration { GenerateFunctorSupport = true, IsExecutable = compileAsExecutable, AssemblyConstants = new Dictionary <string, string> { [AssemblyConstants.ProcessorArchitecture] = executionTarget ?? string.Empty }, RuntimeCapabilities = runtimeCapabilities }; var loaded = new CompilationLoader(_ => sources, _ => references, loadOptions, logger); return(loaded.CompilationOutput); }
private QsCompilation?UpdateCompilation( ImmutableDictionary <Uri, string> sources, QsReferences references, QSharpLogger?logger = null, bool compileAsExecutable = false, string?executionTarget = null, RuntimeCapability?runtimeCapability = null) { var loadOptions = new CompilationLoader.Configuration { GenerateFunctorSupport = true, LoadReferencesBasedOnGeneratedCsharp = string.IsNullOrEmpty(executionTarget), // deserialization of resources in references is only needed if there is an execution target IsExecutable = compileAsExecutable, AssemblyConstants = new Dictionary <string, string> { [AssemblyConstants.ProcessorArchitecture] = executionTarget ?? string.Empty }, RuntimeCapability = runtimeCapability ?? RuntimeCapability.FullComputation }; var loaded = new CompilationLoader(_ => sources, _ => references, loadOptions, logger); return(loaded.CompilationOutput); }
// publicly accessible routines /// <summary> /// Builds the compilation for the Q# code or Q# snippet and referenced assemblies defined by the given options. /// Returns a suitable error code if one of the compilation or generation steps fails. /// Throws an ArgumentNullException if any of the given arguments is null. /// </summary> public static int Run(BuildOptions options, ConsoleLogger logger) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (options?.ResponseFiles != null && options.ResponseFiles.Any()) { options = FromResponseFiles(options.ResponseFiles); } if (options == null) { return(ReturnCode.INVALID_ARGUMENTS); } var usesPlugins = options.Plugins != null && options.Plugins.Any(); var loadOptions = new CompilationLoader.Configuration { ProjectName = options.ProjectName, GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, AttemptFullPreEvaluation = options.TrimLevel > 1, DocumentationOutputFolder = options.DocFolder, BuildOutputFolder = options.OutputFolder ?? (usesPlugins ? "." : null), DllOutputPath = options.EmitDll ? " " : null, // set to e.g. an empty space to generate the dll in the same location as the .bson file RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray <(string, string)> .Empty, EnableAdditionalChecks = false // todo: enable debug mode? }; var loaded = new CompilationLoader(options.LoadSourcesOrSnippet(logger), options.References, loadOptions, logger); return(ReturnCode.Status(loaded)); } }
public int OnExecute(CommandLineApplication app, IConsole console) { console.WriteLine(); console.WriteLine("Smart Contract Builder"); console.WriteLine(); if (File.Exists(this.OutputPath)) { console.WriteLine($"Output file already exists!"); return(1); } ContractCompilationResult result = CompilationLoader.CompileFromFileOrDirectoryName(this.InputFile, console); // Check if the file was found. if (result == null) { return(1); } ValidationServiceResult validationResult = ValidatorService.Validate(this.InputFile, result, console, this.Params); if (!validationResult.Success) { return(1); } else { console.WriteLine("Validation passed!"); } this.WriteDll(validationResult.CompilationResult.Compilation); console.WriteLine($"File {this.OutputPath} written."); return(1); }
// publicly accessible routines /// <summary> /// Builds the compilation for the Q# code or Q# snippet and referenced assemblies defined by the given options. /// Returns a suitable error code if one of the compilation or generation steps fails. /// Throws an ArgumentNullException if any of the given arguments is null. /// </summary> public static int Run(BuildOptions options, ConsoleLogger logger) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (!BuildOptions.IncorporateResponseFiles(options, out options)) { logger.Log(ErrorCode.InvalidCommandLineArgsInResponseFiles, Array.Empty <string>()); return(ReturnCode.INVALID_ARGUMENTS); } var usesPlugins = options.Plugins != null && options.Plugins.Any(); if (!options.ParseAssemblyProperties(out var assemblyConstants)) { logger.Log(WarningCode.InvalidAssemblyProperties, Array.Empty <string>()); } var loadOptions = new CompilationLoader.Configuration { ProjectName = options.ProjectName, AssemblyConstants = assemblyConstants, TargetPackageAssemblies = options.TargetSpecificDecompositions, RuntimeCapabilities = options.RuntimeCapabilites, SkipMonomorphization = options.RuntimeCapabilites == RuntimeCapabilities.Unknown, GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, AttemptFullPreEvaluation = options.TrimLevel > 2, DocumentationOutputFolder = options.DocFolder, BuildOutputFolder = options.OutputFolder ?? (usesPlugins ? "." : null), DllOutputPath = options.EmitDll ? " " : null, // set to e.g. an empty space to generate the dll in the same location as the .bson file IsExecutable = options.MakeExecutable, RewriteSteps = options.Plugins?.Select(step => (step, (string)null)) ?? ImmutableArray <(string, string)> .Empty, EnableAdditionalChecks = false, // todo: enable debug mode? ExposeReferencesViaTestNames = options.ExposeReferencesViaTestNames }; if (options.PerfFolder != null) { CompilationLoader.CompilationTaskEvent += CompilationTracker.OnCompilationTaskEvent; } var loaded = new CompilationLoader(options.LoadSourcesOrSnippet(logger), options.References, loadOptions, logger); if (options.PerfFolder != null) { try { CompilationTracker.PublishResults(options.PerfFolder); } catch (Exception ex) { logger.Log(ErrorCode.PublishingPerfResultsFailed, new string[] { options.PerfFolder }); logger.Log(ex); } } return(ReturnCode.Status(loaded)); } }
// publicly accessible routines /// <summary> /// Builds the compilation for the Q# code or Q# snippet and referenced assemblies defined by the given options. /// Invokes all specified targets (dotnet core apps) with suitable TargetOptions, /// that in particular specify the path to the compiled binary as input and the same output folder, verbosity, and suppressed warnings as the given options. /// The output folder is set to the current directory if one or more targets have been specified but the output folder was left unspecified. /// Returns a suitable error code if one of the compilation or generation steps fails. /// </summary> /// <exception cref="ArgumentNullException">If any of the given arguments is null.</exception> /// </summary> public static int Run(BuildOptions options, ConsoleLogger logger) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } CompilationLoader.BuildTarget DefineTarget(string exeName) => (binary, onException) => { var targetOpts = new TargetOptions { Input = new[] { binary }, OutputFolder = Path.GetFullPath(options.OutputFolder ?? "."), // GetFullPath is needed for the output folder to be relative to the current folder! Verbose = options.Verbose, NoWarn = options.NoWarn, }; var pathToExe = Path.GetFullPath(exeName); var commandLineArgs = $"{pathToExe} {Parser.Default.FormatCommandLine(targetOpts)}"; var success = ProcessRunner.Run("dotnet", commandLineArgs, out var output, out var error, out var exitCode, out var ex, timeout: 30000); if (ex != null) { onException?.Invoke(ex); } if (exitCode != 0) { logger.Log(WarningCode.TargetExitedAbnormally, new[] { exeName, exitCode.ToString() }, pathToExe); } var(outStr, errStr) = (output.ToString(), error.ToString()); if (!String.IsNullOrWhiteSpace(outStr)) { logger.Log(InformationCode.BuildTargetOutput, Enumerable.Empty <string>(), pathToExe, messageParam: outStr); } if (!String.IsNullOrWhiteSpace(errStr)) { logger.Log(InformationCode.BuildTargetError, Enumerable.Empty <string>(), pathToExe, messageParam: errStr); } return(success); }; var specifiesTargets = options.Targets != null && options.Targets.Any(); var loadOptions = new CompilationLoader.Configuration { ProjectName = options.ProjectName, GenerateFunctorSupport = true, SkipSyntaxTreeTrimming = options.TrimLevel == 0, AttemptFullPreEvaluation = options.TrimLevel > 1, DocumentationOutputFolder = options.DocFolder, BuildOutputFolder = options.OutputFolder ?? (specifiesTargets ? "." : null), DllOutputPath = " ", // generating the dll in the same location as the .bson file Targets = options.Targets.ToImmutableDictionary(id => id, DefineTarget) }; var loaded = new CompilationLoader(options.LoadSourcesOrSnippet(logger), options.References, loadOptions, logger); return(ReturnCode.Status(loaded)); }
private int OnExecute(CommandLineApplication app, IConsole console) { if (!this.InputFiles.Any()) { app.ShowHelp(); return(1); } Console.WriteLine(); Console.WriteLine("Smart Contract Validator"); Console.WriteLine(); var determinismValidator = new SctDeterminismValidator(); var formatValidator = new SmartContractFormatValidator(); var warningValidator = new SmartContractWarningValidator(); var reportData = new List <ValidationReportData>(); foreach (string file in this.InputFiles) { var validationData = new ValidationReportData { FileName = file, CompilationErrors = new List <CompilationError>(), DeterminismValidationErrors = new List <ValidationResult>(), FormatValidationErrors = new List <ValidationError>(), Warnings = new List <Warning>() }; reportData.Add(validationData); ContractCompilationResult compilationResult = CompilationLoader.CompileFromFileOrDirectoryName(file, console); // Check if the file was found. if (compilationResult == null) { return(1); } validationData.CompilationSuccess = compilationResult.Success; if (!compilationResult.Success) { Console.WriteLine("Compilation failed!"); Console.WriteLine(); validationData.CompilationErrors .AddRange(compilationResult .Diagnostics .Select(d => new CompilationError { Message = d.ToString() })); continue; } validationData.CompilationBytes = compilationResult.Compilation; Console.WriteLine($"Compilation OK"); Console.WriteLine(); byte[] compilation = compilationResult.Compilation; Console.WriteLine("Building ModuleDefinition"); IContractModuleDefinition moduleDefinition = ContractDecompiler.GetModuleDefinition(compilation, new DotNetCoreAssemblyResolver()).Value; Console.WriteLine("ModuleDefinition built successfully"); Console.WriteLine(); Console.WriteLine($"Validating file {file}..."); Console.WriteLine(); SmartContractValidationResult formatValidationResult = formatValidator.Validate(moduleDefinition.ModuleDefinition); validationData.FormatValid = formatValidationResult.IsValid; validationData .FormatValidationErrors .AddRange(formatValidationResult .Errors .Select(e => new ValidationError { Message = e.Message })); SmartContractValidationResult determinismValidationResult = determinismValidator.Validate(moduleDefinition); validationData.DeterminismValid = determinismValidationResult.IsValid; validationData .DeterminismValidationErrors .AddRange(determinismValidationResult.Errors); SmartContractValidationResult warningResult = warningValidator.Validate(moduleDefinition.ModuleDefinition); validationData .Warnings .AddRange(warningResult .Errors .Select(e => new Warning { Message = e.Message })); } List <IReportSection> reportStructure = new List <IReportSection>(); reportStructure.Add(new HeaderSection()); reportStructure.Add(new CompilationSection()); reportStructure.Add(new FormatSection()); reportStructure.Add(new DeterminismSection()); reportStructure.Add(new WarningsSection()); if (this.ShowBytes) { reportStructure.Add(new ByteCodeSection()); } reportStructure.Add(new FooterSection()); var renderer = new StreamTextRenderer(Console.Out); foreach (ValidationReportData data in reportData) { renderer.Render(reportStructure, data); } return(1); }
static async Task Main(string[] args) { // sample Q# code var qsharpCode = @" namespace HelloQuantum { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Measurement; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Convert; @EntryPoint() operation HelloQ() : Unit { let ones = GetRandomBit(100); Message(""Ones: "" + IntAsString(ones)); Message(""Zeros: "" + IntAsString((100 - ones))); } operation GetRandomBit(count : Int) : Int { mutable resultsTotal = 0; using (qubit = Qubit()) { for (idx in 0..count) { H(qubit); let result = MResetZ(qubit); set resultsTotal += result == One ? 1 | 0; } return resultsTotal; } } }"; // necessary references to compile our Q# program var qsharpReferences = new string[] { "Microsoft.Quantum.QSharp.Core", "Microsoft.Quantum.Runtime.Core", }.Select(x => Assembly.Load(new AssemblyName(x))).Select(a => a.Location); // events emitted by the Q# compiler CompilationLoader.CompilationTaskEvent += (sender, args) => { Console.WriteLine($"{args.ParentTaskName} {args.TaskName} - {args.Type}"); }; // to load our custom rewrite step, we need to point Q# compiler config at our current assembly var config = new CompilationLoader.Configuration { IsExecutable = true, RewriteSteps = new List <(string, string)> { (Assembly.GetExecutingAssembly().Location, null), }, }; // compile Q# code var compilationLoader = new CompilationLoader( loadFromDisk => new Dictionary <Uri, string> { { new Uri(Path.GetFullPath("__CODE_SNIPPET__.qs")), qsharpCode } }.ToImmutableDictionary(), qsharpReferences, options: config, logger: new ConsoleLogger()); // print any diagnostics if (compilationLoader.LoadDiagnostics.Any()) { Console.WriteLine("Diagnostics:" + Environment.NewLine + string.Join(Environment.NewLine, compilationLoader.LoadDiagnostics.Select(d => $"{d.Severity} {d.Code} {d.Message}"))); // if there are any errors, exit if (compilationLoader.LoadDiagnostics.Any(d => d.Severity == Microsoft.VisualStudio.LanguageServer.Protocol.DiagnosticSeverity.Error)) { return; } } // necessary references to compile C# simulation of the Q# compilation var csharpReferences = new string[] { "Microsoft.Quantum.QSharp.Core", "Microsoft.Quantum.Runtime.Core", "Microsoft.Quantum.Simulators", "Microsoft.Quantum.EntryPointDriver", "System.CommandLine", "System.Runtime", "netstandard", "System.Collections.Immutable", typeof(object).Assembly.FullName, }.Select(x => Assembly.Load(new AssemblyName(x))).Select(a => a.Location); // we captured the emitted C# syntax trees into a static variable in the rewrite step var syntaxTrees = InMemoryEmitter.GeneratedFiles.Select(x => CSharpSyntaxTree.ParseText(x.Value)); // compile C# code // make sure to pass in the C# references as Roslyn's metadata references var csharpCompilation = CSharpCompilation.Create("hello-qsharp", syntaxTrees) .WithReferences(csharpReferences.Select(x => MetadataReference.CreateFromFile(x))); // print any diagnostics var csharpDiagnostics = csharpCompilation.GetDiagnostics().Where(d => d.Severity != DiagnosticSeverity.Hidden); if (csharpDiagnostics.Any()) { Console.WriteLine("C# Diagnostics:" + Environment.NewLine + string.Join(Environment.NewLine, csharpDiagnostics.Select(d => $"{d.Severity} {d.Id} {d.GetMessage()}"))); // if there are any errors, exit if (csharpDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error)) { return; } } // emit C# code into an in memory assembly using var peStream = new MemoryStream(); var emitResult = csharpCompilation.Emit(peStream); peStream.Position = 0; var qsharpLoadContext = new QSharpLoadContext(); // run the assembly using reflection var qsharpAssembly = qsharpLoadContext.LoadFromStream(peStream); // the entry point has a special name "__QsEntryPoint__" var entryPoint = qsharpAssembly.GetTypes().First(x => x.Name == "__QsEntryPoint__").GetMethod("Main", BindingFlags.NonPublic | BindingFlags.Static); var entryPointTask = entryPoint.Invoke(null, new object[] { null }) as Task <int>; await entryPointTask; qsharpLoadContext.Unload(); }