diff --git a/.gitignore b/.gitignore index 7127ca52075..0553c56ac2a 100644 --- a/.gitignore +++ b/.gitignore @@ -341,4 +341,8 @@ ASALocalRun/ dbw_test # Controller test artifacts -/src/Qir/Controller/test-artifacts/ \ No newline at end of file +/src/Qir/Controller/test-artifacts/ +/src/Qir/Controller/src + +# out folders +out/ \ No newline at end of file diff --git a/src/Qir/Controller/Controller.cs b/src/Qir/Controller/Controller.cs index 5f9cc2b65cc..70378632128 100644 --- a/src/Qir/Controller/Controller.cs +++ b/src/Qir/Controller/Controller.cs @@ -25,7 +25,7 @@ public static async Task ExecuteAsync( DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, FileInfo errorFile, - IQirDriverGenerator driverGenerator, + IQirSourceFileGenerator driverGenerator, IQirExecutableGenerator executableGenerator, IQuantumExecutableRunner executableRunner, ILogger logger) @@ -37,10 +37,10 @@ public static async Task ExecuteAsync( using var inputFileStream = inputFile.OpenRead(); var input = QirExecutionWrapperSerialization.DeserializeFromFastBinary(inputFileStream); - // Step 32: Create driver. + // Step 2: Create driver. logger.LogInfo("Creating driver file."); var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); - await driverGenerator.GenerateQirDriverCppAsync(sourceDirectory, input.EntryPoint, input.QirBytecode); + await driverGenerator.GenerateQirSourceFilesAsync(sourceDirectory, input.EntryPoint, input.QirBytecode); // Step 3: Create executable. logger.LogInfo("Compiling and linking executable."); @@ -50,12 +50,11 @@ public static async Task ExecuteAsync( // Step 4: Run executable. logger.LogInfo("Running executable."); - using var outputFileStream = outputFile.OpenWrite(); await executableRunner.RunExecutableAsync(executableFile, input.EntryPoint, outputFile); } catch (Exception e) { - logger.LogError("An error has been encountered. Will write an error to the error file and delete any output that has been generated."); + logger.LogError("An error has been encountered. Will write an error to the error file."); logger.LogException(e); await WriteExceptionToFileAsync(e, errorFile); } diff --git a/src/Qir/Controller/Driver/IQirDriverGenerator.cs b/src/Qir/Controller/Driver/IQirSourceFileGenerator.cs similarity index 77% rename from src/Qir/Controller/Driver/IQirDriverGenerator.cs rename to src/Qir/Controller/Driver/IQirSourceFileGenerator.cs index e4dc2a6172c..92e0449c77a 100644 --- a/src/Qir/Controller/Driver/IQirDriverGenerator.cs +++ b/src/Qir/Controller/Driver/IQirSourceFileGenerator.cs @@ -8,7 +8,7 @@ namespace Microsoft.Quantum.Qir.Driver { - public interface IQirDriverGenerator + public interface IQirSourceFileGenerator { /// /// Generates the C++ driver source file and writes the bytecode to a file. @@ -17,6 +17,6 @@ public interface IQirDriverGenerator /// Entry point information. /// The QIR bytecode. /// - Task GenerateQirDriverCppAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode); + Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode); } } diff --git a/src/Qir/Controller/Driver/QirDriverGenerator.cs b/src/Qir/Controller/Driver/QirDriverGenerator.cs deleted file mode 100644 index e169ea12be4..00000000000 --- a/src/Qir/Controller/Driver/QirDriverGenerator.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Utility; -using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; - -namespace Microsoft.Quantum.Qir.Driver -{ - public class QirDriverGenerator : IQirDriverGenerator - { - private readonly ILogger logger; - - public QirDriverGenerator(ILogger logger) - { - this.logger = logger; - } - - public Task GenerateQirDriverCppAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Qir/Controller/Driver/QirSourceFileGenerator.cs b/src/Qir/Controller/Driver/QirSourceFileGenerator.cs new file mode 100644 index 00000000000..7a7cfe891a2 --- /dev/null +++ b/src/Qir/Controller/Driver/QirSourceFileGenerator.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Microsoft.Quantum.Qir.Driver +{ + public class QirSourceFileGenerator : IQirSourceFileGenerator + { + private const string BytecodeFileName = "qir.bc"; + private const string DriverFileName = "driver.cpp"; + private readonly ILogger logger; + + public QirSourceFileGenerator(ILogger logger) + { + this.logger = logger; + } + + public async Task GenerateQirSourceFilesAsync(DirectoryInfo sourceDirectory, EntryPointOperation entryPointOperation, ArraySegment bytecode) + { + if (sourceDirectory.Exists) + { + sourceDirectory.Delete(true); + } + + sourceDirectory.Create(); + logger.LogInfo($"Created source directory at {sourceDirectory.FullName}."); + + // Create driver. + var driverFile = new FileInfo(Path.Combine(sourceDirectory.FullName, DriverFileName)); + using var driverFileStream = driverFile.OpenWrite(); + GenerateQirDriverCppHelper(entryPointOperation, driverFileStream); + logger.LogInfo($"Created driver file at {driverFile.FullName}."); + + // Create bytecode file. + var bytecodeFile = new FileInfo(Path.Combine(sourceDirectory.FullName, BytecodeFileName)); + using var bytecodeFileStream = bytecodeFile.OpenWrite(); + await bytecodeFileStream.WriteAsync(bytecode.Array, bytecode.Offset, bytecode.Count); + logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); + } + + // Virtual method wrapper is to enable mocking in unit tests. + public virtual void GenerateQirDriverCppHelper(EntryPointOperation entryPointOperation, Stream stream) + { + QirDriverGeneration.GenerateQirDriverCpp(entryPointOperation, stream); + } + } +} diff --git a/src/Qir/Controller/Executable/ClangClient.cs b/src/Qir/Controller/Executable/ClangClient.cs index 72fc903d4e8..81bd435aff4 100644 --- a/src/Qir/Controller/Executable/ClangClient.cs +++ b/src/Qir/Controller/Executable/ClangClient.cs @@ -23,7 +23,7 @@ public async Task CreateExecutableAsync(string[] inputFiles, string[] libraries, // string.Join does not automatically prepend the delimiter, so it is included again in the string here. var librariesArg = $"{LinkFlag} {string.Join(LinkFlag, libraries)}"; - var arguments = $"{inputsArg} -I {includePath} -L {libraryPath} {librariesArg} -o {outputPath}"; + var arguments = $"-v {inputsArg} -I {includePath} -L {libraryPath} {librariesArg} -o {outputPath}"; logger.LogInfo($"Invoking clang with the following arguments: {arguments}"); var result = await Process.ExecuteAsync( "clang", diff --git a/src/Qir/Controller/Executable/QirExecutableGenerator.cs b/src/Qir/Controller/Executable/QirExecutableGenerator.cs index c09a7a0fa67..9445f300115 100644 --- a/src/Qir/Controller/Executable/QirExecutableGenerator.cs +++ b/src/Qir/Controller/Executable/QirExecutableGenerator.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; @@ -9,6 +10,11 @@ namespace Microsoft.Quantum.Qir.Executable { public class QirExecutableGenerator : IQirExecutableGenerator { + private static readonly string[] LibrariesToLink = { + "Microsoft.Quantum.Qir.Runtime", + "Microsoft.Quantum.Qir.QSharp.Foundation", + "Microsoft.Quantum.Qir.QSharp.Core" + }; private readonly IClangClient clangClient; private readonly ILogger logger; @@ -18,10 +24,36 @@ public QirExecutableGenerator(IClangClient clangClient, ILogger logger) this.logger = logger; } - public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory) + public async Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory) { - // TODO: Compile and link libraries- "Microsoft.Quantum.Qir.Runtime", "Microsoft.Quantum.Qir.QSharp.Foundation", "Microsoft.Quantum.Qir.QSharp.Core" - throw new System.NotImplementedException(); + // Wrap in a Task.Run because FileInfo methods are not asynchronous. + await Task.Run(async () => + { + var binDirectory = executableFile.Directory; + logger.LogInfo($"Creating binary directory at {binDirectory.FullName}."); + executableFile.Directory.Create(); + + // Copy all library contents to bin. + logger.LogInfo("Copying library directory contents into the executable's folder."); + var libraryFiles = libraryDirectory.GetFiles(); + foreach (var file in libraryFiles) + { + CopyFileIfNotExists(file, binDirectory); + } + + var inputFiles = sourceDirectory.GetFiles().Select(fileInfo => fileInfo.FullName).ToArray(); + await clangClient.CreateExecutableAsync(inputFiles, LibrariesToLink, libraryDirectory.FullName, includeDirectory.FullName, executableFile.FullName); + }); + } + + private void CopyFileIfNotExists(FileInfo fileToCopy, DirectoryInfo destinationDirectory) + { + var newPath = Path.Combine(destinationDirectory.FullName, fileToCopy.Name); + if (!File.Exists(newPath)) + { + var newFile = fileToCopy.CopyTo(newPath); + logger.LogInfo($"Copied file {fileToCopy.FullName} to {newFile.FullName}"); + } } } } diff --git a/src/Qir/Controller/Executable/QuantumExecutableRunner.cs b/src/Qir/Controller/Executable/QuantumExecutableRunner.cs index 774aa0cf517..8ce2bcd41d7 100644 --- a/src/Qir/Controller/Executable/QuantumExecutableRunner.cs +++ b/src/Qir/Controller/Executable/QuantumExecutableRunner.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.CommandLine.Invocation; using System.IO; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; namespace Microsoft.Quantum.Qir.Executable @@ -17,9 +19,23 @@ public QuantumExecutableRunner(ILogger logger) this.logger = logger; } - public Task RunExecutableAsync(FileInfo executableFile, EntryPointOperation entryPointOperation, FileInfo outputFile) + public async Task RunExecutableAsync(FileInfo executableFile, EntryPointOperation entryPointOperation, FileInfo outputFile) { - throw new System.NotImplementedException(); + var arguments = QirDriverGeneration.GenerateCommandLineArguments(entryPointOperation.Arguments); + logger.LogInfo($"Invoking executable {executableFile.FullName} with the following arguments: {arguments}"); + arguments = $"--simulation-output {outputFile.FullName} {arguments}"; + if (outputFile.Exists) + { + outputFile.Delete(); + } + + outputFile.Create().Dispose(); + var result = await Process.ExecuteAsync( + executableFile.FullName, + arguments, + stdOut: s => { logger.LogInfo("executable: " + s); }, + stdErr: s => { logger.LogError("executable: " + s); }); + logger.LogInfo($"Executable has finished running. Result code: {result}"); } } } diff --git a/src/Qir/Controller/Program.cs b/src/Qir/Controller/Program.cs index 88677d0d1ce..6def5f9d223 100644 --- a/src/Qir/Controller/Program.cs +++ b/src/Qir/Controller/Program.cs @@ -16,7 +16,7 @@ static void Main(string[] args) { var logger = new Logger(new Clock()); var execGenerator = new QirExecutableGenerator(new ClangClient(logger), logger); - var driverGenerator = new QirDriverGenerator(logger); + var driverGenerator = new QirSourceFileGenerator(logger); var execRunner = new QuantumExecutableRunner(logger); logger.LogInfo("QIR controller beginning."); diff --git a/src/Qir/Controller/QirController.csproj b/src/Qir/Controller/QirController.csproj index 4570cafa210..e9574233c00 100644 --- a/src/Qir/Controller/QirController.csproj +++ b/src/Qir/Controller/QirController.csproj @@ -7,18 +7,22 @@ + + + + - + diff --git a/src/Qir/Controller/Tests.QirController/ControllerTests.cs b/src/Qir/Controller/Tests.QirController/ControllerTests.cs index 5da9e861d37..6e85b07976b 100644 --- a/src/Qir/Controller/Tests.QirController/ControllerTests.cs +++ b/src/Qir/Controller/Tests.QirController/ControllerTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Reflection; using System.Text.Json; using System.Threading.Tasks; using Microsoft.Quantum.Qir; @@ -22,7 +21,7 @@ namespace Tests.QirController { public class ControllerTests : IDisposable { - private Mock driverGeneratorMock; + private Mock driverGeneratorMock; private Mock executableGeneratorMock; private Mock executableRunnerMock; private Mock loggerMock; @@ -34,7 +33,7 @@ public class ControllerTests : IDisposable public ControllerTests() { - driverGeneratorMock = new Mock(); + driverGeneratorMock = new Mock(); executableGeneratorMock = new Mock(); executableRunnerMock = new Mock(); inputFile = new FileInfo($"{Guid.NewGuid()}-input"); @@ -102,9 +101,9 @@ await Controller.ExecuteAsync( loggerMock.Object); // Verify driver was created. - driverGeneratorMock.Verify(obj => obj.GenerateQirDriverCppAsync( + driverGeneratorMock.Verify(obj => obj.GenerateQirSourceFilesAsync( It.IsAny(), - It.Is(entryPoint => EntryPointsAreEqual(entryPoint, input.EntryPoint)), + It.Is(entryPoint => Util.EntryPointsAreEqual(entryPoint, input.EntryPoint)), It.Is>(bytecode => BytecodesAreEqual(bytecode, input.QirBytecode)))); // Verify executable was generated. @@ -118,7 +117,7 @@ await Controller.ExecuteAsync( // Verify executable was run. executableRunnerMock.Verify(obj => obj.RunExecutableAsync( It.Is(executableFile => actualExecutableFile.FullName == executableFile.FullName), - It.Is(entryPoint => EntryPointsAreEqual(entryPoint, input.EntryPoint)), + It.Is(entryPoint => Util.EntryPointsAreEqual(entryPoint, input.EntryPoint)), It.Is(actualOutputFile => actualOutputFile.FullName == outputFile.FullName))); } @@ -184,14 +183,6 @@ await Controller.ExecuteAsync( Assert.Equal(errorCode, error.Code); } - private bool EntryPointsAreEqual(EntryPointOperation entryPointA, EntryPointOperation entryPointB) - { - var method = typeof(Extensions) - .GetMethod("ValueEquals", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(EntryPointOperation), typeof(EntryPointOperation) }, null); - object[] parameters = { entryPointA, entryPointB }; - return (bool)method.Invoke(null, parameters); - } - private bool BytecodesAreEqual(ArraySegment bytecodeA, ArraySegment bytecodeB) { if (bytecodeA.Count != bytecodeB.Count) diff --git a/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs b/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs new file mode 100644 index 00000000000..e93837e62ea --- /dev/null +++ b/src/Qir/Controller/Tests.QirController/QirDriverGeneratorTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Driver; +using Microsoft.Quantum.Qir.Utility; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class QirDriverGeneratorTests : IDisposable + { + private readonly Mock driverGeneratorMock; + private readonly DirectoryInfo sourceDirectory; + + public QirDriverGeneratorTests() + { + driverGeneratorMock = new Mock(Mock.Of()) { CallBase = true }; + driverGeneratorMock.Setup(obj => obj.GenerateQirDriverCppHelper(It.IsAny(), It.IsAny())); + sourceDirectory = new DirectoryInfo($"{Guid.NewGuid()}-source"); + } + + public void Dispose() + { + Util.DeleteDirectory(sourceDirectory); + } + + [Fact] + public async Task TestGenerateDriver() + { + var entryPoint = new EntryPointOperation() + { + Arguments = new List + { + new Argument() + { + Position = 0, + Name = "argname", + Values = new List { new ArgumentValue { String = "argvalue" } }, + } + } + }; + byte[] bytes = { 1, 2, 3, 4, 5 }; + var bytecode = new ArraySegment(bytes, 1, 3); + await driverGeneratorMock.Object.GenerateQirSourceFilesAsync(sourceDirectory, entryPoint, bytecode); + driverGeneratorMock.Verify(obj => obj.GenerateQirDriverCppHelper(It.Is(actualEntryPoint => Util.EntryPointsAreEqual(entryPoint, actualEntryPoint)), It.IsAny())); + + // Verify that the "bytecode" file was created correctly. + var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); + using var bytecodeFileStream = bytecodeFilePath.OpenRead(); + Assert.Equal(bytecodeFileStream.Length, bytecode.Count); + for (var i = 0; i < bytecodeFileStream.Length; ++i) + { + Assert.Equal(bytecode[i], bytecodeFileStream.ReadByte()); + } + } + } +} diff --git a/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs b/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs new file mode 100644 index 00000000000..3d9da400687 --- /dev/null +++ b/src/Qir/Controller/Tests.QirController/QirExecutableGeneratorTests.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Executable; +using Microsoft.Quantum.Qir.Utility; +using Moq; +using Xunit; + +namespace Tests.QirController +{ + public class QirExecutableGeneratorTests : IDisposable + { + private readonly Mock clangClientMock; + private readonly QirExecutableGenerator executableGenerator; + private readonly DirectoryInfo sourceDirectory; + private readonly DirectoryInfo includeDirectory; + private readonly DirectoryInfo libraryDirectory; + private readonly DirectoryInfo binDirectory; + private readonly IList libraryFiles; + private readonly IList sourceFiles; + + public QirExecutableGeneratorTests() + { + clangClientMock = new Mock(); + executableGenerator = new QirExecutableGenerator(clangClientMock.Object, Mock.Of()); + + // Set up files. + var prefix = Guid.NewGuid().ToString(); + binDirectory = new DirectoryInfo($"{prefix}-bin"); + binDirectory.Create(); + libraryDirectory = new DirectoryInfo($"{prefix}-library"); + libraryDirectory.Create(); + libraryFiles = new List() + { + CreateFile("lib1", libraryDirectory, "lib1 contents"), + CreateFile("lib2", libraryDirectory, "lib2 contents"), + }; + includeDirectory = new DirectoryInfo($"{prefix}-include"); + includeDirectory.Create(); + sourceDirectory = new DirectoryInfo($"{prefix}-source"); + sourceDirectory.Create(); + sourceFiles = new List() + { + CreateFile("src1.cpp", sourceDirectory, "src1 contents"), + CreateFile("src2.bc", sourceDirectory, "src2 contents"), + }; + } + + public void Dispose() + { + Util.DeleteDirectory(sourceDirectory); + Util.DeleteDirectory(includeDirectory); + Util.DeleteDirectory(libraryDirectory); + Util.DeleteDirectory(binDirectory); + } + + [Fact] + public async Task TestGenerateExecutable() + { + string[] expectedLibraries = { + "Microsoft.Quantum.Qir.Runtime", + "Microsoft.Quantum.Qir.QSharp.Foundation", + "Microsoft.Quantum.Qir.QSharp.Core" + }; + + var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); + await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory); + + // Verify invocation of clang. + clangClientMock.Verify(obj => obj.CreateExecutableAsync( + It.Is(s => s.SequenceEqual(sourceFiles.Select(fileInfo => fileInfo.FullName))), + It.Is(s => s.SequenceEqual(expectedLibraries)), + libraryDirectory.FullName, + includeDirectory.FullName, + executableFile.FullName)); + + // Verify files were copied. + Assert.True(FilesWereCopied(libraryFiles.ToArray(), binDirectory)); + } + + private static FileInfo CreateFile(string fileName, DirectoryInfo directory, string contents) + { + var filePath = Path.Combine(directory.FullName, fileName); + var fileInfo = new FileInfo(filePath); + using var fileStream = fileInfo.OpenWrite(); + using var streamWriter = new StreamWriter(fileStream); + streamWriter.Write(contents); + return fileInfo; + } + + private static bool FilesWereCopied(FileInfo[] files, DirectoryInfo destinationDirectory) + { + var destinationDirectoryFiles = destinationDirectory.GetFiles(); + foreach (var file in files) + { + var copiedFile = destinationDirectoryFiles.FirstOrDefault(fileInfo => fileInfo.Name == file.Name); + if (copiedFile == null) + { + return false; + } + + using var originalFileReader = file.OpenText(); + var originalFileContents = originalFileReader.ReadToEnd(); + using var copiedFileReader = copiedFile.OpenText(); + var copiedFileContents = copiedFileReader.ReadToEnd(); + if (originalFileContents != copiedFileContents) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/Qir/Controller/Tests.QirController/Util.cs b/src/Qir/Controller/Tests.QirController/Util.cs new file mode 100644 index 00000000000..476848705ea --- /dev/null +++ b/src/Qir/Controller/Tests.QirController/Util.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Reflection; +using Microsoft.Quantum.QsCompiler.BondSchemas.EntryPoint; + +namespace Tests.QirController +{ + public static class Util + { + public static void DeleteDirectory(DirectoryInfo directory) + { + foreach (var file in directory.GetFiles()) + { + file.Delete(); + } + directory.Delete(); + } + + + public static bool EntryPointsAreEqual(EntryPointOperation entryPointA, EntryPointOperation entryPointB) + { + var method = typeof(Extensions) + .GetMethod("ValueEquals", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(EntryPointOperation), typeof(EntryPointOperation) }, null); + object[] parameters = { entryPointA, entryPointB }; + return (bool)method.Invoke(null, parameters); + } + } +} diff --git a/src/Qir/Controller/test-cases/standalone-input-test.in b/src/Qir/Controller/test-cases/standalone-input-test.in new file mode 100644 index 00000000000..2251eb15f36 Binary files /dev/null and b/src/Qir/Controller/test-cases/standalone-input-test.in differ diff --git a/src/Qir/Controller/test-cases/standalone-input-test.out b/src/Qir/Controller/test-cases/standalone-input-test.out new file mode 100644 index 00000000000..73833a0e784 --- /dev/null +++ b/src/Qir/Controller/test-cases/standalone-input-test.out @@ -0,0 +1,14 @@ +Exercise Supported Inputs Reference +intValue: 42 +intArray: [9223372036854775807, 4, 4, 2, 3] (5) +doubleValue: 3.14000000000000012 +doubleArray: [0.59999999999999998, 0.40000000000000002, 43.20000000000000284] (3) +boolValue: true +boolArray: [true, false, true, true, false] (5) +pauliValue: PauliZ +pauliArray: [PauliX, PauliI, PauliY, PauliY, PauliZ] (5) +rangeValue: 3..12..27 +resultValue: One +resultArray: [One, One, Zero, One] (4) +stringValue: string value +() diff --git a/src/Qir/Controller/test-qir-controller.ps1 b/src/Qir/Controller/test-qir-controller.ps1 index ad70d23d1b7..c8dc17ea1aa 100644 --- a/src/Qir/Controller/test-qir-controller.ps1 +++ b/src/Qir/Controller/test-qir-controller.ps1 @@ -5,14 +5,54 @@ $all_ok = $True Write-Host "##[info]Test QIR Controller" +$buildConfiguration = $Env:BUILD_CONFIGURATION $controllerProject = (Join-Path $PSScriptRoot QirController.csproj) $testCasesFolder = (Join-Path $PSScriptRoot "test-cases") $testArtifactsFolder = (Join-Path $PSScriptRoot "test-artifacts") +$includeDirectory = (Join-Path $testArtifactsFolder "include") +$headerPaths = @((Join-Path $PSScriptRoot "..\..\Qir\Common\externals\CLI11"), (Join-Path $PSScriptRoot "..\..\Qir\Runtime\public")) +$libraryPaths = @((Join-Path $PSScriptRoot "..\..\Qir\Runtime\build\$buildConfiguration\bin"), (Join-Path $PSScriptRoot "..\..\Simulation\Simulators\bin\$buildConfiguration\netstandard2.1")) +$includeDirectory = (Join-Path $testArtifactsFolder "include") +$libraryDirectory = (Join-Path $testArtifactsFolder "library") + +if (($IsWindows) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) +{ + Write-Host "On Windows build using Clang" + $env:CC = "clang.exe" + $env:CXX = "clang++.exe" + $env:RC = "clang++.exe" + + if (!(Get-Command clang -ErrorAction SilentlyContinue) -and (choco find --idonly -l llvm) -contains "llvm") { + # LLVM was installed by Chocolatey, so add the install location to the path. + $env:PATH += ";$($env:SystemDrive)\Program Files\LLVM\bin" + Write-Host "Adding clang to path. Path: $env:PATH" + } +} if (!(Test-Path $testArtifactsFolder -PathType Container)) { New-Item -ItemType Directory -Force -Path $testArtifactsFolder } -Get-ChildItem -Path $testArtifactsFolder | Remove-Item -Force +Get-ChildItem -Path $testArtifactsFolder | Remove-Item -Force -Recurse + +# Copy includes to the include folder +New-Item -ItemType "directory" -Path $includeDirectory -Force +foreach ( $path in $headerPaths ) +{ + Get-ChildItem $path -File | + Foreach-Object { + Copy-Item $_ -Destination (Join-Path $includeDirectory $_.Name) + } +} + +# Copy libraries to the library folder +New-Item -ItemType "directory" -Path $libraryDirectory -Force +foreach ( $path in $libraryPaths ) +{ + Get-ChildItem $path -File | + Foreach-Object { + Copy-Item $_ -Destination (Join-Path $libraryDirectory $_.Name) + } +} # Go through each input file in the test cases folder. Get-ChildItem $testCasesFolder -Filter *.in | @@ -20,7 +60,7 @@ Foreach-Object { # Get the paths to the output and error files to pass to the QIR controller. $outputFile = (Join-Path $testArtifactsFolder ($_.BaseName + ".out")) $errorFile = (Join-Path $testArtifactsFolder ($_.BaseName + ".err")) - dotnet run --project $controllerProject -- --input $_.FullName --output $outputFile --error $errorFile --includeDirectory "placeholder for now" --libraryDirectory "placeholder for now" + dotnet run --project $controllerProject -- --input $_.FullName --output $outputFile --error $errorFile --includeDirectory $includeDirectory --libraryDirectory $libraryDirectory # Compare the expected content of the output and error files vs the actual content. $expectedOutputFile = (Join-Path $testCasesFolder ($_.BaseName + ".out"))