Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion Simulation.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Qir/Controller/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
152 changes: 152 additions & 0 deletions src/Qir/Execution/QirCommandLineTool/Program.cs
Original file line number Diff line number Diff line change
@@ -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<int> 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);
}

/// <summary>
/// Creates the Build command for the command line compiler, which is used to
/// compile the executables from a given QIR DLL.
/// </summary>
/// <returns>The Build command.</returns>
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<FileInfo>(
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<DirectoryInfo[]>(
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<DirectoryInfo[]>(() => new DirectoryInfo[] { })
{
Arity = ArgumentArity.OneOrMore
}
});

buildCommand.AddOption(new Option<DirectoryInfo[]>(
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<DirectoryInfo[]>(() => new DirectoryInfo[] { })
{
Arity = ArgumentArity.OneOrMore
}
});

buildCommand.AddOption(new Option<DirectoryInfo>(
aliases: new string[] { "--executables-directory", "--exe" },
description: "The path to the output directory where the created executables will be placed.")
{
Required = true
});

return buildCommand;
}

/// <summary>
/// Copies the handle and options from the given sub command to the given command.
/// </summary>
/// <param name="root">The command whose handle and options will be set.</param>
/// <param name="subCommand">The sub command that will be copied from.</param>
private static void SetSubCommandAsDefault(Command root, Command subCommand)
{
root.Handler = subCommand.Handler;
foreach (var option in subCommand.Options)
{
root.AddOption(option);
}
}

/// <summary>
/// A modification of the command-line <see cref="HelpBuilder"/> class.
/// </summary>
private sealed class QsHelpBuilder : HelpBuilder
{
/// <summary>
/// Creates a new help builder using the given console.
/// </summary>
/// <param name="console">The console to use.</param>
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;
}
}

/// <summary>
/// A class for encapsulating the different options for the build command.
/// </summary>
public sealed class BuildOptions
{
/// <summary>
/// The path to the .NET DLL file generated by the Q# compiler.
/// </summary>
public FileInfo QSharpDll { get; set; }

/// <summary>
/// One or more paths to the directories containing the libraries to be linked.
/// </summary>
public DirectoryInfo[] LibraryDirectories { get; set; }

/// <summary>
/// One or more paths to the directories containing the headers required for compilation.
/// </summary>
public DirectoryInfo[] IncludeDirectories { get; set; }

/// <summary>
/// The path to the output directory where the created executables will be placed.
/// </summary>
public DirectoryInfo ExecutablesDirectory { get; set; }
}
}
}
15 changes: 15 additions & 0 deletions src/Qir/Execution/QirCommandLineTool/QirCommandLineTool.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PackAsTool>true</PackAsTool>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20213.1" />
<ProjectReference Include="..\..\..\Simulation\Simulators\Microsoft.Quantum.Simulators.csproj" />
<ProjectReference Include="..\Tools\Microsoft.Quantum.Qir.Tools.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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<string[]>(s => s.OrderBy(val => val).SequenceEqual(sourceFiles.OrderBy(val => val.FullName).Select(fileInfo => fileInfo.FullName))),
It.Is<string[]>(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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand All @@ -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<DirectoryInfo>(arg => arg.FullName == sourceDirectory.FullName), libraryDirectory, includeDirectory, linkLibraries));
executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is<DirectoryInfo>(arg => arg.FullName == sourceDirectory.FullName), new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries));
}

[Fact]
Expand Down
28 changes: 22 additions & 6 deletions src/Qir/Execution/Tools/Executable/ClangClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Quantum.Qir.Utility;

Expand All @@ -11,25 +12,29 @@ 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)
{
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<bool>();
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++",
Expand All @@ -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);
}
}
}
2 changes: 1 addition & 1 deletion src/Qir/Execution/Tools/Executable/IClangClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ namespace Microsoft.Quantum.Qir.Tools.Executable
/// </summary>
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);
}
}
7 changes: 4 additions & 3 deletions src/Qir/Execution/Tools/Executable/IQirExecutable.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,10 +14,10 @@ public interface IQirExecutable
/// Builds the executable.
/// </summary>
/// <param name="entryPoint">Entry point operation.</param>
/// <param name="libraryDirectory">Directory containing libraries to link.</param>
/// <param name="includeDirectory">Directory containing files to include.</param>
/// <param name="libraryDirectories">Directories containing libraries to link.</param>
/// <param name="includeDirectories">Directories containing files to include.</param>
/// <returns></returns>
Task BuildAsync(EntryPointOperation entryPoint, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory);
Task BuildAsync(EntryPointOperation entryPoint, IList<DirectoryInfo> libraryDirectories, IList<DirectoryInfo> includeDirectories);

/// <summary>
/// Runs the executable.
Expand Down
6 changes: 3 additions & 3 deletions src/Qir/Execution/Tools/Executable/IQirExecutableGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ internal interface IQirExecutableGenerator
/// </summary>
/// <param name="executableFile">File path to create the executable at. Dependencies will be copied to its directory.</param>
/// <param name="sourceDirectory">Location of the source files.</param>
/// <param name="libraryDirectory">Location of the libraries that must be linked.</param>
/// <param name="includeDirectory">Location of the headers that must be included.</param>
/// <param name="libraryDirectories">Locations of the libraries that must be linked.</param>
/// <param name="includeDirectories">Locations of the headers that must be included.</param>
/// <param name="linkLibraries">Libraries to link.</param>
/// <returns></returns>
public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, DirectoryInfo libraryDirectory, DirectoryInfo includeDirectory, IList<string> linkLibraries);
public Task GenerateExecutableAsync(FileInfo executableFile, DirectoryInfo sourceDirectory, IList<DirectoryInfo> libraryDirectories, IList<DirectoryInfo> includeDirectories, IList<string> linkLibraries);
}
}
4 changes: 2 additions & 2 deletions src/Qir/Execution/Tools/Executable/QirExecutable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DirectoryInfo> libraryDirectories, IList<DirectoryInfo> includeDirectories)
{
var sourceDirectory = new DirectoryInfo(SourceDirectoryPath);
if (sourceDirectory.Exists)
Expand Down Expand Up @@ -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)
Expand Down
Loading