diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000000..5c43e2c4de
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1-focal
+
+# Mono is required to run pack.ps1, so we install it here.
+RUN apt-get -y update && \
+ apt-get -y install dirmngr gnupg apt-transport-https ca-certificates && \
+ apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
+ sh -c 'echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" > /etc/apt/sources.list.d/mono-official-stable.list' && \
+ apt-get -y update && \
+ apt-get -y install mono-complete && \
+ apt-get clean && rm -rf /var/lib/apt/lists/
+# We can now get Mono itself.
+RUN curl -o /usr/local/bin/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe && \
+ # Create as alias for nuget
+ echo "alias nuget=\"mono /usr/local/bin/nuget.exe\"" >> /root/.bash_aliases
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644
index 0000000000..1428c4eb39
--- /dev/null
+++ b/.devcontainer/README.md
@@ -0,0 +1,21 @@
+# Remote Development Container for Visual Studio Code (preview) #
+
+This folder defines a _development container_ for the Quantum Development Kit to get up and running with your development environment for contributing to the Q# compiler.
+
+## What is a Development Container? ##
+
+Visual Studio Code allows for using [Docker](https://docs.microsoft.com/dotnet/standard/microservices-architecture/container-docker-introduction/docker-defined) to quickly define development environments, including all the compilers, command-line tools, libraries, and programming platforms you need to get up and running quickly.
+Using the definitions provided in this folder, Visual Studio Code can use Docker to automatically install the correct version of the Quantum Development Kit as well as other software you might want to use with Q#, such as Python and Jupyter Notebook --- all into an isolated container that doesn't affect the rest of the software you have on your system.
+
+Next steps:
+- [Visual Studio Code: Developing inside a container](https://code.visualstudio.com/docs/remote/containers)
+
+## Getting Started ##
+
+To use this development container, follow the installation instructions on the [Visual Studio Code site](https://code.visualstudio.com/docs/remote/containers#_installation) to prepare your machine to use development containers such as this one.
+Once you have done so, clone the [**microsoft/quantum**](https://github.com/microsoft/quantum) repository and open the folder in Visual Studio Code.
+You should be prompted to reopen the folder for remote development in the development container; if not, make sure that you have the right extension installed from above.
+
+Once you follow the prompt, Visual Studio Code will automatically configure your development container by installing the Quantum Development Kit into a new image, including installing the .NET Core SDK, project templates, Jupyter Notebook support, and the Python host package.
+This process will take a few moments, but once it's complete, you can then open a new shell as normal in Visual Studio Code; this shell will open a command line in your new development container.
+The Q# compiler will then be available in the `/workspace/qsharp-compiler/` folder of your development container, so you can easily build different parts of the compiler by using `dotnet build`.
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..499eadac33
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,13 @@
+{
+ "dockerFile": "./Dockerfile",
+ "extensions": [
+ "ms-dotnettools.csharp",
+ "Ionide.Ionide-fsharp",
+ "ms-vscode.powershell",
+ "quantum.quantum-devkit-vscode",
+ "ms-vscode.cpptools"
+ ],
+ "settings": {
+ "terminal.integrated.shell.linux": "/usr/bin/pwsh"
+ }
+}
diff --git a/QsCompiler.sln b/QsCompiler.sln
index 3a192b074e..ca25e0d9f5 100644
--- a/QsCompiler.sln
+++ b/QsCompiler.sln
@@ -1,3 +1,4 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28809.33
@@ -12,7 +13,7 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SyntaxProcessor", "src\QsCo
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TextProcessor", "src\QsCompiler\TextProcessor\TextProcessor.fsproj", "{57F1310F-9210-47A2-AEC5-DE2CF345D754}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentationParser", "src\QsCompiler\DocumentationParser\DocumentationParser.csproj", "{EB06DB1E-2B86-4279-81F9-D39B62B4AE0A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentationParser", "src\Documentation\DocumentationParser\DocumentationParser.csproj", "{EB06DB1E-2B86-4279-81F9-D39B62B4AE0A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageServer", "src\QsCompiler\LanguageServer\LanguageServer.csproj", "{D21C9875-2E84-423B-B2CA-84103661F7E4}"
EndProject
@@ -24,7 +25,7 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Core", "src\QsCompiler\Core
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Transformations", "src\QsCompiler\Transformations\Transformations.csproj", "{BA5D3733-09F1-4676-9CAC-99AD9A00AECB}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.DocGenerator", "src\QsCompiler\Tests.DocGenerator\Tests.DocGenerator.csproj", "{256A6275-FC7F-42E9-9931-BC6EA6D0F31A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.DocGenerator", "src\Documentation\Tests.DocGenerator\Tests.DocGenerator.csproj", "{256A6275-FC7F-42E9-9931-BC6EA6D0F31A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineTool", "src\QsCompiler\CommandLineTool\CommandLineTool.csproj", "{B15C4786-0D35-4E66-907F-94DA85FD5576}"
EndProject
@@ -44,7 +45,9 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Optimizations", "src\QsComp
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B4A9484D-31FC-4A27-9E26-4C8DE3E02D77}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library2", "src\QsCompiler\TestTargets\Libraries\Library2\Library2.csproj", "{E7E019AB-42F8-48AB-B80C-D33351F2C96A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library2", "src\QsCompiler\TestTargets\Libraries\Library2\Library2.csproj", "{E7E019AB-42F8-48AB-B80C-D33351F2C96A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentationGenerator", "src\Documentation\DocumentationGenerator\DocumentationGenerator.csproj", "{11311F0C-78CA-43A3-9C84-D7EA0AE9748C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -272,6 +275,18 @@ Global
{E7E019AB-42F8-48AB-B80C-D33351F2C96A}.Release|x64.Build.0 = Release|Any CPU
{E7E019AB-42F8-48AB-B80C-D33351F2C96A}.Release|x86.ActiveCfg = Release|Any CPU
{E7E019AB-42F8-48AB-B80C-D33351F2C96A}.Release|x86.Build.0 = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|x64.Build.0 = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Debug|x86.Build.0 = Debug|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|x64.ActiveCfg = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|x64.Build.0 = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|x86.ActiveCfg = Release|Any CPU
+ {11311F0C-78CA-43A3-9C84-D7EA0AE9748C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/bootstrap.ps1 b/bootstrap.ps1
index 824748cb51..6a269ed091 100644
--- a/bootstrap.ps1
+++ b/bootstrap.ps1
@@ -1,3 +1,4 @@
+[CmdletBinding()]
param(
[string]
$AssemblyVersion = $Env:ASSEMBLY_VERSION,
@@ -68,6 +69,7 @@ If ($Env:SEMVER_VERSION -eq $null) { $Env:SEMVER_VERSION ="$SemverVersion" }
If ($Env:VSVSIX_VERSION -eq $null) { $Env:VSVSIX_VERSION ="$VsVsixVersion" }
Write-Host "##vso[task.setvariable variable=VsVsix.Version]$VsVsixVersion"
+Write-Host "##[info]Finding NuSpec references..."
Push-Location (Join-Path $PSScriptRoot 'src/QsCompiler/Compiler')
.\FindNuspecReferences.ps1;
Pop-Location
diff --git a/build/build.ps1 b/build/build.ps1
index f8c54fede7..49ea9219a1 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -121,6 +121,11 @@ if ($Env:ENABLE_VSIX -ne "false") {
Write-Host "##vso[task.logissue type=warning;]VSIX building skipped due to ENABLE_VSIX variable."
}
+# NB: In other repos, we check the manifest here. That can cause problems
+# in our case, however, as some assemblies are only produced during
+# packing and publishing. Thus, as an exception, we verify the manifest
+# only in pack.ps1.
+
if (-not $all_ok) {
throw "Building failed. Check the logs."
exit 1
diff --git a/build/manifest.ps1 b/build/manifest.ps1
new file mode 100644
index 0000000000..9b2e3bb1a6
--- /dev/null
+++ b/build/manifest.ps1
@@ -0,0 +1,48 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+#!/usr/bin/env pwsh
+#Requires -PSEdition Core
+
+& "$PSScriptRoot/set-env.ps1"
+
+if ($Env:ENABLE_VSIX -ne "false") {
+ # The language server is only built if either the VS2019 or VS Code extension
+ # is enabled.
+ $VsixAssemblies = @(
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsCompilationManager.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsCore.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsDataStructures.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsLanguageServer.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsSyntaxProcessor.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsTextProcessor.dll",
+ "./src/QsCompiler/LanguageServer/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsTransformations.dll"
+ );
+
+ # The VS2019 extension itself is only built if msbuild is present.
+ if (Get-Command msbuild -ErrorAction SilentlyContinue) {
+ $VsixAssemblies += @(
+ "./src/VisualStudioExtension/QsharpVSIX/bin/$Env:BUILD_CONFIGURATION/Microsoft.Quantum.VisualStudio.Extension.dll"
+ );
+ }
+} else {
+ $VsixAssemblies = @();
+}
+
+
+@{
+ Packages = @(
+ "Microsoft.Quantum.Compiler",
+ "Microsoft.Quantum.Compiler.CommandLine",
+ "Microsoft.Quantum.ProjectTemplates",
+ "Microsoft.Quantum.Sdk",
+ "Microsoft.Quantum.DocumentationGenerator"
+ );
+ Assemblies = $VsixAssemblies + @(
+ "./src/Documentation/DocumentationGenerator/bin/$Env:BUILD_CONFIGURATION/netstandard2.1/Microsoft.Quantum.DocumentationGenerator.dll",
+ "./src/QsCompiler/CommandLineTool/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsCompiler.dll",
+ "./src/QsCompiler/CommandLineTool/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/Microsoft.Quantum.QsDocumentationParser.dll",
+ "./src/QsCompiler/CommandLineTool/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/qsc.dll",
+ "./src/QuantumSdk/Tools/BuildConfiguration/bin/$Env:BUILD_CONFIGURATION/netcoreapp3.1/publish/Microsoft.Quantum.Sdk.BuildConfiguration.dll"
+ ) | ForEach-Object { Get-Item (Join-Path $PSScriptRoot ".." $_) };
+} | Write-Output;
diff --git a/build/pack.ps1 b/build/pack.ps1
index 140cf38318..ff1cee4c6a 100644
--- a/build/pack.ps1
+++ b/build/pack.ps1
@@ -54,6 +54,30 @@ function Pack-One() {
}
}
+function Pack-Dotnet() {
+ Param($project, $option1 = "", $option2 = "", $option3 = "")
+ if ("" -ne "$Env:ASSEMBLY_CONSTANTS") {
+ $args = @("/property:DefineConstants=$Env:ASSEMBLY_CONSTANTS");
+ } else {
+ $args = @();
+ }
+ dotnet pack (Join-Path $PSScriptRoot $project) `
+ -o $Env:NUGET_OUTDIR `
+ -c $Env:BUILD_CONFIGURATION `
+ -v detailed `
+ @args `
+ /property:Version=$Env:ASSEMBLY_VERSION `
+ /property:PackageVersion=$Env:NUGET_VERSION `
+ $option1 `
+ $option2 `
+ $option3
+
+ if ($LastExitCode -ne 0) {
+ Write-Host "##vso[task.logissue type=error;]Failed to pack $project."
+ $script:all_ok = $False
+ }
+}
+
##
# Q# Language Server (self-contained)
##
@@ -213,6 +237,7 @@ Publish-One '../src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.cspro
Pack-One '../src/QsCompiler/Compiler/Compiler.csproj' '-IncludeReferencedProjects'
Pack-One '../src/QsCompiler/CommandLineTool/CommandLineTool.csproj' '-IncludeReferencedProjects'
+Pack-Dotnet '../src/Documentation/DocumentationGenerator/DocumentationGenerator.csproj'
Pack-One '../src/ProjectTemplates/Microsoft.Quantum.ProjectTemplates.nuspec'
Pack-One '../src/QuantumSdk/QuantumSdk.nuspec'
@@ -230,6 +255,18 @@ if ($Env:ENABLE_VSIX -ne "false") {
Write-Host "##vso[task.logissue type=warning;]VSIX packing skipped due to ENABLE_VSIX variable."
}
+# Copy documentation summarization tool into docs drop.
+# Note that we only copy this tool when DOCS_OUTDIR is set (that is, when we're
+# collecting docs in a build artifact).
+if ("$Env:DOCS_OUTDIR".Trim() -ne "") {
+ Push-Location (Join-Path $PSScriptRoot "../src/Documentation/Summarizer")
+ Copy-Item -Path *.py, *.txt -Destination $Env:DOCS_OUTDIR
+ Pop-Location
+}
+
+Write-Host "##[info]Verifying manifest..."
+& (Join-Path $PSScriptRoot "manifest.ps1")
+
if (-not $all_ok) {
throw "Packing failed. Check the logs."
exit 1
diff --git a/src/Documentation/DocumentationGenerator/DocumentationGeneration.cs b/src/Documentation/DocumentationGenerator/DocumentationGeneration.cs
new file mode 100644
index 0000000000..3f6a434651
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/DocumentationGeneration.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.Quantum.QsCompiler;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+
+namespace Microsoft.Quantum.Documentation
+{
+ ///
+ /// Rewrite step that generates API documentation from documentation
+ /// comments in the Q# source files being compiled.
+ ///
+ public class DocumentationGeneration : IRewriteStep
+ {
+ private readonly List diagnostics;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DocumentationGeneration()
+ {
+ this.diagnostics = new List(); // collects diagnostics that will be displayed to the user
+ }
+
+ ///
+ public string Name => "DocumentationGeneration";
+
+ ///
+ public int Priority => 0; // only compared within this dll
+
+ ///
+ public IDictionary AssemblyConstants { get; } = new Dictionary();
+
+ ///
+ public IEnumerable GeneratedDiagnostics => this.diagnostics;
+
+ ///
+ public bool ImplementsPreconditionVerification => true;
+
+ ///
+ public bool ImplementsTransformation => true;
+
+ ///
+ public bool ImplementsPostconditionVerification => false;
+
+ ///
+ public bool PreconditionVerification(QsCompilation compilation)
+ {
+ var preconditionPassed = true; // nothing to check
+ if (preconditionPassed)
+ {
+ // Diagnostics with severity Info or lower usually won't be displayed to the user.
+ // If the severity is Error or Warning the diagnostic is shown to the user like any other compiler diagnostic,
+ // and if the Source property is set to the absolute path of an existing file,
+ // the user will be directed to the file when double clicking the diagnostics.
+ this.diagnostics.Add(new IRewriteStep.Diagnostic
+ {
+ Severity = DiagnosticSeverity.Info,
+ Message = $"Precondition for {this.Name} was {(preconditionPassed ? "satisfied" : "not satisfied")}.",
+ Stage = IRewriteStep.Stage.PreconditionVerification,
+ });
+ }
+
+ return preconditionPassed;
+ }
+
+ public bool Transformation(QsCompilation compilation, out QsCompilation transformed)
+ {
+ var docProcessor = new ProcessDocComments(
+ this.AssemblyConstants.TryGetValue("DocsOutputPath", out var path)
+ ? path
+ : null,
+ this.AssemblyConstants.TryGetValue("DocsPackageId", out var packageName)
+ ? packageName
+ : null
+ );
+
+ docProcessor.OnDiagnostic += diagnostic =>
+ {
+ this.diagnostics.Add(diagnostic);
+ };
+
+ transformed = docProcessor.OnCompilation(compilation);
+ return true;
+ }
+
+ public bool PostconditionVerification(QsCompilation compilation) =>
+ throw new NotImplementedException();
+ }
+}
diff --git a/src/Documentation/DocumentationGenerator/DocumentationGenerator.csproj b/src/Documentation/DocumentationGenerator/DocumentationGenerator.csproj
new file mode 100644
index 0000000000..8b7425c589
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/DocumentationGenerator.csproj
@@ -0,0 +1,43 @@
+
+
+
+
+ netstandard2.1
+ Microsoft.Quantum.DocumentationGenerator
+ enable
+ nullable
+
+
+
+ Microsoft
+ Experimental documentation generation tool for Q#.
+ Microsoft.Quantum.DocumentationGenerator
+ See: https://docs.microsoft.com/en-us/quantum/relnotes/
+ MIT
+ https://github.com/microsoft/qsharp-compiler
+ https://secure.gravatar.com/avatar/bd1f02955b2853ba0a3b1cdc2434e8ec.png
+ Quantum Q# Qsharp
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ build\Microsoft.Quantum.DocumentationGenerator.props
+
+
+
+
+
+
+
+
diff --git a/src/Documentation/DocumentationGenerator/DocumentationGenerator.props b/src/Documentation/DocumentationGenerator/DocumentationGenerator.props
new file mode 100644
index 0000000000..d45e9d379f
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/DocumentationGenerator.props
@@ -0,0 +1,10 @@
+
+
+
+
+
+ $(MSBuildThisFileDirectory)/../lib/netstandard2.1/Microsoft.Quantum.DocumentationGenerator.dll
+
+
+
+
diff --git a/src/Documentation/DocumentationGenerator/DocumentationWriter.cs b/src/Documentation/DocumentationGenerator/DocumentationWriter.cs
new file mode 100644
index 0000000000..41593bb763
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/DocumentationWriter.cs
@@ -0,0 +1,361 @@
+// 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.CodeAnalysis;
+using Microsoft.Quantum.QsCompiler;
+using Microsoft.Quantum.QsCompiler.Documentation;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+
+namespace Microsoft.Quantum.Documentation
+{
+
+ ///
+ /// Writes API documentation files as Markdown to a given output
+ /// directory, using parsed API documentation comments.
+ ///
+ public class DocumentationWriter
+ {
+ ///
+ /// An event that is raised on diagnostics about documentation
+ /// writing (e.g., if an I/O problem prevents writing to disk).
+ ///
+ public event Action? OnDiagnostic;
+
+ ///
+ /// Path to which output documentation files should be written.
+ ///
+ public string OutputPath { get; }
+
+ private readonly string? packageName;
+
+ ///
+ /// The name of the NuGet package whose contents are being
+ /// documented, or null if the documentation being written
+ /// does not concern a particular package.
+ ///
+ public string? PackageName => this.packageName;
+
+ private readonly string packageLink;
+
+ ///
+ /// Markdown mode used to mark Q# syntax blocks.
+ ///
+ public virtual string LanguageMode => "qsharp";
+
+ private static string AsSeeAlsoLink(string target, string? currentNamespace = null)
+ {
+ var actualTarget = currentNamespace == null || target.Contains(".")
+ ? target
+ : $"{currentNamespace}.{target}";
+ return $"- [{actualTarget}](xref:{actualTarget})";
+ }
+
+ private async Task TryWithExceptionsAsDiagnostics(string description, Func action, DiagnosticSeverity severity = DiagnosticSeverity.Warning)
+ {
+ try
+ {
+ await action();
+ }
+ catch (Exception ex)
+ {
+ this.OnDiagnostic?.Invoke(new IRewriteStep.Diagnostic
+ {
+ Severity = severity,
+ Message = $"Exception raised when {description}:\n{ex.Message}",
+ Range = null,
+ Source = null,
+ Stage = IRewriteStep.Stage.Transformation,
+ });
+ }
+ }
+
+ private async Task WriteAllTextAsync(string filename, string contents)
+ {
+ await this.TryWithExceptionsAsDiagnostics(
+ $"writing output to {filename}",
+ async () => await File.WriteAllTextAsync(
+ Path.Join(this.OutputPath, filename.ToLowerInvariant()),
+ contents
+ )
+ );
+ }
+
+ ///
+ /// Initializes a new instance of the
+ /// class.
+ ///
+ ///
+ /// The path to which documentation files should be written.
+ ///
+ ///
+ /// The name of the NuGet package being documented, or null
+ /// if the documentation to be written by this object does not
+ /// relate to a particular package.
+ ///
+ public DocumentationWriter(string outputPath, string? packageName)
+ {
+ this.OutputPath = outputPath;
+ this.packageName = packageName;
+
+ // If the output path is not null, make sure the directory exists.
+ if (outputPath != null)
+ {
+ this.OnDiagnostic?.Invoke(new IRewriteStep.Diagnostic
+ {
+ Severity = CodeAnalysis.DiagnosticSeverity.Info,
+ Message = $"Writing documentation output to: {outputPath}...",
+ Range = null,
+ Source = null,
+ Stage = IRewriteStep.Stage.Transformation,
+ });
+ if (!Directory.Exists(outputPath))
+ {
+ this.TryWithExceptionsAsDiagnostics(
+ "creating directory",
+ async () => Directory.CreateDirectory(outputPath)
+ ).Wait();
+ }
+ }
+
+ this.packageLink = this.PackageName == null
+ ? string.Empty
+ : $"\nPackage: [{this.PackageName}](https://nuget.org/packages/{this.PackageName})\n";
+ }
+
+ ///
+ /// Given a documentation comment describing a Q# namespace,
+ /// writes a Markdown file documenting that namespace to
+ /// .
+ ///
+ /// The Q# namespace being documented.
+ ///
+ /// The API documentation comment describing .
+ ///
+ /// A representing the result of the asynchronous operation.
+ public async Task WriteOutput(QsNamespace ns, DocComment docComment)
+ {
+ var name = ns.Name.Value;
+ var uid = name;
+ var title = $"{name} namespace";
+ var header = new Dictionary
+ {
+ // DocFX metadata
+ ["uid"] = name,
+ ["title"] = title,
+
+ // docs.ms metadata
+ ["ms.date"] = DateTime.Today.ToString(),
+ ["ms.topic"] = "article",
+
+ // Q# metadata
+ ["qsharp.kind"] = "namespace",
+ ["qsharp.name"] = name,
+ ["qsharp.summary"] = docComment.Summary,
+ };
+ var document = $@"
+# {title}
+
+{docComment.Summary}
+
+"
+ .MaybeWithSection("Description", docComment.Description)
+ .MaybeWithSection(
+ "See Also",
+ string.Join("\n", docComment.SeeAlso.Select(
+ seeAlso => AsSeeAlsoLink(seeAlso)
+ ))
+ )
+ .WithYamlHeader(header);
+
+ // Open a file to write the new doc to.
+ await this.WriteAllTextAsync(
+ $"{name}.md", document
+ );
+ }
+
+ ///
+ /// Given a documentation comment describing a Q# user-defined type
+ /// declaration, writes a Markdown file documenting that UDT
+ /// declaration to .
+ ///
+ /// The Q# UDT being documented.
+ ///
+ /// The API documentation comment describing .
+ ///
+ /// A representing the result of the asynchronous operation.
+ public async Task WriteOutput(QsCustomType type, DocComment docComment)
+ {
+ var namedItemDeclarations = type.TypeItems.ToDictionaryOfDeclarations();
+
+ // Make a new Markdown document for the type declaration.
+ var title = $"{type.FullName.Name.Value} user defined type";
+ var header = new Dictionary
+ {
+ // DocFX metadata
+ ["uid"] = type.FullName.ToString(),
+ ["title"] = title,
+
+ // docs.ms metadata
+ ["ms.date"] = DateTime.Today.ToString(),
+ ["ms.topic"] = "article",
+
+ // Q# metadata
+ ["qsharp.kind"] = "udt",
+ ["qsharp.namespace"] = type.FullName.Namespace.Value,
+ ["qsharp.name"] = type.FullName.Name.Value,
+ ["qsharp.summary"] = docComment.Summary,
+ };
+ var document = $@"
+# {title}
+
+Namespace: [{type.FullName.Namespace.Value}](xref:{type.FullName.Namespace.Value})
+{this.packageLink}
+
+{docComment.Summary}
+
+```{this.LanguageMode}
+{type.WithoutDocumentationAndComments().ToSyntax()}
+```
+
+"
+ .MaybeWithSection(
+ "Named Items",
+ string.Join("\n", type.TypeItems.TypeDeclarations().Select(
+ item =>
+ {
+ (var itemName, var resolvedType) = item;
+ var documentation =
+ docComment.NamedItems.TryGetValue(itemName, out var comment)
+ ? comment
+ : string.Empty;
+ return $"### {itemName} : {resolvedType.ToMarkdownLink()}\n\n{documentation}";
+ }
+ ))
+ )
+ .MaybeWithSection("Description", docComment.Description)
+ .MaybeWithSection("Remarks", docComment.Remarks)
+ .MaybeWithSection("References", docComment.References)
+ .MaybeWithSection(
+ "See Also",
+ string.Join("\n", docComment.SeeAlso.Select(
+ seeAlso => AsSeeAlsoLink(seeAlso, type.FullName.Namespace.Value)
+ ))
+ )
+ .WithYamlHeader(header);
+
+ // Open a file to write the new doc to.
+ await this.WriteAllTextAsync(
+ $"{type.FullName.Namespace.Value}.{type.FullName.Name.Value}.md",
+ document
+ );
+ }
+
+ ///
+ /// Given a documentation comment describing a Q# function or operation
+ /// declaration, writes a Markdown file documenting that callable
+ /// declaration to .
+ ///
+ /// The Q# callable being documented.
+ ///
+ /// The API documentation comment describing .
+ ///
+ /// A representing the result of the asynchronous operation.
+ public async Task WriteOutput(QsCallable callable, DocComment docComment)
+ {
+ // Make a new Markdown document for the type declaration.
+ var kind = callable.Kind.Tag switch
+ {
+ QsCallableKind.Tags.Function => "function",
+ QsCallableKind.Tags.Operation => "operation",
+ QsCallableKind.Tags.TypeConstructor => "type constructor",
+ _ => "",
+ };
+ var title = $@"{callable.FullName.Name.Value} {kind}";
+ var header = new Dictionary
+ {
+ // DocFX metadata
+ ["uid"] = callable.FullName.ToString(),
+ ["title"] = title,
+
+ // docs.ms metadata
+ ["ms.date"] = DateTime.Today.ToString(),
+ ["ms.topic"] = "article",
+
+ // Q# metadata
+ ["qsharp.kind"] = kind,
+ ["qsharp.namespace"] = callable.FullName.Namespace.Value,
+ ["qsharp.name"] = callable.FullName.Name.Value,
+ ["qsharp.summary"] = docComment.Summary,
+ };
+ var document = $@"
+# {title}
+
+Namespace: [{callable.FullName.Namespace.Value}](xref:{callable.FullName.Namespace.Value})
+{this.packageLink}
+
+{docComment.Summary}
+
+```{this.LanguageMode}
+{
+ callable.Kind.Tag switch
+ {
+ QsCallableKind.Tags.Function => "function ",
+ QsCallableKind.Tags.Operation => "operation ",
+ QsCallableKind.Tags.TypeConstructor => "newtype ",
+ _ => ""
+ }
+}{callable.ToSyntax()}
+```
+"
+ .MaybeWithSection("Description", docComment.Description)
+ .MaybeWithSection(
+ "Input",
+ string.Join("\n", callable.ArgumentTuple.InputDeclarations().Select(
+ (item) =>
+ {
+ (var inputName, var resolvedType) = item;
+ var documentation = docComment.Input.TryGetValue(inputName, out var inputComment)
+ ? inputComment
+ : string.Empty;
+ return $"### {inputName} : {resolvedType.ToMarkdownLink()}\n\n{documentation}\n\n";
+ }
+ ))
+ )
+ .WithSection($"Output : {callable.Signature.ReturnType.ToMarkdownLink()}", docComment.Output)
+ .MaybeWithSection(
+ "Type Parameters",
+ string.Join("\n", callable.Signature.TypeParameters.Select(
+ typeParam => $@"### {typeParam}\n\n{(
+ typeParam is QsLocalSymbol.ValidName name
+ ? docComment.TypeParameters.TryGetValue(name.Item.Value, out var comment)
+ ? comment
+ : string.Empty
+ : string.Empty
+ )}"
+ ))
+ )
+ .MaybeWithSection("Remarks", docComment.Remarks)
+ .MaybeWithSection("References", docComment.References)
+ .MaybeWithSection(
+ "See Also",
+ string.Join("\n", docComment.SeeAlso.Select(
+ seeAlso => AsSeeAlsoLink(seeAlso, callable.FullName.Namespace.Value)
+ ))
+ )
+ .WithYamlHeader(header);
+
+ // Open a file to write the new doc to.
+ await this.WriteAllTextAsync(
+ $"{callable.FullName.Namespace.Value}.{callable.FullName.Name.Value}.md",
+ document
+ );
+ }
+
+ }
+
+}
diff --git a/src/Documentation/DocumentationGenerator/Extensions.cs b/src/Documentation/DocumentationGenerator/Extensions.cs
new file mode 100644
index 0000000000..3dad3758a5
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/Extensions.cs
@@ -0,0 +1,345 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.Quantum.QsCompiler;
+using Microsoft.Quantum.QsCompiler.DataTypes;
+using Microsoft.Quantum.QsCompiler.SyntaxTokens;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+using Microsoft.Quantum.QsCompiler.Transformations;
+using Microsoft.Quantum.QsCompiler.Transformations.QsCodeOutput;
+using YamlDotNet.Serialization;
+using Range = Microsoft.Quantum.QsCompiler.DataTypes.Range;
+using ResolvedTypeKind = Microsoft.Quantum.QsCompiler.SyntaxTokens.QsTypeKind<
+ Microsoft.Quantum.QsCompiler.SyntaxTree.ResolvedType,
+ Microsoft.Quantum.QsCompiler.SyntaxTree.UserDefinedType,
+ Microsoft.Quantum.QsCompiler.SyntaxTree.QsTypeParameter,
+ Microsoft.Quantum.QsCompiler.SyntaxTree.CallableInformation
+>;
+
+#nullable enable
+
+namespace Microsoft.Quantum.Documentation
+{
+ // The next several types allow for adding attributes to
+ // callables and UDTs with a single API, so that the extension
+ // methods in this file can be written once for both kinds of
+ // AST items.
+ internal interface IAttributeBuilder
+ {
+ public IAttributeBuilder AddAttribute(QsDeclarationAttribute attribute);
+
+ public T Build();
+ }
+
+ internal class Callable : IAttributeBuilder
+ {
+ private readonly QsCallable callable;
+
+ internal Callable(QsCallable callable)
+ {
+ this.callable = callable;
+ }
+
+ public IAttributeBuilder AddAttribute(QsDeclarationAttribute attribute) =>
+ new Callable(callable.AddAttribute(attribute));
+
+ public QsCallable Build() => callable;
+ }
+
+ internal class Udt : IAttributeBuilder
+ {
+ private QsCustomType type;
+
+ internal Udt(QsCustomType type)
+ {
+ this.type = type;
+ }
+
+ public IAttributeBuilder AddAttribute(QsDeclarationAttribute attribute) =>
+ new Udt(type.AddAttribute(attribute));
+
+ public QsCustomType Build() => type;
+ }
+
+ internal static class Extensions
+ {
+ internal static IAttributeBuilder AttributeBuilder(
+ this QsCallable callable
+ ) => new Callable(callable);
+
+ internal static IAttributeBuilder AttributeBuilder(
+ this QsCustomType type
+ ) => new Udt(type);
+
+ ///
+ /// Given an attribute builder, returns a new copy of that builder
+ /// with one additional attribute.
+ ///
+ /// The type of AST item decorated by the attribute builder.
+ /// The attribute builder to which the new attribute should be added.
+ /// The Q# namespace containing the new attribute.
+ /// The name of the Q# UDT for the new attribute.
+ /// The input to the Q# UDT's type constructor function for the given attribute.
+ /// A new attribute builder with the new attribute added.
+ internal static IAttributeBuilder WithAttribute(
+ this IAttributeBuilder builder, string @namespace, string name,
+ TypedExpression input
+ ) =>
+ builder.AddAttribute(
+ AttributeUtils.BuildAttribute(
+ new QsQualifiedName(
+ NonNullable.New(@namespace),
+ NonNullable.New(name)
+ ),
+ input
+ )
+ );
+
+ private static IAttributeBuilder WithDocumentationAttribute(
+ this IAttributeBuilder builder, string attributeName,
+ TypedExpression input
+ ) => builder.WithAttribute("Microsoft.Quantum.Documentation", attributeName, input);
+
+ private static TypedExpression AsLiteralExpression(this string literal) =>
+ SyntaxGenerator.StringLiteral(
+ NonNullable.New(literal),
+ ImmutableArray.Empty
+ );
+
+ ///
+ /// Given an attribute builder, either returns it unmodified,
+ /// or returns a new copy of the attribute builder with a new
+ /// string-valued documentation attribute added.
+ ///
+ /// The type of AST item decorated by the attribute builder.
+ /// The attribute builder to which the new attribute should be added.
+ /// The name of the Q# UDT for the new attribute.
+ /// The value of the new attribute to be added, or null if no attribute is to be added.
+ /// A new attribute builder with the new attribute added, or if is null.
+ internal static IAttributeBuilder MaybeWithSimpleDocumentationAttribute(
+ this IAttributeBuilder builder, string attributeName, string? value
+ ) =>
+ value == null || value.Trim().Length == 0
+ ? builder
+ : builder.WithDocumentationAttribute(
+ attributeName, value.AsLiteralExpression()
+ );
+
+ ///
+ /// Given an attribute builder, returns a new attribute builder with
+ /// an attribute added for each string in a given collection.
+ ///
+ /// The type of AST item decorated by the attribute builder.
+ /// The attribute builder to which the new attributes should be added.
+ /// The name of the Q# UDT for the new attributes.
+ /// The values of the new attributes to be added.
+ /// A new attribute builder with the one new attribute added for each element of .
+ internal static IAttributeBuilder WithListOfDocumentationAttributes(
+ this IAttributeBuilder builder, string attributeName, IEnumerable items
+ ) =>
+ items
+ .Aggregate(
+ builder,
+ (acc, item) => acc.WithDocumentationAttribute(
+ attributeName, item.AsLiteralExpression()
+ )
+ );
+
+ internal static IAttributeBuilder WithDocumentationAttributesFromDictionary(
+ this IAttributeBuilder builder, string attributeName, IDictionary items
+ ) =>
+ items
+ .Aggregate(
+ builder,
+ (acc, item) => acc.WithDocumentationAttribute(
+ attributeName,
+ // The following populates all of the metadata needed for a
+ // Q# literal of type (String, String).
+ SyntaxGenerator.TupleLiteral(
+ ImmutableArray.Create(
+ item.Key.AsLiteralExpression(),
+ item.Value.AsLiteralExpression()
+ )
+ )
+ )
+ );
+
+ internal static string ToSyntax(this QsCustomType type) =>
+ SyntaxTreeToQsharp.Default.ToCode(type);
+
+ internal static string ToSyntax(this ResolvedCharacteristics characteristics) =>
+ SyntaxTreeToQsharp.CharacteristicsExpression(characteristics)
+ // CharacteristicsExpression returns null when the characteristics
+ // are an empty set; in that case, we want an empty string.
+ ?? string.Empty;
+
+ internal static string ToSyntax(this QsCallable callable, Action? onDiagnostic = null) =>
+ SyntaxTreeToQsharp.DeclarationSignature(
+ callable, SyntaxTreeToQsharp.Default.ToCode
+ );
+
+ internal static string MaybeWithSection(this string document, string name, string? contents) =>
+ contents == null || contents.Trim().Length == 0
+ ? document
+ : document.WithSection(name, contents);
+
+ internal static string WithSection(this string document, string name, string contents) =>
+ $"{document}\n\n## {name}\n\n{contents}";
+
+ internal static string WithYamlHeader(this string document, object header) =>
+ $"---\n{new SerializerBuilder().Build().Serialize(header)}---\n{document}";
+
+ internal static bool IsDeprecated(this QsCallable callable, out string? replacement)
+ {
+ var redirect = SymbolResolution.TryFindRedirect(callable.Attributes);
+ if (redirect.IsNull)
+ {
+ replacement = null;
+ return false;
+ }
+ else
+ {
+ replacement = redirect.Item;
+ return true;
+ }
+ }
+
+ internal static bool IsDeprecated(this QsCustomType type, [NotNullWhen(true)] out string? replacement)
+ {
+ var redirect = SymbolResolution.TryFindRedirect(type.Attributes);
+ if (redirect.IsNull)
+ {
+ replacement = null;
+ return false;
+ }
+ else
+ {
+ replacement = redirect.Item;
+ return true;
+ }
+ }
+
+ internal static Dictionary ToDictionaryOfDeclarations(this QsTuple> items) =>
+ items.InputDeclarations().ToDictionary(
+ declaration => declaration.Item1,
+ declaration => declaration.Item2
+ );
+
+ internal static Dictionary ToDictionaryOfDeclarations(this QsTuple typeItems) =>
+ typeItems.TypeDeclarations().ToDictionary(
+ declaration => declaration.Item1,
+ declaration => declaration.Item2
+ );
+
+ internal static List<(string, ResolvedType)> TypeDeclarations(this QsTuple typeItems) => typeItems switch
+ {
+ QsTuple.QsTuple tuple =>
+ tuple.Item.SelectMany(
+ item => item.TypeDeclarations()
+ )
+ .ToList(),
+ QsTuple.QsTupleItem item => item.Item switch
+ {
+ QsTypeItem.Anonymous _ => new List<(string, ResolvedType)>(),
+ QsTypeItem.Named named =>
+ new List<(string, ResolvedType)>
+ {(
+ named.Item.VariableName.Value,
+ named.Item.Type
+ )},
+ _ => throw new ArgumentException($"Type item {item} is neither anonymous nor named.", nameof(typeItems)),
+ },
+ _ => throw new ArgumentException($"Type items {typeItems} aren't a tuple of type items.", nameof(typeItems)),
+ };
+
+ internal static List<(string, ResolvedType)> InputDeclarations(this QsTuple> items) => items switch
+ {
+ QsTuple>.QsTuple tuple =>
+ tuple.Item.SelectMany(
+ item => item.InputDeclarations()
+ )
+ .ToList(),
+ QsTuple>.QsTupleItem item =>
+ new List<(string, ResolvedType)>
+ {
+ (
+ item.Item.VariableName switch
+ {
+ QsLocalSymbol.ValidName name => name.Item.Value,
+ _ => "__invalid__",
+ },
+ item.Item.Type
+ )
+ },
+ _ => throw new Exception()
+ };
+
+ internal static string ToMarkdownLink(this ResolvedType type) => type.Resolution switch
+ {
+ ResolvedTypeKind.ArrayType array => $"{array.Item.ToMarkdownLink()}[]",
+ ResolvedTypeKind.Function function =>
+ $"{function.Item1.ToMarkdownLink()} -> {function.Item2.ToMarkdownLink()}",
+ ResolvedTypeKind.Operation operation =>
+ $@"{operation.Item1.Item1.ToMarkdownLink()} => {operation.Item1.Item2.ToMarkdownLink()} {
+ operation.Item2.Characteristics.ToSyntax()
+ }",
+ ResolvedTypeKind.TupleType tuple => "(" + string.Join(",",
+ tuple.Item.Select(ToMarkdownLink)
+ ) + ")",
+ ResolvedTypeKind.UserDefinedType udt => udt.Item.ToMarkdownLink(),
+ ResolvedTypeKind.TypeParameter typeParam =>
+ $"'{typeParam.Item.TypeName.Value}",
+ _ => type.Resolution.Tag switch
+ {
+ ResolvedTypeKind.Tags.BigInt => "[BigInt](xref:microsoft.quantum.lang-ref.bigint)",
+ ResolvedTypeKind.Tags.Bool => "[Bool](xref:microsoft.quantum.lang-ref.bool)",
+ ResolvedTypeKind.Tags.Double => "[Double](xref:microsoft.quantum.lang-ref.double)",
+ ResolvedTypeKind.Tags.Int => "[Int](xref:microsoft.quantum.lang-ref.int)",
+ ResolvedTypeKind.Tags.Pauli => "[Pauli](xref:microsoft.quantum.lang-ref.pauli)",
+ ResolvedTypeKind.Tags.Qubit => "[Qubit](xref:microsoft.quantum.lang-ref.qubit)",
+ ResolvedTypeKind.Tags.Range => "[Range](xref:microsoft.quantum.lang-ref.range)",
+ ResolvedTypeKind.Tags.String => "[String](xref:microsoft.quantum.lang-ref.string)",
+ ResolvedTypeKind.Tags.UnitType => "[Unit](xref:microsoft.quantum.lang-ref.unit)",
+ ResolvedTypeKind.Tags.InvalidType => "__invalid__",
+ _ => $"__invalid<{type.Resolution.ToString()}>__",
+ },
+ };
+
+ internal static string ToMarkdownLink(this UserDefinedType type) =>
+ $"[{type.Name.Value}](xref:{type.Namespace.Value}.{type.Name.Value})";
+
+ internal static bool IsInCompilationUnit(this QsNamespaceElement element) =>
+ element switch
+ {
+ QsNamespaceElement.QsCallable callable => callable.Item.IsInCompilationUnit(),
+ QsNamespaceElement.QsCustomType type => type.Item.IsInCompilationUnit(),
+ _ => false,
+ };
+
+ internal static bool IsInCompilationUnit(this QsCallable callable) =>
+ callable.SourceFile.Value.EndsWith(".qs");
+
+ internal static bool IsInCompilationUnit(this QsCustomType type) =>
+ type.SourceFile.Value.EndsWith(".qs");
+
+ internal static QsCustomType WithoutDocumentationAndComments(this QsCustomType type) =>
+ new QsCustomType(
+ fullName: type.FullName,
+ attributes: type.Attributes,
+ modifiers: type.Modifiers,
+ sourceFile: type.SourceFile,
+ location: type.Location,
+ type: type.Type,
+ typeItems: type.TypeItems,
+ documentation: ImmutableArray.Empty,
+ comments: QsComments.Empty
+ );
+ }
+
+}
diff --git a/src/Documentation/DocumentationGenerator/ProcessDocComments.cs b/src/Documentation/DocumentationGenerator/ProcessDocComments.cs
new file mode 100644
index 0000000000..d493c12ac1
--- /dev/null
+++ b/src/Documentation/DocumentationGenerator/ProcessDocComments.cs
@@ -0,0 +1,228 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Quantum.QsCompiler;
+using Microsoft.Quantum.QsCompiler.DataTypes;
+using Microsoft.Quantum.QsCompiler.Documentation;
+using Microsoft.Quantum.QsCompiler.SyntaxTokens;
+using Microsoft.Quantum.QsCompiler.SyntaxTree;
+using Microsoft.Quantum.QsCompiler.Transformations.Core;
+using Newtonsoft.Json.Linq;
+
+using Range = Microsoft.Quantum.QsCompiler.DataTypes.Range;
+
+#nullable enable
+
+namespace Microsoft.Quantum.Documentation
+{
+ ///
+ /// A syntax tree transformation that parses documentation comments,
+ /// saving documentation content back to the syntax tree as attributes,
+ /// and writing formatted documentation content out to Markdown files.
+ ///
+ public class ProcessDocComments
+ : SyntaxTreeTransformation
+ {
+ public class TransformationState
+ { }
+
+ ///
+ /// An event that is raised on diagnostics about documentation
+ /// writing (e.g., if an I/O problem prevents writing to disk).
+ ///
+ public event Action? OnDiagnostic;
+
+ internal readonly DocumentationWriter? Writer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The path to which documentation files should be written.
+ ///
+ ///
+ /// The name of the NuGet package being documented, or null
+ /// if the documentation to be written by this object does not
+ /// relate to a particular package.
+ ///
+ public ProcessDocComments(
+ string? outputPath = null,
+ string? packageName = null
+ )
+ : base(new TransformationState(), TransformationOptions.Disabled)
+ {
+ this.Writer = outputPath == null
+ ? null
+ : new DocumentationWriter(outputPath, packageName);
+
+ if (this.Writer != null)
+ {
+ this.Writer.OnDiagnostic += diagnostic =>
+ this.OnDiagnostic?.Invoke(diagnostic);
+ }
+
+ // We provide our own custom namespace transformation, and expression kind transformation.
+ this.Namespaces = new ProcessDocComments.NamespaceTransformation(this, this.Writer);
+ }
+
+ private class NamespaceTransformation
+ : NamespaceTransformation
+ {
+ private DocumentationWriter? writer;
+
+ internal NamespaceTransformation(ProcessDocComments parent, DocumentationWriter? writer)
+ : base(parent)
+ { this.writer = writer; }
+
+ private void ValidateNames(
+ string symbolName,
+ string nameKind,
+ Func isNameValid,
+ IEnumerable actualNames,
+ Range? range = null,
+ string? source = null
+ )
+ {
+ foreach (var name in actualNames)
+ {
+ if (!isNameValid(name))
+ {
+ (this.Transformation as ProcessDocComments)?.OnDiagnostic?.Invoke(
+ new IRewriteStep.Diagnostic
+ {
+ Message = $"When documenting {symbolName}, found documentation for {nameKind} {name}, but no such {nameKind} exists.",
+ Severity = CodeAnalysis.DiagnosticSeverity.Warning,
+ Range = range,
+ Source = source,
+ Stage = IRewriteStep.Stage.Transformation,
+ }
+ );
+ }
+ }
+ }
+
+ public override QsNamespace OnNamespace(QsNamespace ns)
+ {
+ ns = base.OnNamespace(ns);
+ if (ns.Elements.Any(element => element.IsInCompilationUnit()))
+ {
+ // Concatenate everything into one documentation comment.
+ var comment = new DocComment(
+ ns.Documentation.SelectMany(group => group).SelectMany(comments => comments)
+ );
+ writer?.WriteOutput(ns, comment)?.Wait();
+ }
+
+ return ns;
+ }
+
+ public override QsCustomType OnTypeDeclaration(QsCustomType type)
+ {
+ type = base.OnTypeDeclaration(type);
+ // If the UDT didn't come from a Q# source file, then it
+ // came in from a reference, and shouldn't be documented in this
+ // project.
+ if (!type.IsInCompilationUnit())
+ {
+ return type;
+ }
+
+ var isDeprecated = type.IsDeprecated(out var replacement);
+ var docComment = new DocComment(
+ type.Documentation, type.FullName.Name.Value,
+ deprecated: isDeprecated,
+ replacement: replacement
+ );
+
+ // Validate named item names.
+ var inputDeclarations = type.TypeItems.ToDictionaryOfDeclarations();
+ this.ValidateNames(
+ $"{type.FullName.Namespace.Value}.{type.FullName.Name.Value}",
+ "named item",
+ name => inputDeclarations.ContainsKey(name),
+ docComment.Input.Keys,
+ range: null, // TODO: provide more exact locations once supported by DocParser.
+ source: type.SourceFile.Value
+ );
+
+ this.writer?.WriteOutput(type, docComment)?.Wait();
+
+ return type
+ .AttributeBuilder()
+ .MaybeWithSimpleDocumentationAttribute("Summary", docComment.Summary)
+ .MaybeWithSimpleDocumentationAttribute("Description", docComment.Description)
+ .MaybeWithSimpleDocumentationAttribute("Remarks", docComment.Remarks)
+ .MaybeWithSimpleDocumentationAttribute("References", docComment.References)
+ .WithListOfDocumentationAttributes("SeeAlso", docComment.SeeAlso)
+ .WithListOfDocumentationAttributes("Example", docComment.Examples)
+ .WithDocumentationAttributesFromDictionary("NamedItem", docComment.NamedItems)
+ .Build();
+ }
+
+ public override QsCallable OnCallableDeclaration(QsCallable callable)
+ {
+ callable = base.OnCallableDeclaration(callable);
+ // If the callable didn't come from a Q# source file, then it
+ // came in from a reference, and shouldn't be documented in this
+ // project.
+ if (!callable.IsInCompilationUnit())
+ {
+ return callable;
+ }
+
+ var isDeprecated = callable.IsDeprecated(out var replacement);
+ var docComment = new DocComment(
+ callable.Documentation, callable.FullName.Name.Value,
+ deprecated: isDeprecated,
+ replacement: replacement
+ );
+ var callableName =
+ $"{callable.FullName.Namespace.Value}.{callable.FullName.Name.Value}";
+
+ // Validate input and type parameter names.
+ var inputDeclarations = callable.ArgumentTuple.ToDictionaryOfDeclarations();
+ this.ValidateNames(
+ callableName,
+ "input",
+ name => inputDeclarations.ContainsKey(name),
+ docComment.Input.Keys,
+ range: null, // TODO: provide more exact locations once supported by DocParser.
+ source: callable.SourceFile.Value
+ );
+ this.ValidateNames(
+ callableName,
+ "type parameter",
+ name => callable.Signature.TypeParameters.Any(
+ typeParam =>
+ typeParam is QsLocalSymbol.ValidName validName &&
+ validName.Item.Value == name.TrimStart('\'')
+ ),
+ docComment.TypeParameters.Keys,
+ range: null, // TODO: provide more exact locations once supported by DocParser.
+ source: callable.SourceFile.Value
+ );
+
+ this.writer?.WriteOutput(callable, docComment)?.Wait();
+
+ return callable
+ .AttributeBuilder()
+ .MaybeWithSimpleDocumentationAttribute("Summary", docComment.Summary)
+ .MaybeWithSimpleDocumentationAttribute("Description", docComment.Description)
+ .MaybeWithSimpleDocumentationAttribute("Remarks", docComment.Remarks)
+ .MaybeWithSimpleDocumentationAttribute("References", docComment.References)
+ .WithListOfDocumentationAttributes("SeeAlso", docComment.SeeAlso)
+ .WithListOfDocumentationAttributes("Example", docComment.Examples)
+ .WithDocumentationAttributesFromDictionary("Input", docComment.Input)
+ .MaybeWithSimpleDocumentationAttribute("Output", docComment.Output)
+ .WithDocumentationAttributesFromDictionary("TypeParameter", docComment.TypeParameters)
+ .Build();
+ }
+ }
+ }
+}
diff --git a/src/QsCompiler/DocumentationParser/DocBuilder.cs b/src/Documentation/DocumentationParser/DocBuilder.cs
similarity index 97%
rename from src/QsCompiler/DocumentationParser/DocBuilder.cs
rename to src/Documentation/DocumentationParser/DocBuilder.cs
index e303619b87..ffd70fad2c 100644
--- a/src/QsCompiler/DocumentationParser/DocBuilder.cs
+++ b/src/Documentation/DocumentationParser/DocBuilder.cs
@@ -15,6 +15,7 @@ namespace Microsoft.Quantum.QsCompiler.Documentation
/// This class contains the functionality for writing all of the documentation
/// YAML files for an entire compilation.
///
+ [Obsolete("The YAML-based documentation builder has been replaced by the Markdown-based DocumentationGenerator.")]
public class DocBuilder
{
private readonly string rootDocPath;
diff --git a/src/QsCompiler/DocumentationParser/DocCallable.cs b/src/Documentation/DocumentationParser/DocCallable.cs
similarity index 98%
rename from src/QsCompiler/DocumentationParser/DocCallable.cs
rename to src/Documentation/DocumentationParser/DocCallable.cs
index 14e8944ddc..739d4682c4 100644
--- a/src/QsCompiler/DocumentationParser/DocCallable.cs
+++ b/src/Documentation/DocumentationParser/DocCallable.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
@@ -57,6 +58,7 @@ internal DocCallable(string ns, QsCallable callableObj)
/// Writes a YAML representation of this callable to a text writer.
///
/// The writer to output to
+ [Obsolete("Writing YAML documentation is no longer supported.")]
internal override void WriteToFile(TextWriter text)
{
YamlNode BuildInputNode()
@@ -109,7 +111,7 @@ YamlNode BuildOutputNode()
rootNode.AddStringMapping(Utils.UidKey, this.uid);
rootNode.AddStringMapping(Utils.NameKey, this.name);
rootNode.AddStringMapping(Utils.TypeKey, this.itemType);
- rootNode.AddStringMapping(Utils.NamespaceKey, this.namespaceName.NamespaceAsUid());
+ rootNode.AddStringMapping(Utils.NamespaceKey, this.namespaceName.AsObsoleteUid());
if (!string.IsNullOrWhiteSpace(this.comments.Documentation))
{
rootNode.AddStringMapping(Utils.SummaryKey, this.comments.Documentation);
diff --git a/src/QsCompiler/DocumentationParser/DocComment.cs b/src/Documentation/DocumentationParser/DocComment.cs
similarity index 85%
rename from src/QsCompiler/DocumentationParser/DocComment.cs
rename to src/Documentation/DocumentationParser/DocComment.cs
index 7fc0cb7ae4..cb7c442d63 100644
--- a/src/QsCompiler/DocumentationParser/DocComment.cs
+++ b/src/Documentation/DocumentationParser/DocComment.cs
@@ -1,8 +1,12 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+#nullable enable
+
using System;
+using System.Linq;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
@@ -23,20 +27,20 @@ public class DocComment
/// The summary description of the item.
/// This should be one paragraph of plain text.
///
- public string Summary { get; }
+ public string Summary { get; } = "";
///
/// The (rest of the) full description of the item.
/// This should not duplicate the summary, but rather follow it.
///
- public string Description { get; }
+ public string Description { get; } = "";
///
/// The short hover information for the item.
/// This should be one paragraph of plain text.
/// Currently this is the first paragraph of the summary field.
///
- public string ShortSummary { get; }
+ public string ShortSummary { get; } = "";
///
/// The full markdown-formatted hover information for the item.
@@ -48,55 +52,80 @@ public class DocComment
/// The full markdown-formatted on-line documentation for the item.
/// Currently this consists of the summary field followed by the description field.
///
- public string Documentation { get; }
+ public string Documentation { get; } = "";
///
/// The inputs to the item, as a list of symbol/description pairs.
/// This is only populated for functions and operations.
///
public Dictionary Input { get; }
+ = new Dictionary();
///
/// The output from the item.
/// This is only populated for functions and operations.
///
- public string Output { get; }
+ public string Output { get; } = "";
///
/// The type parameters for the item, as a list of symbol/description pairs.
/// This is only populated for functions and operations.
///
- public Dictionary TypeParameters { get; }
+ public Dictionary TypeParameters { get; } =
+ new Dictionary();
+
+ ///
+ /// Descriptions of each named item for the item being documented,
+ /// as a dictionary from identifiers for each named item to the
+ /// corresponding description.
+ ///
+ ///
+ /// Only applicable when the item being documented is a UDT.
+ ///
+ public Dictionary NamedItems { get; } =
+ new Dictionary();
///
- /// An example of using the item.
+ /// All examples of using the named item, concatenated as a single Markdown
+ /// document.
///
- public string Example { get; }
+ [Obsolete("Please use Examples instead.")]
+ public string Example => string.Join(
+ "\n\n",
+ this.Examples
+ );
+
+ ///
+ /// A list of examples of using the item.
+ ///
+ public ImmutableList Examples { get; } = ImmutableList.Empty;
///
/// Additional commentary about the item.
///
- public string Remarks { get; }
+ public string Remarks { get; } = "";
///
/// A list of links to other documentation related to this item.
///
- public List SeeAlso { get; }
+ public List SeeAlso { get; } =
+ new List();
///
/// Reference material about the item.
///
- public string References { get; }
+ public string References { get; } = "";
///
- /// Constructs a DocComment instance from the documentation comments
+ /// Initializes a new instance of the class
+ /// from the documentation comments
/// associated with a source code element.
///
/// The doc comments from the source code
/// The name of the element
/// Flag indicating whether or not the element had a Deprecated attribute
/// The name of the replacement element for deprecated elements, if given
- public DocComment(IEnumerable docComments, string name, bool deprecated, string replacement)
+ public DocComment(IEnumerable docComments, string name, bool deprecated, string? replacement)
{
static string GetHeadingText(HeadingBlock heading)
{
@@ -181,16 +210,12 @@ static void ParseListSection(IEnumerable blocks, List accum, bool
{
if (sub is ListItemBlock item)
{
- // Some special treatment for funky doc comments in some of the Canon
+ // Some special treatment for funky doc comments in some of the Canon\
if (item.Count == 1 && item.LastChild is LeafBlock leaf && leaf.Inline != null &&
leaf.Inline.FirstChild is LiteralInline literal)
{
var itemText = lowerCase ? GetParagraphText(leaf).ToLowerInvariant() : GetParagraphText(leaf);
- if (itemText.StartsWith("@\"") && itemText.EndsWith("\""))
- {
- itemText = itemText.Substring(2, itemText.Length - 3);
- }
- literal.Content = new Markdig.Helpers.StringSlice(itemText.ToLowerInvariant());
+ literal.Content = new Markdig.Helpers.StringSlice(itemText);
}
accum.Add(ToMarkdown(new Block[] { item }));
}
@@ -241,23 +266,11 @@ static void ParseMapSection(IEnumerable blocks, Dictionary();
- this.Output = "";
- this.TypeParameters = new Dictionary();
- this.Example = "";
- this.Remarks = "";
- this.SeeAlso = new List();
- this.References = "";
-
var deprecationSummary = string.IsNullOrWhiteSpace(replacement)
? DiagnosticItem.Message(WarningCode.DeprecationWithoutRedirect, new[] { name })
: DiagnosticItem.Message(
WarningCode.DeprecationWithRedirect,
- new[] { name, "@\"" + replacement.ToLowerInvariant() + "\"" });
+ new string[] { name, $"" });
var deprecationDetails = "";
var text = string.Join("\n", docComments);
@@ -308,21 +321,24 @@ static void ParseMapSection(IEnumerable blocks, Dictionary 0 && this.Example == "")
+ if (examples.Count > 0 && this.Examples.IsEmpty)
{
- this.Example = ToMarkdown(examples);
+ this.Examples = this.Examples.Add(ToMarkdown(examples));
}
this.Remarks = ToMarkdown(remarks);
break;
case "See Also":
// seeAlso is a list of UIDs, which are all lower case,
// so pass true to lowercase all strings found in this section
- ParseListSection(section, this.SeeAlso, true);
+ ParseListSection(section, this.SeeAlso, false);
break;
case "References":
this.References = ToMarkdown(section);
diff --git a/src/QsCompiler/DocumentationParser/DocItem.cs b/src/Documentation/DocumentationParser/DocItem.cs
similarity index 97%
rename from src/QsCompiler/DocumentationParser/DocItem.cs
rename to src/Documentation/DocumentationParser/DocItem.cs
index 4c034f04cd..43b71c9867 100644
--- a/src/QsCompiler/DocumentationParser/DocItem.cs
+++ b/src/Documentation/DocumentationParser/DocItem.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
@@ -78,6 +79,7 @@ internal YamlMappingNode ToNamespaceItem() =>
/// Writes a full YAML representation of this item to the given text stream.
///
/// The text stream to output to
+ [Obsolete("Writing YAML documentation is no longer supported.")]
internal abstract void WriteToFile(TextWriter text);
}
}
diff --git a/src/QsCompiler/DocumentationParser/DocNamespace.cs b/src/Documentation/DocumentationParser/DocNamespace.cs
similarity index 97%
rename from src/QsCompiler/DocumentationParser/DocNamespace.cs
rename to src/Documentation/DocumentationParser/DocNamespace.cs
index ec9405746b..7b3abb042b 100644
--- a/src/QsCompiler/DocumentationParser/DocNamespace.cs
+++ b/src/Documentation/DocumentationParser/DocNamespace.cs
@@ -198,6 +198,7 @@ int CompareUids(YamlNode node1, YamlNode node2) =>
/// specified directory.
///
/// The directory to write the files to
+ [Obsolete("Writing YAML documentation is no longer supported.")]
internal void WriteItemsToDirectory(string directoryPath, List errors)
{
void WriteItem(DocItem i)
@@ -221,6 +222,7 @@ void WriteItem(DocItem i)
///
/// The mapping node representing the preexisting contents of this namespace's YAML file.
///
+ [Obsolete("Writing YAML documentation is no longer supported.")]
internal void WriteToStream(Stream stream, YamlMappingNode? rootNode = null)
{
string ToSequenceKey(string itemTypeName)
@@ -287,8 +289,7 @@ string ToSequenceKey(string itemTypeName)
foreach (var item in this.items)
{
var typeKey = ToSequenceKey(item.ItemType);
- SortedDictionary typeList;
- if (itemTypeNodes.TryGetValue(typeKey, out typeList))
+ if (itemTypeNodes.TryGetValue(typeKey, out var typeList))
{
if (typeList.ContainsKey(item.Uid))
{
@@ -323,6 +324,7 @@ string ToSequenceKey(string itemTypeName)
/// Writes the YAML file for this namespace.
///
/// The directory to write the file to
+ [Obsolete("Writing YAML documentation is no longer supported.")]
internal void WriteToFile(string directoryPath)
{
var rootNode = Utils.ReadYamlFile(directoryPath, this.name) as YamlMappingNode;
diff --git a/src/QsCompiler/DocumentationParser/DocUdt.cs b/src/Documentation/DocumentationParser/DocUdt.cs
similarity index 98%
rename from src/QsCompiler/DocumentationParser/DocUdt.cs
rename to src/Documentation/DocumentationParser/DocUdt.cs
index a17672146c..35e5eca84d 100644
--- a/src/QsCompiler/DocumentationParser/DocUdt.cs
+++ b/src/Documentation/DocumentationParser/DocUdt.cs
@@ -38,7 +38,7 @@ internal override void WriteToFile(TextWriter text)
rootNode.AddStringMapping(Utils.UidKey, this.uid);
rootNode.AddStringMapping(Utils.NameKey, this.name);
rootNode.AddStringMapping(Utils.TypeKey, this.itemType);
- rootNode.AddStringMapping(Utils.NamespaceKey, this.namespaceName.NamespaceAsUid());
+ rootNode.AddStringMapping(Utils.NamespaceKey, this.namespaceName.AsObsoleteUid());
if (!string.IsNullOrEmpty(this.comments.Documentation))
{
rootNode.AddStringMapping(Utils.SummaryKey, this.comments.Documentation);
diff --git a/src/QsCompiler/DocumentationParser/DocumentationParser.csproj b/src/Documentation/DocumentationParser/DocumentationParser.csproj
similarity index 85%
rename from src/QsCompiler/DocumentationParser/DocumentationParser.csproj
rename to src/Documentation/DocumentationParser/DocumentationParser.csproj
index eac515df95..f6974fed83 100644
--- a/src/QsCompiler/DocumentationParser/DocumentationParser.csproj
+++ b/src/Documentation/DocumentationParser/DocumentationParser.csproj
@@ -16,8 +16,8 @@
-
-
+
+
diff --git a/src/QsCompiler/DocumentationParser/Properties/AssemblyInfo.cs b/src/Documentation/DocumentationParser/Properties/AssemblyInfo.cs
similarity index 100%
rename from src/QsCompiler/DocumentationParser/Properties/AssemblyInfo.cs
rename to src/Documentation/DocumentationParser/Properties/AssemblyInfo.cs
diff --git a/src/QsCompiler/DocumentationParser/Utils.cs b/src/Documentation/DocumentationParser/Utils.cs
similarity index 99%
rename from src/QsCompiler/DocumentationParser/Utils.cs
rename to src/Documentation/DocumentationParser/Utils.cs
index 2ba6604d7e..4df7afaa39 100644
--- a/src/QsCompiler/DocumentationParser/Utils.cs
+++ b/src/Documentation/DocumentationParser/Utils.cs
@@ -475,8 +475,11 @@ internal static void DoTrackingExceptions(Action act, List errors)
}
}
- internal static string NamespaceAsUid(this string namespaceName) =>
- namespaceName.ToLowerInvariant();
+ internal static string AsObsoleteUid(this string qualifiedName) =>
+ qualifiedName.ToLowerInvariant();
+
+ internal static string AsUid(this string qualifiedName) =>
+ qualifiedName;
///
/// Returns the text as a Markdown warning block.
diff --git a/src/Documentation/Summarizer/README.md b/src/Documentation/Summarizer/README.md
new file mode 100644
index 0000000000..957db4479a
--- /dev/null
+++ b/src/Documentation/Summarizer/README.md
@@ -0,0 +1,11 @@
+# summarize_documentation
+
+This utility summarizes Markdown documentation gathered from one or more compilation units,
+producing namespace and TOC files from the gathered documentation.
+
+For example:
+
+```bash
+$ pip install -r requirementx.txt
+$ python obj/qsharp/docs/*.md obj/qsharp/docs
+```
diff --git a/src/Documentation/Summarizer/requirements.txt b/src/Documentation/Summarizer/requirements.txt
new file mode 100644
index 0000000000..eb24756324
--- /dev/null
+++ b/src/Documentation/Summarizer/requirements.txt
@@ -0,0 +1,3 @@
+python-frontmatter
+click
+ruamel-yaml
diff --git a/src/Documentation/Summarizer/summarize_documentation.py b/src/Documentation/Summarizer/summarize_documentation.py
new file mode 100644
index 0000000000..0c1f4c1717
--- /dev/null
+++ b/src/Documentation/Summarizer/summarize_documentation.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+from collections import defaultdict
+from dataclasses import dataclass, asdict, field
+from pathlib import Path
+import glob
+
+import click
+import frontmatter
+import ruamel.yaml as yaml
+
+namespace_comment = "### YamlMime:QSharpNamespace"
+warning_comment = """
+# This file is automatically generated.
+# Please do not modify this file manually, or your changes may be lost when
+# documentation is rebuilt.
+"""
+
+@dataclass(frozen=True)
+class NamespaceItem:
+ summary: str = ""
+ name: str = ""
+ namespace: str = ""
+ uid: str = ""
+ kind: str = ""
+
+@dataclass
+class Namespace:
+ summary: str = ""
+ uid: str = ""
+ name: str = ""
+ # NB: We need to set default_factory instead of default, since set is a
+ # mutable type.
+ items = field(default_factory=set)
+
+def items_of_kind(items, kind):
+ return [
+ {
+ "uid": item.uid,
+ "summary": item.summary
+ }
+ for item in
+ sorted(
+ (item for item in items if item.kind == kind),
+ key=(lambda item: item.uid)
+ )
+ ]
+
+@click.command()
+@click.argument("sources")
+@click.argument("output_path")
+def main(sources : str, output_path : str):
+ namespaces = defaultdict(Namespace)
+ output_path = Path(output_path)
+ for source in glob.glob(sources):
+ print(source)
+ with open(source, 'r', encoding='utf8') as f:
+ header, _ = frontmatter.parse(f.read())
+ if header['qsharp.kind'] == 'namespace':
+ namespaces[header['qsharp.name']].summary = header['qsharp.summary']
+ namespaces[header['qsharp.name']].name = header['qsharp.name']
+ namespaces[header['qsharp.name']].uid = header['uid']
+ else:
+ namespaces[header['qsharp.namespace']].items.add(NamespaceItem(
+ summary=header['qsharp.summary'],
+ name=header['qsharp.name'],
+ namespace=header['qsharp.namespace'],
+ uid=header["uid"],
+ kind=header["qsharp.kind"]
+ ))
+
+ for namespace_name, namespace in namespaces.items():
+ uid = namespace.uid or namespace_name
+ name = namespace.name or namespace_name
+ namespace_page = {
+ "uid": uid,
+ "name": name,
+ "summary": namespace.summary,
+ "operations": items_of_kind(namespace.items, "operation"),
+ "functions": items_of_kind(namespace.items, "function"),
+ "newtypes": items_of_kind(namespace.items, "udt")
+ }
+
+ with open(output_path / f"{name.lower()}.yml", "w", encoding="utf8") as f:
+ f.write(namespace_comment + warning_comment + yaml.dump(namespace_page))
+
+ toc_page = [
+ {
+ "uid": namespace.name,
+ "name": namespace_name,
+ "items": [
+ {
+ "name": item.name,
+ "uid": item.uid
+ }
+ for item in sorted(
+ namespace.items,
+ key=lambda item: item.uid
+ )
+ ]
+ }
+ for namespace_name, namespace in sorted(
+ namespaces.items(),
+ key=lambda pair: pair[0]
+ )
+ ]
+ with open(output_path / "toc.yml", "w", encoding="utf8") as f:
+ f.write(warning_comment + yaml.dump(toc_page))
+
+if __name__ == "__main__":
+ main()
diff --git a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs b/src/Documentation/Tests.DocGenerator/DocParsingTests.cs
similarity index 97%
rename from src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs
rename to src/Documentation/Tests.DocGenerator/DocParsingTests.cs
index 4e82c5e169..3384681da2 100644
--- a/src/QsCompiler/Tests.DocGenerator/DocParsingTests.cs
+++ b/src/Documentation/Tests.DocGenerator/DocParsingTests.cs
@@ -104,7 +104,7 @@ public void ParseUdt()
"",
"# See Also",
"- Microsoft.Quantum.Canon.PauliEvolutionSet",
- "- @\"microsoft.quantum.canon.evolutionset\""
+ "- Microsoft.Quantum.Canon.EvolutionSet",
};
string expected = @"### YamlMime:QSharpType
# This file is automatically generated.
@@ -140,8 +140,8 @@ element indexes the subsystem on which the generator acts on.
```
syntax: newtype GeneratorIndex = ((Int[], Double[]), Int[]);
seeAlso:
-- microsoft.quantum.canon.paulievolutionset
-- microsoft.quantum.canon.evolutionset
+- Microsoft.Quantum.Canon.PauliEvolutionSet
+- Microsoft.Quantum.Canon.EvolutionSet
...
";
var intArrayType = ResolvedType.New(QsType.NewArrayType(ResolvedType.New(QsType.Int)));
@@ -390,24 +390,24 @@ public void ParseDeprecated()
comment = new DocComment(comments.Take(2), "name", true, "NewName");
Assert.Equal(
"> [!WARNING]\n" +
- "> name has been deprecated. Please use @\"newname\" instead.\n" +
+ "> name has been deprecated. Please use instead.\n" +
"\n" +
"This is some text",
comment.Summary);
- Assert.Equal("name has been deprecated. Please use @\"newname\" instead.", comment.ShortSummary);
+ Assert.Equal("name has been deprecated. Please use instead.", comment.ShortSummary);
Assert.Equal(comment.Summary, comment.Documentation);
// Test with both
comment = new DocComment(comments, "name", true, "NewName");
Assert.Equal(
"> [!WARNING]\n" +
- "> name has been deprecated. Please use @\"newname\" instead.\n" +
+ "> name has been deprecated. Please use instead.\n" +
">\n" +
"> Some other text\n" +
"\n" +
"This is some text",
comment.Summary);
- Assert.Equal("name has been deprecated. Please use @\"newname\" instead.", comment.ShortSummary);
+ Assert.Equal("name has been deprecated. Please use instead.", comment.ShortSummary);
Assert.Equal(comment.Summary, comment.Documentation);
}
}
diff --git a/src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs b/src/Documentation/Tests.DocGenerator/DocWritingTests.cs
similarity index 100%
rename from src/QsCompiler/Tests.DocGenerator/DocWritingTests.cs
rename to src/Documentation/Tests.DocGenerator/DocWritingTests.cs
diff --git a/src/QsCompiler/Tests.DocGenerator/Tests.DocGenerator.csproj b/src/Documentation/Tests.DocGenerator/Tests.DocGenerator.csproj
similarity index 93%
rename from src/QsCompiler/Tests.DocGenerator/Tests.DocGenerator.csproj
rename to src/Documentation/Tests.DocGenerator/Tests.DocGenerator.csproj
index 963b1ecbd5..0cc6ecb9ae 100644
--- a/src/QsCompiler/Tests.DocGenerator/Tests.DocGenerator.csproj
+++ b/src/Documentation/Tests.DocGenerator/Tests.DocGenerator.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/src/QsCompiler/Tests.DocGenerator/Utils.cs b/src/Documentation/Tests.DocGenerator/Utils.cs
similarity index 100%
rename from src/QsCompiler/Tests.DocGenerator/Utils.cs
rename to src/Documentation/Tests.DocGenerator/Utils.cs
diff --git a/src/QsCompiler/CommandLineTool/Commands/Build.cs b/src/QsCompiler/CommandLineTool/Commands/Build.cs
index 8386eed9a2..594da37663 100644
--- a/src/QsCompiler/CommandLineTool/Commands/Build.cs
+++ b/src/QsCompiler/CommandLineTool/Commands/Build.cs
@@ -57,12 +57,6 @@ public static IEnumerable UsageExamples
HelpText = "Destination folder where the output of the compilation will be generated.")]
public string OutputFolder { get; set; }
- [Option(
- "doc",
- Required = false,
- SetName = CODE_MODE,
- HelpText = "Destination folder where documentation will be generated.")]
- public string DocFolder { get; set; }
#nullable restore annotations
[Option(
@@ -125,8 +119,14 @@ internal static bool IncorporateResponseFiles(
///
private static IEnumerable SplitCommandLineArguments(string commandLine)
{
+ string TrimQuotes(string s) =>
+ s.StartsWith('"') && s.EndsWith('"')
+ ? s.Substring(1, s.Length - 2)
+ : s;
+
var parmChars = commandLine?.ToCharArray() ?? Array.Empty();
var inQuote = false;
+
for (int index = 0; index < parmChars.Length; index++)
{
var precededByBackslash = index > 0 && parmChars[index - 1] == '\\';
@@ -144,9 +144,10 @@ private static IEnumerable SplitCommandLineArguments(string commandLine)
parmChars[index] = '\n';
}
}
+
return new string(parmChars)
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
- .Select(arg => arg.Trim('"'));
+ .Select(arg => TrimQuotes(arg));
}
///
@@ -198,7 +199,6 @@ public static int Run(BuildOptions options, ConsoleLogger logger)
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,
diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs
index 7653d59ca8..ebb2d0d874 100644
--- a/src/QsCompiler/CommandLineTool/Options.cs
+++ b/src/QsCompiler/CommandLineTool/Options.cs
@@ -90,7 +90,8 @@ internal bool ParseAssemblyProperties(out Dictionary parsed)
parsed = new Dictionary();
foreach (var keyValue in this.AdditionalAssemblyProperties ?? Array.Empty())
{
- var pieces = keyValue?.Split(":");
+ // NB: We use `count: 2` here to ensure that assembly constants can contain colons.
+ var pieces = keyValue?.Split(":", count: 2);
success =
success && !(pieces is null) && pieces.Length == 2 &&
parsed.TryAdd(pieces[0].Trim().Trim('"'), pieces[1].Trim().Trim('"'));
diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs
index d128ae62af..7fda44f9ad 100644
--- a/src/QsCompiler/Compiler/CompilationLoader.cs
+++ b/src/QsCompiler/Compiler/CompilationLoader.cs
@@ -301,7 +301,6 @@ internal bool Success(Configuration options) =>
this.WasSuccessful(!options.SkipSyntaxTreeTrimming, this.TreeTrimming) &&
this.WasSuccessful(options.ConvertClassicalControl, this.ConvertClassicalControl) &&
this.WasSuccessful(options.IsExecutable && !options.SkipMonomorphization, this.Monomorphization) &&
- this.WasSuccessful(options.DocumentationOutputFolder != null, this.Documentation) &&
this.WasSuccessful(options.SerializeSyntaxTree, this.Serialization) &&
this.WasSuccessful(options.BuildOutputFolder != null, this.BinaryFormat) &&
this.WasSuccessful(options.DllOutputPath != null, this.DllGeneration) &&
@@ -627,20 +626,6 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference
}
}
- if (this.config.DocumentationOutputFolder != null)
- {
- this.RaiseCompilationTaskStart("OutputGeneration", "DocumentationGeneration");
- this.compilationStatus.Documentation = Status.Succeeded;
- var docsFolder = Path.GetFullPath(string.IsNullOrWhiteSpace(this.config.DocumentationOutputFolder) ? "." : this.config.DocumentationOutputFolder);
- void OnDocException(Exception ex) => this.LogAndUpdate(ref this.compilationStatus.Documentation, ex);
- var docsGenerated = this.VerifiedCompilation != null && DocBuilder.Run(docsFolder, this.VerifiedCompilation.SyntaxTree.Values, this.VerifiedCompilation.SourceFiles, onException: OnDocException);
- if (!docsGenerated)
- {
- this.LogAndUpdate(ref this.compilationStatus.Documentation, ErrorCode.DocGenerationFailed, Enumerable.Empty());
- }
- this.RaiseCompilationTaskEnd("OutputGeneration", "DocumentationGeneration");
- }
-
this.RaiseCompilationTaskEnd("OverallCompilation", "OutputGeneration");
this.RaiseCompilationTaskEnd(null, "OverallCompilation");
}
diff --git a/src/QsCompiler/Compiler/Compiler.csproj b/src/QsCompiler/Compiler/Compiler.csproj
index 0aa3b0593f..670f874017 100644
--- a/src/QsCompiler/Compiler/Compiler.csproj
+++ b/src/QsCompiler/Compiler/Compiler.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj b/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj
index ee8e14a0ae..d32a850e53 100644
--- a/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj
+++ b/src/QsCompiler/SyntaxProcessor/SyntaxProcessor.fsproj
@@ -23,7 +23,7 @@
-
+
diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs
index 47961c0290..77c908fcc3 100644
--- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs
+++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs
@@ -203,41 +203,6 @@ let ``execute rewrite steps only if validation passes`` () =
Assert.Equal(CompilationLoader.Status.NotRun, loaded.FunctorSupport)
Assert.Equal(CompilationLoader.Status.NotRun, loaded.Monomorphization)
-
-[]
-let ``generate docs`` () =
- let docsFolder = ("TestCases", "docs.Out") |> Path.Combine
- if (Directory.Exists docsFolder) then
- for file in Directory.GetFiles docsFolder do
- File.Delete file
-
- let toc = Path.Combine (docsFolder, "toc.yml")
- let nsDoc = Path.Combine (docsFolder, "Microsoft.Quantum.Testing.General.yml")
- let opDoc = Path.Combine (docsFolder, "microsoft.quantum.testing.general.unitary.yml")
- let existsAndNotEmpty fileName = fileName |> File.Exists && not (File.ReadAllText fileName |> String.IsNullOrWhiteSpace)
- let args =
- [|
- "build"
- "--input"
- ("TestCases","General.qs") |> Path.Combine
- "--doc"
- docsFolder
- |]
-
- let result = Program.Main args
- Assert.Equal(ReturnCode.SUCCESS, result)
- Assert.True (existsAndNotEmpty toc)
- Assert.True (existsAndNotEmpty nsDoc)
- Assert.True (existsAndNotEmpty opDoc)
-
- // Verify that we can compile repeatedly without errors despite docs already existing.
- let result2 = Program.Main args
- Assert.Equal(ReturnCode.SUCCESS, result2)
- Assert.True (existsAndNotEmpty toc)
- Assert.True (existsAndNotEmpty nsDoc)
- Assert.True (existsAndNotEmpty opDoc)
-
-
[]
let ``find path relative`` () =
let fullPath = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.qs")
diff --git a/src/QsCompiler/Transformations/QsharpCodeOutput.cs b/src/QsCompiler/Transformations/QsharpCodeOutput.cs
index 3405b41b0a..907af4544d 100644
--- a/src/QsCompiler/Transformations/QsharpCodeOutput.cs
+++ b/src/QsCompiler/Transformations/QsharpCodeOutput.cs
@@ -131,6 +131,13 @@ public SyntaxTreeToQsharp(TransformationContext? context = null)
public string? ToCode(TypedExpression ex) =>
this.ToCode(ex.Expression);
+ public string ToCode(QsCustomType type)
+ {
+ var nrPreexistingLines = this.SharedState.NamespaceOutputHandle.Count;
+ this.Namespaces.OnTypeDeclaration(type);
+ return string.Join(Environment.NewLine, this.SharedState.NamespaceOutputHandle.Skip(nrPreexistingLines));
+ }
+
public string ToCode(QsStatementKind stmKind)
{
var nrPreexistingLines = this.SharedState.StatementOutputHandle.Count;
diff --git a/src/QuantumSdk/README.md b/src/QuantumSdk/README.md
index acc2ef9c77..e2ab4cb52d 100644
--- a/src/QuantumSdk/README.md
+++ b/src/QuantumSdk/README.md
@@ -3,14 +3,14 @@
## Content ##
-The NuGet package Microsoft.Quantum.Sdk serves a .NET Core Sdk for developing quantum applications, meaning it acts as [shared SDK Component](https://docs.microsoft.com/en-us/dotnet/core/tools/cli-msbuild-architecture#the-tooling-layers).
+The NuGet package Microsoft.Quantum.Sdk serves a .NET Core Sdk for developing quantum applications, meaning it acts as [shared SDK Component](https://docs.microsoft.com/en-us/dotnet/core/tools/cli-msbuild-architecture#the-tooling-layers).
It contains the properties and targets that define the compilation process for Q# projects, tools used as part of the build, as well as some project system support for Q# files. It in particular also provides the support for executing a compilation step defined in a package or project reference as part of the compilation process. See [this section](#extending-the-q#-compiler) for more details.
-The Sdk includes all \*.qs files within the project directory as well as the Q# standard libraries by default. No additional reference to `Microsoft.Quantum.Standard` is needed. For more details see the [section on defined properties](#defined-project-properties) below.
+The Sdk includes all \*.qs files within the project directory as well as the Q# standard libraries by default. No additional reference to `Microsoft.Quantum.Standard` is needed. For more details see the [section on defined properties](#defined-project-properties) below.
## Using the Sdk ##
-To use the Quantum Sdk simply list the NuGet package as Sdk at the top of your project file:
+To use the Quantum Sdk simply list the NuGet package as Sdk at the top of your project file:
```
...
@@ -18,7 +18,7 @@ To use the Quantum Sdk simply list the NuGet package as Sdk at the top of your p
```
See also the [MSBuild documentation](https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-use-project-sdk?view=vs-2019).
-If you would like to build the Microsoft.Quantum.Sdk NuGet package from source, first run the `bootstrap.ps1` script in the root of this repository and then run the `build/pack.ps1` script. You will find the built package in the generated `drops` folder under `nugets`. Simply add the package to your local NuGet folder to start using it.
+If you would like to build the Microsoft.Quantum.Sdk NuGet package from source, first run the `bootstrap.ps1` script in the root of this repository and then run the `build/pack.ps1` script. You will find the built package in the generated `drops` folder under `nugets`. Simply add the package to your local NuGet folder to start using it.
[comment]: # (TODO: add a section on specifying an execution target)
@@ -42,8 +42,8 @@ The order in which these steps are executed can be configured by specifying thei
...
```
-If no priority is specified, the priority for that reference is set to zero.
-Steps defined within packages or projects with higher priority are executed first. If several classes within a certain reference implement the `IRewriteStep` interface, then these steps are executed according to their priority specified as part of the interface. The priority defined for the project or package reference takes precedence, such that the priorities defined by the interface property are not compared across assemblies.
+If no priority is specified, the priority for that reference is set to zero.
+Steps defined within packages or projects with higher priority are executed first. If several classes within a certain reference implement the `IRewriteStep` interface, then these steps are executed according to their priority specified as part of the interface. The priority defined for the project or package reference takes precedence, such that the priorities defined by the interface property are not compared across assemblies.
[comment]: # (TODO: describe how to limit included rewrite steps to a particular execution target)
@@ -53,30 +53,30 @@ See the [this section](#packaging) for more detail on how to package a Q# compil
### Injected C# code ###
-It is possible to inject C# code into Q# packages e.g. for integration purposes. By default the Sdk is configured to do just that, see also the section on [defined properties](#defined-project-properties). That code may be generated as part of a custom rewrite step, e.g. if the code generation requires information about the Q# compilation.
+It is possible to inject C# code into Q# packages e.g. for integration purposes. By default the Sdk is configured to do just that, see also the section on [defined properties](#defined-project-properties). That code may be generated as part of a custom rewrite step, e.g. if the code generation requires information about the Q# compilation.
The Sdk defines a couple of build targets that can be redefined to run certain tasks before important build stages. These targets do nothing by default and merely serve as handles to easily integrate into the build process.
-The following such targets are currently available:
+The following such targets are currently available:
-- `BeforeQsharpCompile`:
-The target will execute right before the Q# compiler is invoked. All assembly references and the paths to all qsc references will be resolved at that time.
+- `BeforeQsharpCompile`:
+The target will execute right before the Q# compiler is invoked. All assembly references and the paths to all qsc references will be resolved at that time.
-- `BeforeCsharpCompile`:
-The target will execute right before the C# compiler is invoked. All Q# compilation steps will have completed at that time, and in particular all rewrite steps will have been executed.
+- `BeforeCsharpCompile`:
+The target will execute right before the C# compiler is invoked. All Q# compilation steps will have completed at that time, and in particular all rewrite steps will have been executed.
-For example, if a qsc reference contains a rewrite step that generates C# code during transformation, that code can be included into the built dll by adding the following target to the project file:
+For example, if a qsc reference contains a rewrite step that generates C# code during transformation, that code can be included into the built dll by adding the following target to the project file:
```
-
+
```
### Distributing Q# compiler extensions as NuGet packages ###
-In order to avoid a dependency of the Q# build targets on the Restore target, we require that NuGet packages containing Q# compiler extensions define a property that contains the path to the dll to load. This is done by including a file with MSBuild props in the package, following the instructions [here](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#include-msbuild-props-and-targets-in-a-package).
+In order to avoid a dependency of the Q# build targets on the Restore target, we require that NuGet packages containing Q# compiler extensions define a property that contains the path to the dll to load. This is done by including a file with MSBuild props in the package, following the instructions [here](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#include-msbuild-props-and-targets-in-a-package).
The content of the file should be similar to the following, with `Package_Name` being replace by the name of your package after replacing dots by underscore, and `Package.Name` should be replaced by the assembly name of your package:
@@ -88,111 +88,113 @@ The content of the file should be similar to the following, with `Package_Name`
$(MSBuildThisFileDirectory)/../lib/netstandard2.1/Package.Name.dll
-
+
```
This [example](https://github.com/microsoft/qsharp-compiler/tree/main/examples/CompilerExtensions/ExtensionPackage) provides a template for packaging a Q# compiler extension.
-If you develop a NuGet package to extend the Q# compilation process, we recommend to distribute it as a self-contained package to avoid issues due to references that could not be resolved. Each qsc reference is loaded into its own context to avoid issues when several references depend on different versions of the same package.
+If you develop a NuGet package to extend the Q# compilation process, we recommend to distribute it as a self-contained package to avoid issues due to references that could not be resolved. Each qsc reference is loaded into its own context to avoid issues when several references depend on different versions of the same package.
### Troubleshooting compiler extensions ###
-The compiler attempts to load rewrite steps even if these have been compiled against a different compiler version. While we do our best to mitigate issue due to a version mismatch, it is generally recommended to use compiler extensions that are compiled against the same compiler package version as the Sdk version of the project.
+The compiler attempts to load rewrite steps even if these have been compiled against a different compiler version. While we do our best to mitigate issue due to a version mismatch, it is generally recommended to use compiler extensions that are compiled against the same compiler package version as the Sdk version of the project.
When a rewrite steps fails to execute, setting the `QscVerbosity` to "Detailed" or "Diagnostic" will log the encountered exception with stack trace:
```
Detailed
```
-A `FileNotFoundException` will be raised if a compiler extension attempts to load a reference that either could not be found, or could not be loaded for other reasons.
+A `FileNotFoundException` will be raised if a compiler extension attempts to load a reference that either could not be found, or could not be loaded for other reasons.
By default, the compiler will search the project output directory for a suitable assembly in case a dependency cannot be found.
-If such an exception occurs during a compilation step loaded from a package reference, the issue may be resolved by adding the package containing the missing dll to the project or by copying the missing dll to the output directory.
+If such an exception occurs during a compilation step loaded from a package reference, the issue may be resolved by adding the package containing the missing dll to the project or by copying the missing dll to the output directory.
If such an exception occurs during a compilation step loaded from a project reference, issue may be resolved by defining the property
```
true
```
-in the project that implements the compilation step.
+in the project that implements the compilation step.
## Defined project properties ##
-The Sdk defines the following properties for each project using it:
+The Sdk defines the following properties for each project using it:
-- `QsharpLangVersion`:
+- `QsharpLangVersion`:
The version of the Q# language specification.
-- `QuantumSdkVersion`:
+- `QuantumSdkVersion`:
The NuGet version of the Sdk package.
-The following properties can be configured to customize the build:
+The following properties can be configured to customize the build:
-- `AdditionalQscArguments`:
-May contain additional arguments to pass to the Q# command line compiler. Valid additional arguments are `--emit-dll`, or `--no-warn` followed by any number of integers specifying the warnings to ignore.
+- `AdditionalQscArguments`:
+May contain additional arguments to pass to the Q# command line compiler. Valid additional arguments are `--emit-dll`, or `--no-warn` followed by any number of integers specifying the warnings to ignore.
-- `CsharpGeneration`:
-Specifies whether to generate C# code as part of the compilation process. Setting this property to false may prevent certain interoperability features or integration with other pieces of the Quantum Development Kit.
+- `CsharpGeneration`:
+Specifies whether to generate C# code as part of the compilation process. Setting this property to false may prevent certain interoperability features or integration with other pieces of the Quantum Development Kit.
- `DefaultSimulator`:
-Specifies the simulator to use by default for execution. Valid values are QuantumSimulator, ToffoliSimulator, ResourcesEstimator, or the fully qualified name of a custom simulator.
+Specifies the simulator to use by default for execution. Valid values are QuantumSimulator, ToffoliSimulator, ResourcesEstimator, or the fully qualified name of a custom simulator.
-- `IncludeQsharpCorePackages`:
-Specifies whether the packages providing the basic language support for Q# are referenced. This property is set to true by default. If set to false, the Sdk will not reference any Q# libraries.
+- `IncludeQsharpCorePackages`:
+Specifies whether the packages providing the basic language support for Q# are referenced. This property is set to true by default. If set to false, the Sdk will not reference any Q# libraries.
- `IncludeProviderPackages`:
Specifies whether the packages for specific hardware providers should be automatically included based on the specified `ExecutionTarget`. This property is set to true by default. If set to false, the Sdk will not automatically reference any provider packages.
-- `QscExe`:
-The command to invoke the Q# compiler. The value set by default invokes the Q# compiler that is packaged as tool with the Sdk. The default value can be accessed via the `DefaultQscExe` property.
+- `QscExe`:
+The command to invoke the Q# compiler. The value set by default invokes the Q# compiler that is packaged as tool with the Sdk. The default value can be accessed via the `DefaultQscExe` property.
-- `QscVerbosity`:
+- `QscVerbosity`:
Defines the verbosity of the Q# compiler. Recognized values are: Quiet, Minimal, Normal, Detailed, and Diagnostic.
-- `QsharpDocsGeneration`:
-Specified whether to generate yml documentation for the compiled Q# code. The default value is "false".
+- `QsharpDocsGeneration`:
+Specified whether to generate yml documentation for the compiled Q# code. The default value is "false".
-- `QsharpDocsOutputPath`:
-Directory where any generated documentation will be saved.
+- `QsharpDocsOutputPath`:
+Directory where any generated documentation will be saved.
+
+- `QsharpDocsPackageId`: Specifies the package ID that should appear in generated documentation. Set to `PackageId` by default, but can be overriden to allow for documenting parts of metapackages.
[comment]: # (TODO: document QscBuildConfigExe, QscBuildConfigOutputPath)
## Defined item groups ##
-The following configurable item groups are used by the Sdk:
+The following configurable item groups are used by the Sdk:
-- `PackageLoadFallbackFolder`:
-Contains the directories where the Q# compiler will look for a suitable dll if a qsc reference or one if its dependencies cannot be found. By default, the project output path is included in this item group.
+- `PackageLoadFallbackFolder`:
+Contains the directories where the Q# compiler will look for a suitable dll if a qsc reference or one if its dependencies cannot be found. By default, the project output path is included in this item group.
-- `PackageReference`:
+- `PackageReference`:
Contains all referenced NuGet packages. Package references for which the `IsQscReference` attribute is set to "true" may extend the Q# compiler and any implemented rewrite steps will be executed as part of the compilation process. See [this section](#extending-the-q#-compiler) for more details.
-- `ProjectReference`:
+- `ProjectReference`:
Contains all referenced projects. Project references for which the `IsQscReference` attribute is set to "true" may extend the Q# compiler and any implemented rewrite steps will be executed as part of the compilation process. See [this section](#extending-the-q#-compiler) for more details.
-- `QsharpCompile`:
+- `QsharpCompile`:
Contains all Q# source files included in the compilation.
# Sdk Packages #
A NuGet package of type `Sdk` enjoys certain privileges in terms of when and how its content is loaded.
-To understand how the content in this package works it is useful to understand how the properties, item groups, and targets defined in the Sdk are combined with those defined by a specific project.
-The order of evaluation for properties and item groups is roughly the following:
+To understand how the content in this package works it is useful to understand how the properties, item groups, and targets defined in the Sdk are combined with those defined by a specific project.
+The order of evaluation for properties and item groups is roughly the following:
- Properties defined in \*.props files of the Sdk
- Properties defined or included by the specific project file
- Properties defined in *.targets files of the Sdk
- Item groups defined in *.props files of the Sdk
- Item groups defined or included by the specific project file
-- Item groups defined in *.targets files of the Sdk
+- Item groups defined in *.targets files of the Sdk
-Similar considerations apply for the definition of targets. MSBuild will overwrite targets if multiple targets with the same name are defined. In that case, the target is replaced in its entirety independent on whether the values for `DependsOn`, `BeforeTarget`, and `AfterTarget` match - i.e. those will be overwritten. However, a target can be "anchored" by the surrounding targets' specifications of their dependencies, see e.g. the defined `BeforeCsharpCompile` target.
+Similar considerations apply for the definition of targets. MSBuild will overwrite targets if multiple targets with the same name are defined. In that case, the target is replaced in its entirety independent on whether the values for `DependsOn`, `BeforeTarget`, and `AfterTarget` match - i.e. those will be overwritten. However, a target can be "anchored" by the surrounding targets' specifications of their dependencies, see e.g. the defined `BeforeCsharpCompile` target.
## Load context in .NET Core
-To avoid issues with conflicting packages, we load each Q# compiler extension into its own context. For more information, see the Core CLR [design docs](https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/assemblyloadcontext.md).
+To avoid issues with conflicting packages, we load each Q# compiler extension into its own context. For more information, see the Core CLR [design docs](https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/assemblyloadcontext.md).
## Known Issues ##
The following issues and PRs may be of interest when using the Sdk:
-> https://github.com/NuGet/Home/issues/8692
-> https://github.com/dotnet/runtime/issues/949
-> https://github.com/NuGet/NuGet.Client/pull/3170
+> https://github.com/NuGet/Home/issues/8692
+> https://github.com/dotnet/runtime/issues/949
+> https://github.com/NuGet/NuGet.Client/pull/3170
diff --git a/src/QuantumSdk/Sdk/Sdk.props b/src/QuantumSdk/Sdk/Sdk.props
index 5b96ae2dd5..56d4ce0309 100644
--- a/src/QuantumSdk/Sdk/Sdk.props
+++ b/src/QuantumSdk/Sdk/Sdk.props
@@ -23,26 +23,33 @@
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets
index 5440be1925..242f27bf0d 100644
--- a/src/QuantumSdk/Sdk/Sdk.targets
+++ b/src/QuantumSdk/Sdk/Sdk.targets
@@ -68,9 +68,13 @@
+
+ <_QscDocsPackageId Condition="'$(PackageId)' != ''">$(PackageId)
+ <_QscDocsPackageId Condition="'$(QsharpDocsPackageId)' != ''">$(QsharpDocsPackageId)
<_QscCommandIsExecutableFlag Condition="'$(ResolvedQsharpOutputType)' == 'QsharpExe'">--build-exe
<_QscCommandOutputFlag>--output "$(GeneratedFilesOutputPath)"
- <_QscCommandDocsFlag Condition="$(QsharpDocsGeneration)">--doc "$(QsharpDocsOutputPath)"
<_QscCommandInputFlag Condition="@(QsharpCompile->Count()) > 0">--input "@(QsharpCompile,'" "')"
<_QscCommandReferencesFlag Condition="@(ResolvedQsharpReferences->Count()) > 0">--references "@(ResolvedQsharpReferences,'" "')"
<_QscCommandLoadFlag Condition="@(_PrioritizedResolvedQscReferences->Count()) > 0">--load "@(_PrioritizedResolvedQscReferences,'" "')"
@@ -78,12 +82,14 @@
<_QscCommandTargetDecompositionsFlag Condition="@(ResolvedTargetSpecificDecompositions->Count()) > 0">--target-specific-decompositions "@(ResolvedTargetSpecificDecompositions,'" "')"
<_QscCommandTestNamesFlag Condition="$(ExposeReferencesViaTestNames)">--load-test-names
<_QscCommandPredefinedAssemblyProperties>ProcessorArchitecture:$(ResolvedProcessorArchitecture) QsharpOutputType:$(ResolvedQsharpOutputType)
- <_QscCommandPredefinedAssemblyProperties Condition="$(DefaultSimulator) != ''">$(_QscCommandPredefinedAssemblyProperties) DefaultSimulator:$(DefaultSimulator)
- <_QscCommandPredefinedAssemblyProperties Condition="$(ExecutionTarget) != ''">$(_QscCommandPredefinedAssemblyProperties) ExecutionTarget:$(ExecutionTarget)
- <_QscCommandPredefinedAssemblyProperties Condition="$(ExposeReferencesViaTestNames)">$(_QscCommandPredefinedAssemblyProperties) ExposeReferencesViaTestNames:true
+ <_QscCommandPredefinedAssemblyProperties Condition="'$(DefaultSimulator)' != ''">$(_QscCommandPredefinedAssemblyProperties) DefaultSimulator:$(DefaultSimulator)
+ <_QscCommandPredefinedAssemblyProperties Condition="'$(ExecutionTarget)' != ''">$(_QscCommandPredefinedAssemblyProperties) ExecutionTarget:$(ExecutionTarget)
+ <_QscCommandPredefinedAssemblyProperties Condition="'$(ExposeReferencesViaTestNames)'">$(_QscCommandPredefinedAssemblyProperties) ExposeReferencesViaTestNames:true
+ <_QscCommandPredefinedAssemblyProperties Condition="'$(_QscDocsPackageId)' != ''">$(_QscCommandPredefinedAssemblyProperties) DocsPackageId:$(_QscDocsPackageeId)
+ <_QscCommandPredefinedAssemblyProperties Condition="'$(QsharpDocsGeneration)'">$(_QscCommandPredefinedAssemblyProperties) DocsOutputPath:"$(QsharpDocsOutputPath)"
<_QscCommandAssemblyPropertiesFlag>--assembly-properties $(_QscCommandPredefinedAssemblyProperties) $(QscCommandAssemblyProperties)
<_QscPackageLoadFallbackFoldersFlag Condition="@(ResolvedPackageLoadFallbackFolders->Count()) > 0">--package-load-fallback-folders "@(ResolvedPackageLoadFallbackFolders,'" "')"
- <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandIsExecutableFlag) $(_QscCommandDocsFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandRuntimeFlag) $(_QscCommandTargetDecompositionsFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments)
+ <_QscCommandArgs>--proj "$(PathCompatibleAssemblyName)" $(_QscCommandIsExecutableFlag) $(_QscCommandInputFlag) $(_QscCommandOutputFlag) $(_QscCommandReferencesFlag) $(_QscCommandLoadFlag) $(_QscCommandRuntimeFlag) $(_QscCommandTargetDecompositionsFlag) $(_QscPackageLoadFallbackFoldersFlag) $(_QscCommandTestNamesFlag) $(_QscCommandAssemblyPropertiesFlag) $(AdditionalQscArguments)
<_QscCommandArgsFile>$(QscBuildConfigOutputPath)qsc.rsp