Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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));
        }
    }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        // 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));
        }
    }
Beispiel #7
0
        // 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();
        }