diff --git a/src/QuantumSdk/Tools/BuildConfiguration/Generate.cs b/src/QuantumSdk/Tools/BuildConfiguration/Generate.cs index d4ab25b086..ceec4a43f3 100644 --- a/src/QuantumSdk/Tools/BuildConfiguration/Generate.cs +++ b/src/QuantumSdk/Tools/BuildConfiguration/Generate.cs @@ -2,15 +2,28 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; namespace Microsoft.Quantum.Sdk.Tools { public static partial class BuildConfiguration { + /// + /// Assembly names that are not compatible with the Q# compiler when loaded as a plugin. + /// + private static readonly IReadOnlyCollection IncompatibleQscReferences = new[] + { + // TODO: This is to work around an assembly that is included with the C# generation package, but which + // can't be loaded as a compiler reference. If the SDK gains support for packages containing a combination + // of assemblies that can be loaded as rewrite steps and those that can't, this should be removed. + // + // See: https://github.com/microsoft/qsharp-compiler/issues/435 + "Microsoft.Quantum.CsharpGeneration.EntryPointDriver.dll" + }; + /// /// Generates a suitable configuration file for the Q# compiler based on the given options. /// Encountered exceptions are logged to the console, and indicated by the returned status. @@ -24,21 +37,12 @@ public static ReturnCode Generate(Options options) "diagnostic".Equals(options.Verbosity, StringComparison.InvariantCultureIgnoreCase) || "diag".Equals(options.Verbosity, StringComparison.InvariantCultureIgnoreCase); - static (string, int) ParseQscReference(string qscRef) - { - var pieces = qscRef.Trim().TrimStart('(').TrimEnd(')').Split(','); - var path = pieces.First().Trim(); - return (path, Int32.TryParse(pieces.Skip(1).SingleOrDefault(), out var priority) ? priority : 0); - } - - var qscReferences = options.QscReferences?.ToArray() ?? new string[0]; - var orderedQscReferences = new string[0]; + IEnumerable compatibleQscReferences; + IEnumerable incompatibleQscReferences; try { - orderedQscReferences = qscReferences - .Select(ParseQscReference) - .OrderByDescending(qscRef => qscRef.Item2) - .Select(qscRef => qscRef.Item1).ToArray(); + (compatibleQscReferences, incompatibleQscReferences) = + ParseQscReferences(options.QscReferences ?? Array.Empty()); } catch (Exception ex) { @@ -49,16 +53,45 @@ public static ReturnCode Generate(Options options) return ReturnCode.INVALID_ARGUMENTS; } - return BuildConfiguration.WriteConfigFile(options.OutputFile, orderedQscReferences, verbose) + if (verbose) + { + foreach (var reference in incompatibleQscReferences) + { + Console.Error.WriteLine($"Skipped incompatible QSC reference: {reference}"); + } + } + return WriteConfigFile(options.OutputFile, compatibleQscReferences, verbose) ? ReturnCode.SUCCESS : ReturnCode.IO_EXCEPTION; } + /// + /// Parses the QSC reference strings in the format "(path, priority)" and partitions them into compatible and + /// incompatible references. + /// + /// The QSC reference strings to parse. + /// A tuple of compatible and incompatible QSC references. + private static (IEnumerable, IEnumerable) ParseQscReferences(IEnumerable qscReferences) + { + static (string, int) ParseQscReference(string qscRef) + { + var pieces = qscRef.Trim().TrimStart('(').TrimEnd(')').Split(','); + var path = pieces.First().Trim(); + return (path, int.TryParse(pieces.Skip(1).SingleOrDefault(), out var priority) ? priority : 0); + } + var compatible = qscReferences + .Select(ParseQscReference) + .OrderByDescending(qscRef => qscRef.Item2) + .Select(qscRef => qscRef.Item1) + .ToLookup(qscRef => !IncompatibleQscReferences.Contains(Path.GetFileName(qscRef))); + return (compatible[true], compatible[false]); + } + /// /// Work in progress: /// The signature and output of this method will change in the future. /// - private static bool WriteConfigFile(string configFile, string[] qscReferences, bool verbose = false) + private static bool WriteConfigFile(string configFile, IEnumerable qscReferences, bool verbose = false) { try {