diff --git a/Simulation.sln b/Simulation.sln index 419bd8075cf..f636b1c1a9a 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -121,7 +121,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Execution", "Execution", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tools\Microsoft.Quantum.Qir.Tools.csproj", "{C60226E3-98DE-4E92-AED4-B4A93D4CA063}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tests.Microsoft.Quantum.Qir.Tools\Tests.Microsoft.Quantum.Qir.Tools.csproj", "{4794FC80-4594-403F-AFEC-4889EFE87EA0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Microsoft.Quantum.Qir.Tools", "src\Qir\Execution\Tests.Microsoft.Quantum.Qir.Tools\Tests.Microsoft.Quantum.Qir.Tools.csproj", "{4794FC80-4594-403F-AFEC-4889EFE87EA0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QirCommandLineTool", "src\Qir\Execution\QirCommandLineTool\QirCommandLineTool.csproj", "{039D2649-20F6-46C7-8599-C44F3AA2CD4C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QirExe", "src\Simulation\Simulators.Tests\TestProjects\QirExe\QirExe.csproj", "{A0B98D0E-FC28-4FD8-8806-4825B9F5489D}" EndProject @@ -809,6 +811,22 @@ Global {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {4794FC80-4594-403F-AFEC-4889EFE87EA0}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Debug|x64.ActiveCfg = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Debug|x64.Build.0 = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Release|Any CPU.Build.0 = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Release|x64.ActiveCfg = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.Release|x64.Build.0 = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {039D2649-20F6-46C7-8599-C44F3AA2CD4C}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {A0B98D0E-FC28-4FD8-8806-4825B9F5489D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0B98D0E-FC28-4FD8-8806-4825B9F5489D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A0B98D0E-FC28-4FD8-8806-4825B9F5489D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -883,6 +901,7 @@ Global {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {C60226E3-98DE-4E92-AED4-B4A93D4CA063} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} {4794FC80-4594-403F-AFEC-4889EFE87EA0} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} + {039D2649-20F6-46C7-8599-C44F3AA2CD4C} = {442E66C8-F69F-44E9-9CD9-1F52C37EA41B} {A0B98D0E-FC28-4FD8-8806-4825B9F5489D} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Qir/Controller/Controller.cs b/src/Qir/Controller/Controller.cs index 1ae7e6a747c..93b8b38a8bd 100644 --- a/src/Qir/Controller/Controller.cs +++ b/src/Qir/Controller/Controller.cs @@ -41,7 +41,7 @@ public static async Task ExecuteAsync( var bytecodeArray = input.QirBytecode.Array.Skip(input.QirBytecode.Offset).Take(input.QirBytecode.Count).ToList().ToArray(); var executableFile = new FileInfo(Path.Combine(BinaryDirectoryPath, ExecutableName)); var executable = new QirFullStateExecutable(executableFile, bytecodeArray, logger); - await executable.BuildAsync(executionInfo.EntryPoint, libraryDirectory, includeDirectory); + await executable.BuildAsync(executionInfo.EntryPoint, new[] { libraryDirectory }, new[] { includeDirectory }); // Step 3: Run executable. if (outputFile.Exists) diff --git a/src/Qir/Execution/QirCommandLineTool/Program.cs b/src/Qir/Execution/QirCommandLineTool/Program.cs new file mode 100644 index 00000000000..760788508e7 --- /dev/null +++ b/src/Qir/Execution/QirCommandLineTool/Program.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.CommandLine; +using System.CommandLine.Builder; +using System.CommandLine.Help; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Tools; + +namespace Microsoft.Quantum.CommandLineCompiler +{ + class Program + { + private static async Task Main(string[] args) + { + var buildCommand = CreateBuildCommand(); + + var root = new RootCommand() { buildCommand }; + SetSubCommandAsDefault(root, buildCommand); + root.Description = "Command-line tool for processing QIR DLL files."; + root.TreatUnmatchedTokensAsErrors = true; + + Console.OutputEncoding = Encoding.UTF8; + return await new CommandLineBuilder(root) + .UseDefaults() + .UseHelpBuilder(context => new QsHelpBuilder(context.Console)) + .Build() + .InvokeAsync(args); + } + + /// + /// Creates the Build command for the command line compiler, which is used to + /// compile the executables from a given QIR DLL. + /// + /// The Build command. + private static Command CreateBuildCommand() + { + var buildCommand = new Command("build", "(default) Build the executables from a QIR DLL.") + { + Handler = CommandHandler.Create((BuildOptions settings) => + { + return QirTools.BuildFromQSharpDll(settings.QSharpDll, settings.LibraryDirectories, settings.IncludeDirectories, settings.ExecutablesDirectory); + }) + }; + buildCommand.TreatUnmatchedTokensAsErrors = true; + + buildCommand.AddOption(new Option( + aliases: new string[] { "--qsharp-dll", "--dll" }, + description: "The path to the .NET DLL file generated by the Q# compiler.") + { + Required = true + }); + + buildCommand.AddOption(new Option( + aliases: new string[] { "--library-directories", "--lib" }, + description: "One or more paths to the directories containing the libraries to be linked.") + { + Required = false, + Argument = new Argument(() => new DirectoryInfo[] { }) + { + Arity = ArgumentArity.OneOrMore + } + }); + + buildCommand.AddOption(new Option( + aliases: new string[] { "--include-directories", "--include" }, + description: "One or more paths to the directories containing the headers required for compilation.") + { + Required = false, + Argument = new Argument(() => new DirectoryInfo[] { }) + { + Arity = ArgumentArity.OneOrMore + } + }); + + buildCommand.AddOption(new Option( + aliases: new string[] { "--executables-directory", "--exe" }, + description: "The path to the output directory where the created executables will be placed.") + { + Required = true + }); + + return buildCommand; + } + + /// + /// Copies the handle and options from the given sub command to the given command. + /// + /// The command whose handle and options will be set. + /// The sub command that will be copied from. + private static void SetSubCommandAsDefault(Command root, Command subCommand) + { + root.Handler = subCommand.Handler; + foreach (var option in subCommand.Options) + { + root.AddOption(option); + } + } + + /// + /// A modification of the command-line class. + /// + private sealed class QsHelpBuilder : HelpBuilder + { + /// + /// Creates a new help builder using the given console. + /// + /// The console to use. + internal QsHelpBuilder(IConsole console) : base(console) + { + } + + protected override string ArgumentDescriptor(IArgument argument) + { + // Hide long argument descriptors. + var descriptor = base.ArgumentDescriptor(argument); + return descriptor.Length > 30 ? argument.Name : descriptor; + } + } + + /// + /// A class for encapsulating the different options for the build command. + /// + public sealed class BuildOptions + { + /// + /// The path to the .NET DLL file generated by the Q# compiler. + /// + public FileInfo QSharpDll { get; set; } + + /// + /// One or more paths to the directories containing the libraries to be linked. + /// + public DirectoryInfo[] LibraryDirectories { get; set; } + + /// + /// One or more paths to the directories containing the headers required for compilation. + /// + public DirectoryInfo[] IncludeDirectories { get; set; } + + /// + /// The path to the output directory where the created executables will be placed. + /// + public DirectoryInfo ExecutablesDirectory { get; set; } + } + } +} diff --git a/src/Qir/Execution/QirCommandLineTool/QirCommandLineTool.csproj b/src/Qir/Execution/QirCommandLineTool/QirCommandLineTool.csproj new file mode 100644 index 00000000000..868df199d12 --- /dev/null +++ b/src/Qir/Execution/QirCommandLineTool/QirCommandLineTool.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.1 + true + + + + + + + + + diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs index b110c59df65..fd88b7550f6 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableGeneratorTests.cs @@ -65,14 +65,14 @@ public void Dispose() public async Task TestGenerateExecutable() { var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); - await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, libraryDirectory, includeDirectory, linkLibraries); + await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries); // Verify invocation of clang. clangClientMock.Verify(obj => obj.CreateExecutableAsync( It.Is(s => s.OrderBy(val => val).SequenceEqual(sourceFiles.OrderBy(val => val.FullName).Select(fileInfo => fileInfo.FullName))), It.Is(s => s.OrderBy(val => val).SequenceEqual(linkLibraries.OrderBy(val => val))), - libraryDirectory.FullName, - includeDirectory.FullName, + new[] { libraryDirectory.FullName }, + new[] { includeDirectory.FullName }, executableFile.FullName)); // Verify files were copied. diff --git a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs index 8c3b469c2a5..61750a83d5c 100644 --- a/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs +++ b/src/Qir/Execution/Tests.Microsoft.Quantum.Qir.Tools/QirExecutableTests.cs @@ -72,7 +72,7 @@ public async Task TestBuild() }); // Build the executable. - await qirExecutable.Object.BuildAsync(entryPoint, libraryDirectory, includeDirectory); + await qirExecutable.Object.BuildAsync(entryPoint, new[] { libraryDirectory }, new[] { includeDirectory }); // Verify that the "bytecode" file was created correctly. var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); @@ -90,7 +90,7 @@ public async Task TestBuild() Assert.Equal(driverFileContents, actualDriverContents); // Verify that the executable was generated. - executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is(arg => arg.FullName == sourceDirectory.FullName), libraryDirectory, includeDirectory, linkLibraries)); + executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is(arg => arg.FullName == sourceDirectory.FullName), new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries)); } [Fact] diff --git a/src/Qir/Execution/Tools/Executable/ClangClient.cs b/src/Qir/Execution/Tools/Executable/ClangClient.cs index 0279e8cdf53..a8e74f2a404 100644 --- a/src/Qir/Execution/Tools/Executable/ClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/ClangClient.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Utility; @@ -11,6 +12,8 @@ namespace Microsoft.Quantum.Qir.Tools.Executable internal class ClangClient : IClangClient { private const string LinkFlag = " -l "; + private const string LibraryPathFlag = " -L "; + private const string IncludePathFlag = " -I "; private readonly ILogger logger; public ClangClient(ILogger logger) @@ -18,18 +21,20 @@ public ClangClient(ILogger logger) this.logger = logger; } - public async Task CreateExecutableAsync(string[] inputFiles, string[] libraries, string libraryPath, string includePath, string outputPath) + public async Task CreateExecutableAsync(string[] inputFiles, string[] linkedLibraries, string[] libraryPaths, string[] includePaths, string outputPath) { var inputsArg = string.Join(' ', inputFiles); // 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} -std=c++17 -v"; - logger.LogInfo($"Invoking clang with the following arguments: {arguments}"); + var linkedLibrariesArg = $"{LinkFlag} {string.Join(LinkFlag, linkedLibraries)}"; + var libraryPathsArg = $"{LibraryPathFlag} {string.Join(LibraryPathFlag, libraryPaths)}"; + var includePathsArg = $"{IncludePathFlag} {string.Join(IncludePathFlag, includePaths)}"; + var arguments = $"{inputsArg} {includePathsArg} {libraryPathsArg} {linkedLibrariesArg} -o {outputPath} -std=c++17 -v"; + logger?.LogInfo($"Invoking clang with the following arguments: {arguments}"); var taskCompletionSource = new TaskCompletionSource(); using var process = new Process(); - Environment.SetEnvironmentVariable("DYLD_LIBRARY_PATH", libraryPath); - Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", libraryPath); + AddPathsToEnvironmentVariable("DYLD_LIBRARY_PATH", libraryPaths); + AddPathsToEnvironmentVariable("LD_LIBRARY_PATH", libraryPaths); process.StartInfo = new ProcessStartInfo { FileName = "clang++", @@ -40,5 +45,16 @@ public async Task CreateExecutableAsync(string[] inputFiles, string[] libraries, process.Start(); await taskCompletionSource.Task; } + + private static void AddPathsToEnvironmentVariable(string variable, string[] values) + { + var newValue = string.Join(Path.PathSeparator, values); + var oldValue = Environment.GetEnvironmentVariable(variable); + if (oldValue != null) + { + newValue = oldValue + $"{Path.PathSeparator}{newValue}"; + } + Environment.SetEnvironmentVariable(variable, newValue); + } } } diff --git a/src/Qir/Execution/Tools/Executable/IClangClient.cs b/src/Qir/Execution/Tools/Executable/IClangClient.cs index f5a1bb38135..87423492187 100644 --- a/src/Qir/Execution/Tools/Executable/IClangClient.cs +++ b/src/Qir/Execution/Tools/Executable/IClangClient.cs @@ -10,6 +10,6 @@ namespace Microsoft.Quantum.Qir.Tools.Executable /// internal interface IClangClient { - Task CreateExecutableAsync(string[] inputFiles, string[] libraries, string libraryPath, string includePath, string outputPath); + Task CreateExecutableAsync(string[] inputFiles, string[] linkedLibraries, string[] libraryPaths, string[] includePaths, string outputPath); } } diff --git a/src/Qir/Execution/Tools/Executable/IQirExecutable.cs b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs index 477dd2cfe2a..e3ad50a6fcb 100644 --- a/src/Qir/Execution/Tools/Executable/IQirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/IQirExecutable.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Microsoft.Quantum.Qir.Serialization; @@ -13,10 +14,10 @@ public interface IQirExecutable /// Builds the executable. /// /// Entry point operation. - /// Directory containing libraries to link. - /// Directory containing files to include. + /// Directories containing libraries to link. + /// Directories containing files to include. /// - Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory); + Task BuildAsync(EntryPointOperation entryPoint, IList libraryDirectories, IList includeDirectories); /// /// Runs the executable. diff --git a/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs index 3ddb310f98e..3715a8fed2e 100644 --- a/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs @@ -14,10 +14,10 @@ internal interface IQirExecutableGenerator /// /// File path to create the executable at. Dependencies will be copied to its directory. /// Location of the source files. - /// Location of the libraries that must be linked. - /// Location of the headers that must be included. + /// Locations of the libraries that must be linked. + /// Locations of the headers that must be included. /// Libraries to link. /// - public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, IList linkLibraries); + public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, IList libraryDirectories, IList includeDirectories, IList linkLibraries); } } diff --git a/src/Qir/Execution/Tools/Executable/QirExecutable.cs b/src/Qir/Execution/Tools/Executable/QirExecutable.cs index 4c6f73c7d2d..9aa7d671b8a 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutable.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutable.cs @@ -49,7 +49,7 @@ internal QirExecutable(FileInfo executableFile, byte[] qirBytecode, ILogger logg this.executableGenerator = executableGenerator; } - public async Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory) + public async Task BuildAsync(EntryPointOperation entryPoint, IList libraryDirectories, IList includeDirectories) { var sourceDirectory = new DirectoryInfo(SourceDirectoryPath); if (sourceDirectory.Exists) @@ -77,7 +77,7 @@ public async Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libra } logger.LogInfo($"Created bytecode file at {bytecodeFile.FullName}."); - await executableGenerator.GenerateExecutableAsync(ExecutableFile, sourceDirectory, libraryDirectory, includeDirectory, LinkLibraries); + await executableGenerator.GenerateExecutableAsync(ExecutableFile, sourceDirectory, libraryDirectories, includeDirectories, LinkLibraries); } public async Task RunAsync(ExecutionInformation executionInformation, Stream output) diff --git a/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs index 80eaf8a768a..3201adeebe1 100644 --- a/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs +++ b/src/Qir/Execution/Tools/Executable/QirExecutableGenerator.cs @@ -20,25 +20,25 @@ public QirExecutableGenerator(IClangClient clangClient, ILogger logger) this.logger = logger; } - public async Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, IList linkLibraries) + public async Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, IList libraryDirectories, IList includeDirectories, IList linkLibraries) { // 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}."); + 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) + logger?.LogInfo("Copying library directories into the executable's folder."); + + foreach (var dir in libraryDirectories) { - CopyFileIfNotExists(file, binDirectory); + CopyDirectoryContents(dir, binDirectory); } var inputFiles = sourceDirectory.GetFiles().Select(fileInfo => fileInfo.FullName).ToArray(); - await clangClient.CreateExecutableAsync(inputFiles, linkLibraries.ToArray(), libraryDirectory.FullName, includeDirectory.FullName, executableFile.FullName); + await clangClient.CreateExecutableAsync(inputFiles, linkLibraries.ToArray(), libraryDirectories.Select(dir => dir.FullName).ToArray(), includeDirectories.Select(dir => dir.FullName).ToArray(), executableFile.FullName); }); } @@ -48,7 +48,16 @@ private void CopyFileIfNotExists(FileInfo fileToCopy, DirectoryInfo destinationD if (!File.Exists(newPath)) { var newFile = fileToCopy.CopyTo(newPath); - logger.LogInfo($"Copied file {fileToCopy.FullName} to {newFile.FullName}"); + logger?.LogInfo($"Copied file {fileToCopy.FullName} to {newFile.FullName}"); + } + } + + private void CopyDirectoryContents(DirectoryInfo directoryToCopy, DirectoryInfo destinationDirectory) + { + FileInfo[] files = directoryToCopy.GetFiles(); + foreach (var file in files) + { + CopyFileIfNotExists(file, destinationDirectory); } } } diff --git a/src/Qir/Execution/Tools/QirTools.cs b/src/Qir/Execution/Tools/QirTools.cs index e430ce5501b..b79c73072bb 100644 --- a/src/Qir/Execution/Tools/QirTools.cs +++ b/src/Qir/Execution/Tools/QirTools.cs @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Microsoft.Quantum.Qir.Tools.Executable; -using Microsoft.Quantum.QsCompiler; -using Microsoft.Quantum.QsCompiler.QIR; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Tools.Executable; +using Microsoft.Quantum.QsCompiler; +using Microsoft.Quantum.QsCompiler.QIR; namespace Microsoft.Quantum.Qir.Tools { @@ -20,13 +21,13 @@ public static class QirTools /// Creates a QIR-based executable from a .NET DLL generated by the Q# compiler. /// /// .NET DLL generated by the Q# compiler. - /// Directory where the libraries to link to are located. - /// Directory where the headers needed for compilation are located. + /// Directory where the libraries to link to are located. + /// Directory where the headers needed for compilation are located. /// Directory where the created executables are placed. public static async Task BuildFromQSharpDll( FileInfo qsharpDll, - DirectoryInfo libraryDirectory, - DirectoryInfo includeDirectory, + IList libraryDirectories, + IList includeDirectories, DirectoryInfo executablesDirectory) { using var qirContentStream = new MemoryStream(); @@ -34,19 +35,19 @@ public static async Task BuildFromQSharpDll( { throw new ArgumentException("The given DLL does not contain QIR byte code."); } - + if (!executablesDirectory.Exists) { executablesDirectory.Create(); } - + var tasks = EntryPointOperationLoader.LoadEntryPointOperations(qsharpDll).Select(entryPointOp => { - var exeFileInfo = new FileInfo(Path.Combine(executablesDirectory.FullName, entryPointOp.Name)); + var exeFileInfo = new FileInfo(Path.Combine(executablesDirectory.FullName, $"{entryPointOp.Name}.exe")); var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray()); - return exe.BuildAsync(entryPointOp, libraryDirectory, includeDirectory); + return exe.BuildAsync(entryPointOp, libraryDirectories, includeDirectories); }); - + await Task.WhenAll(tasks); // ToDo: Return list of created file names