diff --git a/.editorconfig b/.editorconfig index 6767078e0e..625762f16d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,10 +5,21 @@ root = true indent_style = space indent_size = 4 insert_final_newline = true +tab_width = 4 +end_of_line = crlf +# until [VSSpellChecker bug 277](https://github.com/EWSoftware/VSSpellChecker/issues/277) +# is fixed, disable the analyzers. It's more of a PITA than a help +# VSSPELL: Disable the analyzers until Bug 277 is fixed. +vsspell_code_analyzers_enabled = false + +# VSSPELL: Spell checker settings for all files +vsspell_section_id = 1c7003ec377c4bd9bb3c509d29770210 +vsspell_ignored_words_1c7003ec377c4bd9bb3c509d29770210 = File:.\IgnoredWords.dic|bar # match VS generated formatting for MSBuild project files [*.*proj,*.props,*.targets] indent_size = 2 +tab_width = 2 # match ISO standard requirement for C/C++ [*.c,*.h,*.cpp] @@ -56,6 +67,28 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_around_declaration_statements = ignore csharp_space_between_method_call_parameter_list_parentheses = true csharp_space_between_square_brackets = true +csharp_using_directive_placement = outside_namespace:error +csharp_prefer_simple_using_statement = true:error +csharp_prefer_braces = true:error +csharp_style_namespace_declarations = block_scoped:error +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = false:error +csharp_style_prefer_primary_constructors = false:silent +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_expression_bodied_constructors = false:error +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = true:suggestion +dotnet_diagnostic.RECS0001.severity = error +dotnet_diagnostic.SA1023.severity = none +dotnet_diagnostic.SA1124.severity = none +csharp_style_allow_embedded_statements_on_same_line_experimental = false:error +dotnet_diagnostic.SA1512.severity = none +dotnet_diagnostic.SA1129.severity = error +csharp_style_prefer_null_check_over_type_check = true:error +csharp_prefer_simple_default_expression = true:error +csharp_space_between_method_declaration_parameter_list_parentheses = true +csharp_space_between_method_declaration_empty_parameter_list_parentheses = true +dotnet_diagnostic.SA0001.severity = none # Analysis and refactoring rules for Ubiquity.NET # Description: Code analysis rules for Ubiquity.NET projects @@ -65,6 +98,17 @@ csharp_space_between_square_brackets = true # Code files [*.{cs,vb}] +# IDE0059: Unnecessary assignment of a value +dotnet_diagnostic.IDE0059.severity = none + +# CA1420: Property, type, or attribute requires runtime marshalling +dotnet_diagnostic.CA1420.severity = error + +# IDE0079: Remove unnecessary suppression +dotnet_diagnostic.IDE0079.severity = none + +# SYSLIB1092: The usage of 'LibraryImportAttribute' does not follow recommendations. +dotnet_diagnostic.SYSLIB1092.severity = warning dotnet_diagnostic.AD0001.severity = none @@ -1272,8 +1316,6 @@ dotnet_diagnostic.SA1127.severity = error dotnet_diagnostic.SA1128.severity = error -dotnet_diagnostic.SA1129.severity = error - dotnet_diagnostic.SA1130.severity = error dotnet_diagnostic.SA1131.severity = error @@ -1392,8 +1434,6 @@ dotnet_diagnostic.SA1510.severity = error dotnet_diagnostic.SA1511.severity = error -dotnet_diagnostic.SA1512.severity = error - dotnet_diagnostic.SA1513.severity = error dotnet_diagnostic.SA1514.severity = error @@ -1488,3 +1528,69 @@ dotnet_diagnostic.SX1101.severity = error # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = none + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_auto_properties = true:error +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:error +dotnet_style_prefer_conditional_expression_over_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_return = true:warning +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:error +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_diagnostic.MSTEST0032.severity = none diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..6adc43e9f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Set the default behavior, in case people don't have core.autocrlf set in global settings +* text=auto diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e08d855fea..5c9bec54b1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,8 +13,7 @@ Please provide the following information when submitting an issue. **.NET Framework Used:** -- [ ] .NET Core 3.0.0 -- [ ] .NET Core 3.1.0 +- [ ] .NET 9.0 - [ ] Something else **OS Environment:** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 1636e25003..77fd582e9c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -10,7 +10,7 @@ assignees: '' **Is your feature request related to a problem? Please describe.** > NOTE: > Feature requests are intended to request a new feature or a change in the intended/designed -> functionality (a.k.a. DCR). They should not be used to report bugs, which are incorrect +> functionality (a.k.a. "Design Change Request" [DCR]). They should not be used to report bugs, which are incorrect > implementations of the designed functionality. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 4ffa802c2e..33463a523c 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -3,45 +3,52 @@ on: push: branches: - develop - - llvm_10 + - LLVM20 + paths-ignore: - '**.md' + - '**.dic' + pull_request: branches: - develop - - llvm_10 + - LLVM20 + + paths-ignore: + - '**.md' + - '**.dic' + jobs: build: runs-on: windows-latest steps: - name: Checkout Repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: persist-credentials: false - fetch-depth: 0 - name: Build Source run: .\Build-All.ps1 -ForceClean -BuildMode Source - name: Publish build logs if: always() && github.event_name == 'pull_request' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: Build Logs path: .\BuildOutput\BinLogs - name: Publish NuGET Packages - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: Nuget Packages - path: .\BuildOutput\Nuget + name: NuGet Packages + path: .\BuildOutput\NuGet - name: Run Tests run: .\Invoke-UnitTests.ps1 - name: Publish test logs if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: Test Logs path: .\BuildOutput\Test-Results @@ -50,25 +57,22 @@ jobs: runs-on: windows-latest steps: - name: Checkout Repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: persist-credentials: false - fetch-depth: 0 - name: Build Docs - run: .\Build-All.ps1 -ForceClean -BuildMode Docs + run: .\Build-Docs.ps1 -FullInit -Configuration Release - name: Upload docs artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: Docs path: .\BuildOutput\docs\current - name: Publish build logs if: always() && github.event_name == 'pull_request' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: Build Logs path: .\BuildOutput\BinLogs - - diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 4592073707..1ce7e0e433 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -12,23 +12,22 @@ jobs: uses: actions/checkout@v2 with: persist-credentials: false - fetch-depth: 0 - name: Build Source - run: .\Build-All.ps1 + run: .\Build-All.ps1 -FullInit - name: Publish Artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: Nuget Packages - path: .\BuildOutput\Nuget + name: NuGet Packages + path: .\BuildOutput\NuGet - name: Run Tests run: .\Invoke-UnitTests.ps1 - name: Publish test logs if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: Test Logs path: .\BuildOutput\Test-Results diff --git a/.gitignore b/.gitignore index d94c620809..8c1eb93c95 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,10 @@ bld/ # Docs updated only by CI builds for GitHub Pages [Dd]ocs/ -# Visual Studo 2015 cache/options directory +# Visual Studio 2015 cache/options directory .vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ @@ -91,25 +93,45 @@ _ReSharper*/ !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets -# Windows Azure Build Output +# Microsoft Azure Build Output csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt -# Others +# Visual Studio cache files +# files ending in .cache can be ignored *.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview +*.jfm *.pfx *.publishsettings node_modules/ -bower_components/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # RIA/Silverlight projects Generated_Code/ @@ -122,9 +144,21 @@ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + # Microsoft Fakes FakesAssemblies/ +# GhostDoc plugin setting file +*.GhostDoc.xml + # Node.js Tools for Visual Studio .ntvs_analysis.dat @@ -159,3 +193,4 @@ Tools/ **/GeneratedDocs/ **/GeneratedCode/ /src/Interop/LibLLVM/EXPORTS.g.DEF +/CurrentVersionInfo.json diff --git a/Build-All.ps1 b/Build-All.ps1 index ac6cfcbd8b..af9e2aca8d 100644 --- a/Build-All.ps1 +++ b/Build-All.ps1 @@ -5,10 +5,6 @@ .PARAMETER Configuration This sets the build configuration to use, default is "Release" though for inner loop development this may be set to "Debug" -.PARAMETER AllowVsPreReleases - Switch to enable use of Visual Studio Pre-Release versions. This is NEVER enabled for official production builds, however it is - useful when adding support for new versions during the pre-release stages. - .PARAMETER ForceClean Forces a complete clean (Recursive delete of the build output) @@ -23,8 +19,8 @@ .DESCRIPTION This script is used by the automated build to perform the actual build. The Ubiquity.NET - family of projects all employ a PowerShell driven build that is generally divorced from the - automated build infrastructure used. This is done for several reasons, but the most + family of projects all employ a PowerShell driven build that is generally divorced from + the automated build infrastructure used. This is done for several reasons, but the most important ones are the ability to reproduce the build locally for inner development and for flexibility in selecting the actual back end. The back ends have changed a few times over the years and re-writing the entire build in terms of those back ends each time is @@ -34,20 +30,22 @@ [cmdletbinding()] Param( [string]$Configuration="Release", - [switch]$AllowVsPreReleases, [switch]$ForceClean, [ValidateSet('All','Source','Docs')] [System.String]$BuildMode = 'All' ) -pushd $PSScriptRoot +$ErrorActionPreference = "Stop" +$InformationPreference = "Continue" + +Push-Location $PSScriptRoot $oldPath = $env:Path try { # Pull in the repo specific support and force a full initialization of all the environment # as this is a top level build command. . .\repo-buildutils.ps1 - $buildInfo = Initialize-BuildEnvironment -FullInit -AllowVsPreReleases:$AllowVsPreReleases + $buildInfo = Initialize-BuildEnvironment -FullInit $BuildSource = $false $BuildDocs = $false; @@ -61,19 +59,19 @@ try if((Test-Path -PathType Container $buildInfo['BuildOutputPath']) -and $ForceClean ) { Write-Information "Cleaning output folder from previous builds" - rd -Recurse -Force -Path $buildInfo['BuildOutputPath'] + remove-Item -Recurse -Force -Path $buildInfo['BuildOutputPath'] -ProgressAction SilentlyContinue } - md $buildInfo['NuGetOutputPath'] -ErrorAction SilentlyContinue | Out-Null + New-Item -ItemType Directory $buildInfo['NuGetOutputPath'] -ErrorAction SilentlyContinue | Out-Null if($BuildSource) { - .\Build-Source.ps1 -AllowVsPreReleases:$AllowVsPreReleases + .\Build-Source.ps1 -Configuration:$Configuration } if($BuildDocs) { - .\Build-Docs.ps1 -AllowVsPreReleases:$AllowVsPreReleases + .\Build-Docs.ps1 -Configuration:$Configuration } } catch @@ -87,7 +85,7 @@ catch } finally { - popd + Pop-Location $env:Path = $oldPath } diff --git a/Build-Docs.ps1 b/Build-Docs.ps1 index f517b9e80b..48d3e3476a 100644 --- a/Build-Docs.ps1 +++ b/Build-Docs.ps1 @@ -5,10 +5,6 @@ .PARAMETER Configuration This sets the build configuration to use, default is "Release" though for inner loop development this may be set to "Debug" -.PARAMETER AllowVsPreReleases - Switch to enable use of Visual Studio Pre-Release versions. This is NEVER enabled for official production builds, however it is - useful when adding support for new versions during the pre-release stages. - .PARAMETER FullInit Performs a full initialization. A full initialization includes forcing a re-capture of the time stamp for local builds as well as writes details of the initialization to the information and verbose streams. @@ -16,68 +12,125 @@ .PARAMETER NoClone Skip cloning of the docs repository. Useful for inner loop development where you only do the clone once when iterating on doc updates. + +.PARAMETER ShowDocs + Show the docs via `docfx serve --open-browser ...`. This option is useful for inner loop development of the docs as it allows + opening a browser on the newly created docs for testing/checking the contents are produced correctly. + +.PARAMETER ServeDocs + Skips the docs build and implies ShowDocs to serve the current docs in the repo. This is useful after a previous + Build-All or similar validation on a local machine as that will also build the docs but won't show/serve them. #> Param( [string]$Configuration="Release", - [switch]$AllowVsPreReleases, [switch]$FullInit, - [switch]$NoClone + [switch]$NoClone, + [switch]$ShowDocs, + [switch]$ServeDocs ) +function Invoke-DocFX +{ + docfx $args + if(!$?) + { + throw "Invoke of dotnet returned exit code: $LastExitCode" + } +} + +function Get-FullBuildNumber +{ + # docfx no longer supports the docfxconsole and insists on doing everything manually + # with the command line instead of within a project file... So do it the hard way... + + # First step is to generate a JSON file with the version information that comes + # from the MSBUILD tasks in the `CSemVer.Build.Tasks` NUGET package. To do that + # a no target project is used to capture the results of the versioning into a + # JSON file, then that file is read in to get the values for PS so that a consistent + # build number is used for the docs generation. + + # generate the CurrentVersionInfo.json file + Write-Information 'Generating CurrentVersionInfo.json' + + Invoke-External dotnet build ./BuildVersion.msbuildproj | Out-Null + + # Read in the generated JSON for use in PS + $versionInfo = Get-Content ./CurrentVersionInfo.json | ConvertFrom-Json -AsHashTable + return "$($versionInfo['FullBuildNumber'])" +} + +$docFXToolVersion = '2.78.3' + +$InformationPreference = 'Continue' +$ErrorInformationPreference = 'Stop' + Push-Location $PSScriptRoot $oldPath = $env:Path try { - . .\repo-buildutils.ps1 - $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit -AllowVsPreReleases:$AllowVsPreReleases + . ./repo-buildutils.ps1 - $msBuildProperties = @{ Configuration = $Configuration - LlvmVersion = $buildInfo['LlvmVersion'] - } - $msBuildPropertyList = ConvertTo-PropertyList $msBuildProperties + $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit + + # make sure the supported tool is installed. + Invoke-External dotnet tool install --global docfx --version $docFXToolVersion | Out-Null $docsOutputPath = $buildInfo['DocsOutputPath'] + Write-Information "Docs OutputPath: $docsOutputPath" - # Clone docs output location so it is available as a destination for the Generated docs content - # and the versioned docs links can function correctly for locally generated docs - if(!$NoClone -and !(Test-Path (Join-Path $docsOutputPath '.git') -PathType Container)) + if(!$ServeDocs) { - if(Test-Path -PathType Container $docsOutputPath) + # Clone docs output location so it is available as a destination for the Generated docs content + # and the versioned docs links can function correctly for locally generated docs + if(!$NoClone -and !(Test-Path (Join-Path $docsOutputPath '.git') -PathType Container)) { - Remove-Item -Path $docsOutputPath -Recurse -Force + # clean out existing docs content so that clone can work + if(Test-Path -PathType Container $docsOutputPath) + { + Write-Information "Cleaning $docsOutputPath" + Remove-Item -Path $docsOutputPath -Recurse -Force -ProgressAction SilentlyContinue + } + + Write-Information "Cloning Docs repository" + Invoke-External git clone $buildInfo['OfficialGitRemoteUrl'] -b gh-pages $docsOutputPath -q } - Write-Information "Cloning Docs repository" - git clone https://github.com/UbiquityDotNET/Llvm.NET.git -b gh-pages $docsOutputPath -q - if(!$?) + # Delete everything in the docs output except the git folder so the result of applying changes + # is ONLY what is generated by this script. + Write-Information "Cleaning $docsOutputPath" + Get-ChildItem -Path $docsOutputPath -Exclude '.git' | remove-item -Recurse -Force -ProgressAction SilentlyContinue + + # Create a file to disable the default GitHub Pages use of JEKYLL as this uses docfx to generate the final + # HTML. [It is at least theoretically plausible that JEKYLL could handle some/all of the metadata files produced + # by DOCFX but it is not clear how much of the "build" stage would be lost if ONLY the metadata phase was used. + # Thus, for now, this uses the docfx build phase.] + "$([DateTime]::UtcNow.ToString('o'))" | Out-File -Path (Join-Path $docsOutputPath '.nojekyll') + + $fullBuildNumber = Get-FullBuildNumber + push-location './docfx' + try + { + Write-Information "Building docs [FullBuildNumber=$fullBuildNumber]" + Invoke-External docfx '-m' _buildVersion=$fullBuildNumber '-o' $docsOutputPath + } + finally { - throw "Git clone failed" + Pop-Location } } - - # remove all contents from 'current' docs to ensure clean generated docs for this release - $currentVersionDocsPath = Join-Path $docsOutputPath 'current' - if(Test-Path -PathType Container $currentVersionDocsPath) + else { - Remove-Item -Path $currentVersionDocsPath -Recurse -Force + $ShowDocs = $true } - $docfxRestoreBinLogPath = Join-Path $buildInfo['BinLogsPath'] Ubiquity.NET.Llvm-docfx-Restore.binlog - $docfxBuildBinLogPath = Join-Path $buildInfo['BinLogsPath'] Ubiquity.NET.Llvm-docfx-Build.binlog - - Write-Information "Building top level docs index" - dotnet build 'docfx\index\Ubiquity.NET.Llvm.Docfx.Index.csproj' -p:$msBuildPropertyList - - Write-Information "Building current version library docs" - dotnet build 'docfx\current\Ubiquity.NET.Llvm.Docfx.API.csproj' -p:$msBuildPropertyList - - # NOTE: Current state of DocFX requires a distinct restore pass. - #Invoke-MSBuild -Targets 'Restore' -Project docfx\Ubiquity.NET.Llvm.DocFX.sln -Properties $msBuildProperties -LoggerArgs ($buildInfo['MsBuildLoggerArgs'] + @("/bl:$docfxRestoreBinLogPath") ) - #Invoke-MSBuild -Targets 'Build' -Project docfx\Ubiquity.NET.Llvm.DocFX.sln -Properties $msBuildProperties -LoggerArgs ($buildInfo['MsBuildLoggerArgs'] + @("/bl:$docfxBuildBinLogPath") ) + if($ShowDocs) + { + Invoke-External docfx serve '--open-browser' $docsOutputPath + } } catch { - # everything from the official docs to the various articles in the blog-sphere says this isn't needed + # Everything from the official docs to the various articles in the blog-sphere says this isn't needed # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location # information is retained and the error reported will include the correct source file and line number # data for the error. Without this, only the error message is retained and the location information is @@ -86,6 +139,6 @@ catch } finally { - Push-Location + Pop-Location $env:Path = $oldPath } diff --git a/Build-Interop.ps1 b/Build-Native.ps1 similarity index 79% rename from Build-Interop.ps1 rename to Build-Native.ps1 index 4b501491e6..aa69fcb666 100644 --- a/Build-Interop.ps1 +++ b/Build-Native.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - Builds the native code Extended LLVM C API DLL along with the interop .NET assembly for it + Builds the native code Extended LLVM C API DLL .PARAMETER Configuration This sets the build configuration to use, default is "Release" though for inner loop development this may be set to "Debug" @@ -61,8 +61,9 @@ try # is insufficient to keep the size reasonable enough to support posting to public galleries. Additionally, the # support for native lib projects in NuGet is tenuous at best. Due to various compiler version dependencies # and incompatibilities libs are generally not something published in a package. However, since the build time - # for the libraries exceeds the time allowed for most hosted build services these must be pre-built for the - # automated builds. + # for the libraries exceeds the time allowed for most hosted build services, or the size of the output exceeds + # allowed footprint these must be pre-built for the automated builds. [At least, until an OSS friendly solution + # is found anyway] Install-LlvmLibs $buildInfo $msBuildProperties = @{ Configuration = $Configuration @@ -70,17 +71,16 @@ try } $msbuildPropertyList = ConvertTo-PropertyList $msBuildProperties + # Build and run the source generator as it produces the EXPORTS.g.def needed for the native code to build Write-Information "Building LllvmBindingsGenerator" - - dotnet build 'src\Interop\LlvmBindingsGenerator\LlvmBindingsGenerator.csproj' -p:$msbuildPropertyList + Invoke-DotNet build 'src\Interop\LlvmBindingsGenerator\LlvmBindingsGenerator.csproj' -p:$msbuildPropertyList Write-Information "Generating P/Invoke Bindings" - Write-Information "LlvmBindingsGenerator.exe $($buildInfo['LlvmLibsRoot']) $(Join-Path $buildInfo['SrcRootPath'] 'Interop\LibLLVM') $(Join-Path $buildInfo['SrcRootPath'] 'Interop\Ubiquity.NET.Llvm.Interop')" - - dotnet "$($buildInfo['BuildOutputPath'])\bin\LlvmBindingsGenerator\$Configuration\net5.0\LlvmBindingsGenerator.dll" $buildInfo['LlvmLibsRoot'] (Join-Path $buildInfo['SrcRootPath'] 'Interop\LibLLVM') (Join-Path $buildInfo['SrcRootPath'] 'Interop\Ubiquity.NET.Llvm.Interop') + Write-Verbose "LlvmBindingsGenerator $($buildInfo['LlvmLibsRoot']) $(Join-Path $buildInfo['SrcRootPath'] 'Interop\LibLLVM') $(Join-Path $buildInfo['SrcRootPath'] 'Interop\Ubiquity.NET.Llvm.Interop')" + Invoke-DotNet "$($buildInfo['BuildOutputPath'])\bin\LlvmBindingsGenerator\$Configuration\net8.0\LlvmBindingsGenerator.dll" $buildInfo['LlvmLibsRoot'] (Join-Path $buildInfo['SrcRootPath'] 'Interop\LibLLVM') (Join-Path $buildInfo['SrcRootPath'] 'Interop\Ubiquity.NET.Llvm.Interop') if($LASTEXITCODE -eq 0) { - # now build the projects that consume generated output for the bindings + # now build the native DLL that consumes the generated output for the bindings # Need to invoke NuGet directly for restore of vcxproj as /t:Restore target doesn't support packages.config # and PackageReference isn't supported for native projects... [Sigh...] @@ -94,10 +94,14 @@ try Write-Information "Building LibLLVM" $libLLVMBinLogPath = Join-Path $buildInfo['BinLogsPath'] LibLLVM-Build.binlog - Invoke-MSBuild -Targets 'Build' -Project 'src\Interop\LibLLVM\LibLLVM.vcxproj' -Properties $msBuildProperties -LoggerArgs ($buildInfo['MsBuildLoggerArgs'] + @("/bl:$libLLVMBinLogPath") ) - Write-Information "Building Ubiquity.NET.Llvm.Interop" - dotnet build 'src\Interop\Interop.sln' -p:$msbuildPropertyList + # LibLlvm ONLY has a release configuration, the interop lib only ever references that. + # The libraries are just too big to produce a Debug build with symbols etc... + # Force a release build no matter what the "configuration" parameter is. + $libLLvmBuildProps = @{ Configuration = 'Release' + LlvmVersion = $buildInfo['LlvmVersion'] + } + Invoke-MSBuild -Targets 'Build' -Project 'src\Interop\LibLLVM\LibLLVM.vcxproj' -Properties $libLLvmBuildProps -LoggerArgs ($buildInfo['MsBuildLoggerArgs'] + @("/bl:$libLLVMBinLogPath") ) } else { diff --git a/Build-Source.ps1 b/Build-Source.ps1 index ded2cf057d..9bbe9be7b8 100644 --- a/Build-Source.ps1 +++ b/Build-Source.ps1 @@ -12,10 +12,14 @@ .PARAMETER FullInit Performs a full initialization. A full initialization includes forcing a re-capture of the time stamp for local builds as well as writes details of the initialization to the information and verbose streams. + +.PARAMETER ZipNuget + Zips all of the nuget packages into a single zip. This is NOT ordinarily needed and consumes a significant abount of time + so it is provided as a flag. Publishing of NUGET packages is now part of the GitHub actions for automated builds and does + not need this. #> Param( [string]$Configuration="Release", - [switch]$AllowVsPreReleases, [switch]$FullInit ) @@ -27,23 +31,15 @@ try # based on the switch parameter. Normally FullInit is done in Build-All, which calls this # script. But for a local "inner loop" development this might be the only script used. . .\repo-buildutils.ps1 - $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit -AllowVsPreReleases:$AllowVsPreReleases - - # build the interop layer first using the script to manage the complexities of generated marshaling Interop - # and dependencies between C# and C++ projects. - .\Build-Interop.ps1 -AllowVsPreReleases:$AllowVsPreReleases - - # build the Managed code OO Wrapper layer - Write-Information "dotnet build 'src\Ubiquity.NET.Llvm.sln' -c $Configuration -p:`"LlvmVersion=$($buildInfo['LlvmVersion'])`"" - dotnet build 'src\Ubiquity.NET.Llvm.sln' -c $Configuration -p:"LlvmVersion=$($buildInfo['LlvmVersion'])" + $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit - # Create a ZIP file of all the nuget packages - pushd $buildInfo['NuGetOutputPath'] - Compress-Archive -Force -Path *.* -DestinationPath (join-path $buildInfo['BuildOutputPath'] Nuget.Packages.zip) + # build the Managed code support + Write-Information "dotnet build 'src\Ubiquity.NET.Llvm.slnx' -c $Configuration" + Invoke-External dotnet build --tl:off 'src\Ubiquity.NET.Llvm.slnx' '-c' $Configuration } catch { - # everything from the official docs to the various articles in the blog-sphere says this isn't needed + # Everything from the official docs to the various articles in the blog-sphere say this isn't needed # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location # information is retained and the error reported will include the correct source file and line number # data for the error. Without this, only the error message is retained and the location information is diff --git a/BuildVersion.msbuildproj b/BuildVersion.msbuildproj new file mode 100644 index 0000000000..f41048b76c --- /dev/null +++ b/BuildVersion.msbuildproj @@ -0,0 +1,45 @@ + + + net9.0 + + + + + + + + + <__InRestore>true + + + + + + + + + + + + + + + + + + <__Line Include="{"/> + <__Line Include=" "FullBuildNumber" : "$(FullBuildNumber)"," /> + <__Line Include=" "BuildMajor" : $(BuildMajor)," /> + <__Line Include=" "BuildMinor" : $(BuildMinor)," /> + <__Line Include=" "BuildPatch" : $(BuildPatch)," /> + <__Line Include=" "PreReleaseName" : "$(PreReleaseName)"," /> + <__Line Include=" "CiBuildName" : "$(CiBuildName)"," /> + <__Line Include=" "CiBuildIndex" : $(CiBuildIndex)," /> + <__Line Condition="'$(PreReleaseNumber)'!=''" Include=""PreReleaseNumber" : $(PreReleaseNumber)," /> + <__Line Condition="'$(PreReleaseFix)'!=''" Include=""PreReleaseFix" : $(PreReleaseFix)," /> + <__Line Condition="'$(BuildMeta)'!=''" Include=""BuildMeta" : "$(BuildMeta)"," /> + <__Line Include="}"/> + + + + diff --git a/BuildVersion.xml b/BuildVersion.xml index a0de5ac1bc..b0bf6dff51 100644 --- a/BuildVersion.xml +++ b/BuildVersion.xml @@ -1,6 +1,6 @@ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 104d96f532..a72b9bd451 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,10 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making +participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, +disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, +religion, or sexual identity and orientation. ## Our Standards @@ -24,23 +27,34 @@ Examples of unacceptable behavior by participants include: ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate +and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, +and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor +for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project +or its community. Examples of representing a project or community include using an official project e-mail address, posting +via an official social media account, or acting as an appointed representative at an online or offline event. Representation +of a project may be further defined and clarified by project maintainers. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at osscodeofconduct@telliam.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at +osscodeofconduct@telliam.com. The project team will review and investigate all complaints, and will respond in a way that +it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the +reporter of an incident. Further details of specific enforcement policies may be posted separately. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions +as determined by other members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at +[http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/Directory.Build.props b/Directory.Build.props index ab8f32b8aa..d2652a780b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,7 +9,7 @@ Projects importing this property sheet can still override those settings. Implementation Note: - ALL properties defined here should be done conditional to allow inheriting from + ALL properties defined here should be done conditionally to allow inheriting from a parent. Conditional properties makes this project a well behaved sub module for other projects that might want to set the build outputs etc... into a different place. --> @@ -34,7 +34,8 @@ $([MSBuild]::NormalizeDirectory("$(BaseBuildOutputPath)", "Intermediate", "$(MSBuildProjectName)")) $([MSBuild]::NormalizeDirectory("$(BaseIntermediateOutputPath)", "$(UnifiedPlatformPathName)", "$(Configuration)")) - $([MSBuild]::NormalizeDirectory("$(BaseBuildOutputPath)","bin", "$(MSBuildProjectName)")) + $([MSBuild]::NormalizeDirectory("$(BaseBuildOutputPath)", "bin")) + $([MSBuild]::NormalizeDirectory("$(BaseBuildOutputBinPath)", "$(MSBuildProjectName)")) $([MSBuild]::NormalizeDirectory("$(BaseBuildOutputPath)","NuGet")) $(BaseBuildOutputPath)packages\ @@ -44,25 +45,40 @@ false false $(APPVEYOR_REPO_COMMIT_TIMESTAMP) - 10 - 0 - 0 + 20 + 1 + 1 + $(LlvmVersionMajor).$(LlvmVersionMinor).$(LlvmVersionPatch) + + [$(LlvmVersion), 21.0.0) - + + + + true + + + + - $(MSBuildThisFileDirectory)Ubiquity.NET.ruleset true true - $(MSBuildProjectName.Contains('Test')) enable + true + true + + disable + - + diff --git a/Directory.Build.targets b/Directory.Build.targets index 38ea4b5399..f112cb20aa 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,21 +1,20 @@ - + + --> + Since Nuget.config is configured to include the build output location this + will ensure the folder exists during restore so that it won't fail. + --> - + - $(LlvmVersionMajor).$(LlvmVersionMinor).$(LlvmVersionPatch) version=$(PackageVersion);llvmversion=$(LlvmVersion);buildbinoutput=$(BaseOutputPath);configuration=$(Configuration);$(NuspecProperties) @@ -42,7 +41,7 @@ $(Description) $(llvmVersion) - + @@ -62,9 +61,9 @@ + var client = new WebClient(); + client.DownloadFile( SourceUrl, DestinationPath ); + ]]> @@ -79,9 +78,10 @@ - + + @@ -92,4 +92,100 @@ + + + + + + + + + + + <_ProjectReferencesWithVersions Condition="'%(FullPath)' != ''"> + @(ProjectReference->'%(PackageVersion)') + + <_ProjectReferencesWithVersions Condition="'%(Identity)' != '' And '%(PackageVersion)' != ''"> + $([System.String]::new('%(PackageVersion)').Replace('~',%(ProjectVersion))) + + + + + + + + + $(BaseBuildOutputPath)packages\Java + $(BaseBuildOutputPath)packages\antlr4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000000..7121853af8 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IgnoredWords.dic b/IgnoredWords.dic new file mode 100644 index 0000000000..3f5eb797e2 --- /dev/null +++ b/IgnoredWords.dic @@ -0,0 +1,112 @@ +ABI +Addr +alloca +anonymize +antlr +api +app +apps +argkind +ascii +attrib +Attribs +AttributeSet +baz +binaryop +binlogs +binop +bitcode +Blinky +blockdiag +blog +bool +buildbinoutput +buildtransitive +builtinop +byref +byval +cibuild +Cmp +Config +const +contentfiles +Contributors +crlf +csharp +csproj +de +defacto +dllimport +docfx +docfxconsole +dotnet +enum +Enums +env +exe +facepalm +foo +fullsrc +func +gh +github +Globalization +Hashtable +Identifier +inline +inlined +Interop +Lexer +LibLLVM +Llilum +llvm +llvmversion +LValue +marshalling +memcopy +metadata +Mips +msbuild +msg +nav +noinline +nounwind +nullability +Nullable +optimizenone +pages +paren +perf +pointee +Pre +proj +readonly +refactor +repl +repo +RMW +runtimes +RValue +src +struct +structs +Subrange +telliam +tl +trx +typdef +Typedef +uid +unaryop +undiscoverable +userdefinedop +Users +usings +utils +vcxproj +versioned +versioning +Xchg +xref +yaml +yyyy diff --git a/Invoke-UnitTests.ps1 b/Invoke-UnitTests.ps1 index 39cbed61f8..48008100ba 100644 --- a/Invoke-UnitTests.ps1 +++ b/Invoke-UnitTests.ps1 @@ -5,54 +5,49 @@ .PARAMETER Configuration This sets the build configuration to use, default is "Release" though for inner loop development this may be set to "Debug" -.PARAMETER AllowVsPreReleases - Switch to enable use of Visual Studio Pre-Release versions. This is NEVER enabled for official production builds, however it is - useful when adding support for new versions during the pre-release stages. - .PARAMETER FullInit Performs a full initialization. A full initialization includes forcing a re-capture of the time stamp for local builds as well as writes details of the initialization to the information and verbose streams. #> Param( [string]$Configuration="Release", - [switch]$AllowVsPreReleases, [switch]$FullInit ) try { . .\repo-buildutils.ps1 - $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit -AllowVsPreReleases:$AllowVsPreReleases - - $testsFailed = $false - - Write-Information 'Running Interop tests...' - $testsFailed = $testsFailed -or (Invoke-DotNetTest $buildInfo 'src\Interop\InteropTests\InteropTests.csproj') - - Write-Information 'Running Core library tests...' - $testsFailed = $testsFailed -or (Invoke-DotNetTest $buildInfo 'src\Ubiquity.NET.Llvm.Tests\Ubiquity.NET.Llvm.Tests.csproj') + $buildInfo = Initialize-BuildEnvironment -FullInit:$FullInit - Write-Information 'Running JIT library tests...' - $testsFailed = $testsFailed -or (Invoke-DotNetTest $buildInfo 'src\Ubiquity.NET.Llvm.JIT.Tests\Ubiquity.NET.Llvm.JIT.Tests.csproj') - - Write-Information 'Running tests for Kaleidoscope Samples...' - $testsFailed = $testsFailed -or (Invoke-DotNetTest $buildInfo 'Samples\Kaleidoscope\Kaleidoscope.Tests\Kaleidoscope.Tests.csproj') - - Write-Information 'Running sample app for .NET Core' - Push-Location (Join-path $buildInfo['BuildOutputPath'] 'bin\CodeGenWithDebugInfo\Release\net5.0') + Push-Location $BuildInfo["SrcRootPath"] try { - dotnet CodeGenWithDebugInfo.dll M3 'Support Files\test.c' $buildInfo['TestResultsPath'] - $testsFailed = $testsFailed -or ($LASTEXITCODE -ne 0) + Invoke-External dotnet test Ubiquity.NET.Llvm.slnx '-c' $Configuration '-tl:off' '--logger:trx' '--no-build' '-s' '.\x64.runsettings' } finally { Pop-Location } - if($testsFailed) + # ensure the samples run - output not validated but they need to compile and run without crashing + Write-Information 'Running sample apps for .NET Core' + Push-Location (Join-path $buildInfo['BuildOutputPath'] 'bin\CodeGenWithDebugInfo\Release\net9.0') + try { - throw "One or more tests failed - Build should fail" + $testGenOutputPath = Join-Path $buildInfo['TestResultsPath'] 'M3' + Write-Information "CodeGenWithDebugInfo M3 'Support Files\test.c' $testGenOutputPath" + Invoke-External dotnet CodeGenWithDebugInfo.dll M3 'Support Files\test.c' $testGenOutputPath + + $testGenOutputPath = Join-Path $buildInfo['TestResultsPath'] 'X64' + Write-Information "CodeGenWithDebugInfo X64 'Support Files\test.c' $testGenOutputPath" + Invoke-External dotnet CodeGenWithDebugInfo.dll X64 'Support Files\test.c' $testGenOutputPath + + Set-Location (Join-path $buildInfo['BuildOutputPath'] 'bin\OrcV2VeryLazy\Release\net9.0') + Invoke-External dotnet OrcV2VeryLazy.dll + } + finally + { + Pop-Location } } catch diff --git a/Packages.props b/Packages.props deleted file mode 100644 index e2fd8e3349..0000000000 --- a/Packages.props +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/PsModules/CommonBuild/CommonBuild.psd1 b/PsModules/CommonBuild/CommonBuild.psd1 index b916289837..bc3cb74415 100644 --- a/PsModules/CommonBuild/CommonBuild.psd1 +++ b/PsModules/CommonBuild/CommonBuild.psd1 @@ -3,7 +3,7 @@ RootModule = 'CommonBuild.psm1' # Version number of this module. -ModuleVersion = '1.0.0' +ModuleVersion = '2.0.0' # ID used to uniquely identify this module GUID = '6da1bba8-442f-4bc3-9991-9c8783df06a4' @@ -15,16 +15,16 @@ Author = 'Ubiquity.NET Contributors' CompanyName = 'Ubquity.NET' # Copyright statement for this module -Copyright = '(c) 2020 Ubiquity.NET Contributors. All rights reserved.' +Copyright = '(c) 2020-2025 Ubiquity.NET Contributors. All rights reserved.' # Description of the functionality provided by this module Description = 'Common build support functions for Ubiquity.NET projects' # Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '5.1' +PowerShellVersion = '7.0' # PowerShell editions this module is compatible with -CompatiblePSEditions = @('Core', 'Desktop') +CompatiblePSEditions = @('Core') # Name of the Windows PowerShell host required by this module # PowerShellHostName = '' @@ -61,29 +61,32 @@ CompatiblePSEditions = @('Core', 'Desktop') # Functions to export from this module FunctionsToExport = @( + 'Get-FunctionsToExport' + 'Assert-CmakeInfo', + 'Assert-CMakeList', + 'Assert-OfficialGitRemote', + 'Get-BuildVersionTag', + 'Assert-IsCMakeConfig', + 'New-CMakeConfig', + 'Invoke-GenerateCMakeConfig', + 'Build-CmakeConfig', 'ConvertTo-NormalizedPath', 'ConvertTo-PropertyList', - 'Expand-7zArchive', 'Expand-ArchiveStream', 'Expand-StreamFromUri', - 'Find-7Zip', - 'Find-MSBuild', 'Find-OnPath', - 'Find-VSInstance', - 'Get-CurrentBuildKind' + 'Get-BuildVersionTag', + 'Get-CurrentBuildKind', + 'Get-GitRemotes', + 'Get-GitRemoteName', 'Get-GitHubReleases', 'Get-GitHubTaggedRelease', - 'Invoke-TimedBlock', - 'Invoke-MSBuild', - 'Invoke-NuGet', - 'Update-Submodules', - 'Invoke-DotNetTest', 'Initialize-CommonBuildEnvironment', - 'Show-FullBuildInfo', - 'Invoke-Git' - 'Get-BuildVersionTag' + 'Invoke-External', + 'Invoke-TimedBlock', + 'New-CmakeSettings', + 'Show-FullBuildInfo' ) - # Cmdlets to export from this module CmdletsToExport = '*' diff --git a/PsModules/CommonBuild/CommonBuild.psm1 b/PsModules/CommonBuild/CommonBuild.psm1 index d7bbec887a..207d310d26 100644 --- a/PsModules/CommonBuild/CommonBuild.psm1 +++ b/PsModules/CommonBuild/CommonBuild.psm1 @@ -1,827 +1,119 @@ -# Repository neutral common build support utilities -# This library is intended for use across multiple repositories -# and therefore, should only contain functionality that is independent -# of the particulars of any given repository. +# This script is a generalized script entry point for module support that is easily cloned for other modules +# and is independent of the module's functionality. This is invocable using the PS module name qualifier +# syntax `/Get-ExportedFunctionNames` -function Ensure-PathExists +function Get-ExportedFunctionNames { - param([Parameter(Mandatory=$true, ValueFromPipeLine)]$path) - mkdir -Force -ErrorAction SilentlyContinue $path | Out-Null -} - -function Get-DefaultBuildPaths([string]$repoRoot) -{ -<# -.SYNOPSIS - Gets the default set of paths for a build - -.DESCRIPTION - This function initializes a hash table with the default paths for a build. This - allows for standardization of build output locations etc... across builds and repositories - in the organization. The values set are as follows: - - | Name | Description | - |---------------------|--------------------------------------| - | RepoRootPath | Root of the repository for the build | - | BuildOutputPath | Base directory for all build output during the build | - | NuGetRepositoryPath | NuGet 'packages' directory for C++ projects using packages.config | - | NuGetOutputPath | Location where NuGet packages created during the build are placed | - | SrcRootPath | Root of the source code for this repository | - | DocsOutputPath | Root path for the generated documentation for the project | - | BinLogsPath | Path to where the binlogs are generated for PR builds to allow diagnosing failures in the automated builds | - | TestResultsPath | Path to where test results are placed. | - | DownloadsPath | Location where any downloaded files, used by the build are placed | - | ToolsPath | Location of any executable tools downloaded for the build (Typically expanded from a compressed download) | -#> - $buildOutputPath = ConvertTo-NormalizedPath (Join-Path $repoRoot 'BuildOutput') - $buildPaths = @{ - RepoRootPath = $repoRoot - BuildOutputPath = $buildOutputPath - NuGetRepositoryPath = Join-Path $buildOutputPath 'packages' - NuGetOutputPath = Join-Path $buildOutputPath 'NuGet' - SrcRootPath = Join-Path $repoRoot 'src' - DocsRepoPath = Join-Path $buildOutputPath 'docs' - DocsOutputPath = Join-Path $buildOutputPath 'docs' - BinLogsPath = Join-Path $buildOutputPath 'BinLogs' - TestResultsPath = Join-Path $buildOutputPath 'Test-Results' - DownloadsPath = Join-Path $repoRoot 'Downloads' - ToolsPath = Join-Path $repoRoot 'Tools' - } - - $buildPaths.GetEnumerator() | %{ Ensure-PathExists $_.Value } - return $buildPaths -} - -function Verify-OfficialGitRemote -{ -<# -.SYNOPSIS - Verifies the current git remote is the official repository - -.PARAMETER OfficialUrl - URL of the official remote to Verify - -.PARAMETER Activity - String describing the activity. This is used in the exception message stating - that the activity is only valid with the correct remote URL - -.DESCRIPTION - Some operations like, Release tags, and docs updates must only be pushed from a repository with the - official GitHub repository as the origin remote. This, among other things, ensures that the links - to source in the generated docs will have the correct URLs (e.g. docs pushed to the official repository - MUST not have links to source in some private fork). This function is used, primarily in OneFlow release - management scripts to ensure operations are done using the correct remote. -#> - Param( - [Parameter(Mandatory=$True)][string]$OfficialUrl, - [Parameter(Mandatory=$True)][string]$Activity - ) - - # Release tags must only be pushed from a repository with the official GitHub repository as the origin remote. - # This ensures that the links to source in the generated docs will have the correct URLs - # (e.g. docs pushed to the official repository MUST not have links to source in some private fork) - $remoteUrl = Invoke-Git ls-remote --get-url - - if($remoteUrl -ine "https://github.com/UbiquityDotNET/Llvm.NET.git") - { - throw "$Activity is only allowed when the origin remote is the official repository - current remote is '$remoteUrl'" - } - -} - -function Update-Submodules -{ -<# -.SYNOPSIS - Updates Git submodules for this repository -#> - Write-Information "Updating submodules" - git submodule -q update --init --recursive -} - -function Find-OnPath -{ -<# -.SYNOPSIS - Searches for an executable on the current environment search path - -.PARAMETER exeName - The executable to search for - -.NOTES - This is a simple wrapper around the command line 'where' utility to select the first location found -#> - [CmdletBinding()] - Param( [Parameter(Mandatory=$True,Position=0)][string]$exeName) - $path = $null - Write-Information "Searching for $exeName..." - try - { - $path = where.exe $exeName 2>$null | select -First 1 - } - catch - { - # everything from the official docs to the various articles in the blog-sphere says this isn't needed - # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location - # information is retained and the error reported will include the correct source file and line number - # data for the error. Without this, only the error message is retained and the location information is - # Line 1, Column 1, of the outer most script file, which is, of course, completely useless. - throw - } - - if($path) - { - Write-Information "Found $exeName at: '$path'" - } - return $path -} - -function ConvertTo-NormalizedPath([string]$path ) -{ -<# -.SYNOPSIS - Converts a potentially relative folder path to an absolute one with a trailing delimiter - -.PARAMETER path - Path to convert - -.NOTES - The delimiters in the path are converted to the native system preferred form during conversion -#> - if(![System.IO.Path]::IsPathRooted($path)) - { - $path = [System.IO.Path]::Combine((pwd).Path,$path) - } - - $path = [System.IO.Path]::GetFullPath($path) - if( !$path.EndsWith([System.IO.Path]::DirectorySeparatorChar) -and !$path.EndsWith([System.IO.Path]::AltDirectorySeparatorChar)) - { - $path = $path + [System.IO.Path]::DirectorySeparatorChar - } - return $path -} - -function ConvertTo-PropertyList([hashtable]$hashTable) -{ -<# -.SYNOPSIS - Converts a hash table into a semi-colon delimited property list -#> - return ( $hashTable.GetEnumerator() | %{ @{$true=$_.Key;$false= $_.Key + "=" + $_.Value }[[string]::IsNullOrEmpty($_.Value) ] } ) -join ';' -} - -function Invoke-TimedBlock([string]$activity, [ScriptBlock]$block) -{ -<# -.SYNOPSIS - Invokes a script block with a timer - -.PARAMETER activity - Name of the activity to output as part of Write-Information messages for the timer - -.PARAMETER block - Script block to execute with the timer - -.DESCRIPTION - This will print a start (via Write-Information), start the timer, run the script block stop the timer - then print a finish message indicating the total time the script block took to run. -#> - $timer = [System.Diagnostics.Stopwatch]::StartNew() - Write-Information "Starting: $activity" - try - { - $block.Invoke() - } - catch - { - # everything from the official docs to the various articles in the blog-sphere says this isn't needed - # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location - # information is retained and the error reported will include the correct source file and line number - # data for the error. Without this, only the error message is retained and the location information is - # Line 1, Column 1, of the outer most script file, which is, of course, completely useless. - throw - } - finally - { - $timer.Stop() - Write-Information "Finished: $activity - Time: $($timer.Elapsed.ToString())" - } -} - -function Expand-ArchiveStream([Parameter(Mandatory=$true, ValueFromPipeLine)]$src, [Parameter(Mandatory=$true)]$OutputPath) -{ -<# -.SYNOPSIS - Expands an archive stream - -.PARAMETER src - Input stream containing compressed ZIP archive data to expand - -.PARAMETER OutputPath - Out put destination for the decompressed data -#> - $zipArchive = [System.IO.Compression.ZipArchive]::new($src) - [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory( $zipArchive, $OutputPath) -} - -function Expand-StreamFromUri([Parameter(Mandatory=$true, ValueFromPipeLine)]$uri, [Parameter(Mandatory=$true)]$OutputPath) -{ -<# -.SYNOPSIS - Downloads and expands a ZIP file to the specified destination - -.PARAMETER uri - URI of the ZIP file to download and expand - -.PARAMETER OutputPath - Output folder to expand the ZIP contents into -#> - $strm = (Invoke-WebRequest -UseBasicParsing -Uri $uri).RawContentStream - Expand-ArchiveStream $strm $OutputPath -} - -function Invoke-NuGet -{ -<# -.SYNOPSIS - Invokes NuGet with any arguments provided - -.DESCRIPTION - This will attempt to find Nuget.exe on the current path, and if not found will download the latest - version from NuGet.org before running the command. -#> - $buildPaths = Get-DefaultBuildPaths ([IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))) - $NuGetPaths = Find-OnPath NuGet.exe -ErrorAction Continue - if( !$NuGetPaths ) - { - $nugetToolsPath = Join-Path $buildPaths['ToolsPath'] 'NuGet.exe' - if( !(Test-Path $nugetToolsPath)) - { - # Download it from official NuGet release location - Write-Verbose "Downloading Nuget.exe to $nugetToolsPath" - Invoke-WebRequest -UseBasicParsing -Uri https://dist.NuGet.org/win-x86-commandline/latest/NuGet.exe -OutFile $nugetToolsPath - } - - $env:Path = "$env:Path;$($buildPaths['ToolsPath'])" - } - - Write-Information "NuGet $args" - NuGet.exe $args - $err = $LASTEXITCODE - if($err -ne 0) - { - throw "Error running NuGet: $err" - } -} - -function Find-VSInstance([switch]$PreRelease, $Version = '[15.0, 18.0)', [string[]]$requiredComponents = @('Microsoft.Component.MSBuild')) -{ -<# -.SYNOPSIS - Finds an installed VS instance - -.PARAMETER PreRelease - Indicates if the search should include pre-release versions of Visual Studio - -.PARAMETER version - The version range to search for. [Default is '[15.0, 18.0)'] - -.PARAMETER requiredComponents - The set of required components to search for. [Default is an empty array] - -.DESCRIPTION - Uses the official MS provided PowerShell module to find a VS instance. If the VSSetup - module is not loaded it is loaded first. If it isn't installed, then the module is installed. -#> - $forceModuleInstall = [System.Convert]::ToBoolean($env:IsAutomatedBuild) - $existingModule = Get-InstalledModule -ErrorAction SilentlyContinue VSSetup - if(!$existingModule) - { - Write-Information "Installing VSSetup module" - Install-Module VSSetup -Scope CurrentUser -Force:$forceModuleInstall | Out-Null - } - - $vs = Get-VSSetupInstance -Prerelease:$PreRelease | - Select-VSSetupInstance -Version $Version -Require $requiredComponents | - select -Last 1 - - return $vs -} - -function Find-MSBuild([switch]$AllowVsPreReleases) -{ -<# -.SYNOPSIS - Locates MSBuild if not already in the environment path - -.DESCRIPTION - Attempts to find MSBuild on the current environment path, if not found uses Find-VSInstance - to locate a Visual Studio instance that can provide an MSBuild. - -.PARAMETER AllowVsPreReleases - Switch to indicate if the search for a VS Instance should include pre-release versions. -#> - $foundOnPath = $true - $msBuildPath = Find-OnPath msbuild.exe -ErrorAction Continue - if( !$msBuildPath ) - { - Write-Verbose "MSBuild not found on PATH attempting to locate VS installation" - $vsInstall = Find-VSInstance -Prerelease:$AllowVsPreReleases - if( !$vsInstall ) - { - throw "MSBuild not found on PATH and No instances of VS found to use" - } - - Write-Verbose "VS installation found: $($vsInstall | Format-List | Out-String)" - $msBuildPath = [System.IO.Path]::Combine( $vsInstall.InstallationPath, 'MSBuild', '15.0', 'bin', 'MSBuild.exe') - if(!(Test-Path -PathType Leaf $msBuildPath)) - { - $msBuildPath = [System.IO.Path]::Combine( $vsInstall.InstallationPath, 'MSBuild', 'current', 'bin', 'MSBuild.exe') - } - - $foundOnPath = $false - } - - if( !(Test-Path -PathType Leaf $msBuildPath ) ) - { - Write-Information 'MSBuild not found' - return $null - } - - Write-Verbose "MSBuild Found at: $msBuildPath" - $versionInfo = & $msBuildPath -version - return @{ FullPath=$msBuildPath - BinPath=[System.IO.Path]::GetDirectoryName( $msBuildPath ) - FoundOnPath=$foundOnPath - Version = $versionInfo[-1] - } -} - -function Invoke-MSBuild([string]$project, [hashtable]$properties, $targets, $loggerArgs=@(), $additionalArgs=@()) -{ -<# -.SYNOPSIS - Invokes MSBuild with the options provided - -.PARAMETER project - The project to build - -.PARAMETER properties - Hash table of properties to provide to MSBuild - -.PARAMETER targets - MSBuild targets to build - -.PARAMETER loggerArgs - MSBuild logger arguments - -.PARAMMETER additionalArgs - Additional args for MSBuild -#> - $projName = [System.IO.Path]::GetFileNameWithoutExtension($project) - $msbuildArgs = @($project, "/m", "/t:$($targets -join ';')") + $loggerArgs + $additionalArgs - if( $properties ) - { - $msbuildArgs += @( "/p:$(ConvertTo-PropertyList $properties)" ) - } - - Write-Information "msbuild $($msbuildArgs -join ' ')" - msbuild $msbuildArgs - if($LASTEXITCODE -ne 0) - { - Write-Error "Error running msbuild: $LASTEXITCODE" -ErrorAction Stop - } -} - -function Find-7Zip() -{ -<# -.SYNOPSIS - Attempts to find 7zip console command - -.DESCRIPTION - This will try to find 7z.exe on the current path, and if not found tries to find the registered install location -#> - $path7Z = Find-OnPath '7z.exe' -ErrorAction SilentlyContinue - if(!$path7Z) - { - if( Test-Path -PathType Container HKLM:\SOFTWARE\7-Zip ) - { - $path7Z = Join-Path (Get-ItemProperty HKLM:\SOFTWARE\7-Zip\ 'Path').Path '7z.exe' - } - - if( !$path7Z -and ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") ) - { - $hklm = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64) - $subKey = $hklm.OpenSubKey("SOFTWARE\7-Zip") - $root = $subKey.GetValue("Path") - if($root) - { - $path7Z = Join-Path $root '7z.exe' - } - } - } - - if(!$path7Z -or !(Test-Path -PathType Leaf $path7Z ) ) - { - throw "Can't find 7-zip command line executable" - } - - return $path7Z -} - -Function Expand-7zArchive([string]$Path, [string]$Destination) -{ -<# -.SYNOPSIS - Expands an archive packed with 7-Zip -#> - $7zPath = Find-7Zip - $7zArgs = @( - 'x' # eXtract with full path - '-y' # auto apply Yes for all prompts - "`"-o$($Destination)`"" # Output directory - "`"$($Path)`"" # 7z file name + param( + [System.IO.FileInfo[]] $publics = @( Get-ChildItem -Path (Join-path $PSScriptroot 'Public' '*.ps1') -ErrorAction SilentlyContinue ) ) - Write-Information "Expanding '$Path' to '$Destination'" - & $7zPath $7zArgs | Out-Null -} - -enum BuildKind -{ - LocalBuild - PullRequestBuild - CiBuild - ReleaseBuild -} - -function Get-CurrentBuildKind -{ -<# -.SYNOPSIS - Determines the kind of build for the current environment -.DESCRIPTION - This function retrieves environment values set by various automated builds - to determine the kind of build the environment is for. The return is one of - the [BuildKind] enumeration values: - - | Name | Description | - |------------------|-------------| - | LocalBuild | This is a local developer build (e.g. not an automated build) - | PullRequestBuild | This is a build from a PullRequest with untrusted changes, so build should limit the steps appropriately | - | CiBuild | This build is from a Continuous Integration (CI) process, usually after a PR is accepted and merged to the branch | - | ReleaseBuild | This is an official release build, the output is ready for publication (Automated builds may use this to automatically publish) | -#> - [OutputType([BuildKind])] - param() - - $currentBuildKind = [BuildKind]::LocalBuild - - # IsAutomatedBuild is the top level gate (e.g. if it is false, all the others must be false) - $isAutomatedBuild = [System.Convert]::ToBoolean($env:CI) ` - -or [System.Convert]::ToBoolean($env:APPVEYOR) ` - -or [System.Convert]::ToBoolean($env:GITHUB_ACTIONS) - - if( $isAutomatedBuild ) + $retVal = New-Object -TypeName 'System.Collections.ArrayList' + foreach($pubScript in $publics) { - # PR and release builds have externally detected indicators that are tested - # below, so default to a CiBuild (e.g. not a PR, And not a RELEASE) - $currentBuildKind = [BuildKind]::CiBuild + $script = Get-Command $pubscript.FullName - # IsPullRequestBuild indicates an automated buddy build and should not be trusted - $isPullRequestBuild = $env:GITHUB_BASE_REF -or $env:APPVEYOR_PULL_REQUEST_NUMBER + # use PS built-in parser to find all of the public functions + $functionNames = $script.ScriptBlock.AST.FindAll( + { $args[0] -is [Management.Automation.Language.FunctionDefinitionAst] }, + $false + ).Name - if($isPullRequestBuild) + foreach($name in $functionNames) { - $currentBuildKind = [BuildKind]::PullRequestBuild - } - else - { - if([System.Convert]::ToBoolean($env:APPVEYOR)) - { - $isReleaseBuild = $env:APPVEYOR_REPO_TAG - } - elseif([System.Convert]::ToBoolean($env:GITHUB_ACTIONS)) + if($name -like '*-*') { - $isReleaseBuild = $env:GITHUB_REF -like 'refs/tags/*' + $retVal.Add($name) | Out-Null } - - if($isReleaseBuild) + else { - $currentBuildKind = [BuildKind]::ReleaseBuild + Write-Information "found non-standard function name '$name' in '$script'" } } } - return $currentBuildKind -} - -function Get-GitHubReleases($org, $project) -{ -<# -.SYNOPSIS - Gets a collection of the GitHub releases for a project - -.DESCRIPTION - This function retrieves a collection of releases from a given GitHub organization and project. - The result is a collection of GitHub releases as JSON data. - -.PARAMETER org - GitHub organization name that owns the project - -.PARAMETER project - GitHub project to retrieve releases from -#> - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - - $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/$org/$project/releases" - foreach($r in $releases) - { - $r - } -} - -function Get-GitHubTaggedRelease($org, $project, $tag) -{ -<# -.SYNOPSIS - Gets a specific tagged release for a GitHub project - -.DESCRIPTION - This function retrieves a single tagged release from a given GitHub organization and project. - The result is a GitHub release as JSON data. - -.PARAMETER org - GitHub organization name that owns the project - -.PARAMETER project - GitHub project to retrieve releases from - -.PARAMETER tag - Tag to find the specific release for -#> - - Get-GithubReleases $org $project | ?{$_.tag_name -eq $tag} -} - -function Invoke-DotNetTest($buildInfo, $projectRelativePath) -{ -<# -.SYNOPSIS - Invokes specified .NET tests for a project - -.DESCRIPTION - This invokes 'dotnet.exe test ...' for the relative project path. The absolute path for the test to - run is derived from the buildInfo parameter. - -.PARAMETER buildInfo - Hashtable of properties for the build. This function only requires two properties: "RepoRootPath", which is the Root - of the repository this build is for and "SrcRootPath" that refers to the root of the source code of the repository. The - relative project path is combined with the "RepoRootPath" to get the absolute path of the project to test. Additionally, - there must be an 'x64.runsettings' file in the "SrcRootPath" to configure the proper settings for an x64 run. - -.PARAMETER projectRelativePath - Relative path to the project to test. The absolute path is computed by combining $buildInfo['RepoRootPath'] with the relative - path provided. -#> - $testProj = Join-Path $buildInfo['RepoRootPath'] $projectRelativePath - $runSettings = Join-Path $buildInfo['SrcRootPath'] 'x64.runsettings' - $result = dotnet test $testProj -s $runSettings --no-build --no-restore --logger "trx" -r $buildInfo['TestResultsPath'] ` - | Out-String - Write-Information $result - return $LASTEXITCODE -ne 0 -} - -function Get-BuildVersionXML -{ -<# -.SYNOPSIS - Retrieves the contents of the BuildVersion.XML file in the RepoRootPath - -.DESCRIPTION - Reads the contents of the BuildVersion.xml file and returns it as XML - for additional processing. - -.PARAMETER buildInfo - Hashtable containing Information about the repository and build. This function - requires the presence of a 'RepoRootPath' property to indicate the root of the - repository containing the BuildVersion.xml file. -#> - [OutputType([xml])] - param ($buildInfo) - - return [xml](Get-Content (Join-Path $buildInfo['RepoRootPath'] 'BuildVersion.xml')) + return $retVal } -function Get-BuildVersionTag +# Exported from the module but NOT from the PSD1 list. Instead, you should use module qualified name +# to access this function. It is only useful when updating the set of public functions exported +# to generate the required 'FunctionsToExport' property of the PSD1 file. +function Get-FunctionsToExport { <# .SYNOPSIS - Retrieves the git tag name to apply for this build. - -.DESCRIPTION - Reads the contents of the BuildVersion.xml file and generates a git - release tag name for the current build. - - This is a standalone function instead of a property on the build - information Hashtable so that it is always dynamically evaluated - based on the current contents of the BuildVersion.XML file as that - is generally updated when this is needed. - -.PARAMETER buildInfo - Hashtable containing Information about the repository and build. This function - requires the table include an entry for 'RepoRootPath' to indicate the root of the - repository containing the BuildVersion.xml file. - -.PARAMETER ReleaseNameOnly - Returns only the final release name of the current version dropping any pre-release - if present. This is mostly used in cases that need to determine the release Name - before the XML file is updated. Including creation of a release branch where the - change to the XML will occur. + Gets the 'FunctionsToExport' node for the PSD1 file as a string #> - [OutputType([string])] - Param([hashtable]$buildInfo, [switch]$ReleaseNameOnly) + $bldr = New-Object -TypeName 'System.Text.StringBuilder' 'FunctionsToExport = @(' + $bldr.AppendLine() | Out-Null + $exportedFunctionNames = Get-ExportedFunctionNames - # determine release tag from the build version XML file in the branch - $buildVersionXml = (Get-BuildVersionXML $buildInfo) - $buildVersionData = $buildVersionXml.BuildVersionData - $preReleaseSuffix="" - if(!$ReleaseNameOnly) + # Force inject the one known always present function + # NOTE: PS needs BOTH the PSD and the calls to "Export-Member -Function" + if(!$exportedNames -or $exportedNames.Length -eq 0) { - if($buildVersionData.PSObject.Properties['PreReleaseName']) - { - $preReleaseSuffix = "-$($buildVersionData.PreReleaseName)" - if($buildVersionData.PSObject.Properties['PreReleaseNumber']) - { - $preReleaseSuffix += ".$($buildVersionData.PreReleaseNumber)" - if($buildVersionData.PSObject.Properties['PreReleaseFix']) - { - $preReleaseSuffix += ".$($buildVersionData.PreReleaseFix)" - } - } - } + $bldr.AppendLine(" 'Get-FunctionsToExport'") | Out-Null } - - return "v$($buildVersionData.BuildMajor).$($buildVersionData.BuildMinor).$($buildVersionData.BuildPatch)$preReleaseSuffix" -} - -function Initialize-CommonBuildEnvironment -{ -<# -.SYNOPSIS - Initializes the build environment for the build scripts - -.PARAMETER FullInit - Performs a full initialization. A full initialization includes forcing a re-capture of the time stamp for local builds - as well as writes details of the initialization to the information and verbose streams. - -.PARAMETER AllowVsPreReleases - Switch to enable use of Visual Studio Pre-Release versions. This is NEVER enabled for official production builds, however it is - useful when adding support for new versions during the pre-release stages. - -.PARAMETER DefaultMsBuildVerbosity - Default MSBuild verbosity for the console logger output, default value for this is 'Minimal'. - -.DESCRIPTION - This script is used to initialize the build environment in a central place, it returns the - build info Hashtable with properties determined for the build. Script code should use these - properties instead of any environment variables. While this script does setup some environment - variables for non-script tools (i.e., MSBuild) script code should not rely on those. - - This script will setup the PATH environment variable to contain the path to MSBuild so it is - readily available for all subsequent script code. - - Environment variables set for non-script tools: - - | Name | Description | - |--------------------|-------------| - | IsAutomatedBuild | "true" if in an automated build environment "false" for local developer builds | - | IsPullRequestBuild | "true" if this is a build from an untrusted pull request (limited build, no publish etc...) | - | IsReleaseBuild | "true" if this is an official release build | - | CiBuildName | Name of the build for Constrained Semantic Version construction | - | BuildTime | ISO-8601 formatted time stamp for the build (local builds are based on current time, automated builds use the time from the HEAD commit) - - This function returns a Hashtable containing properties for the current build with the following properties: - - | Name | Description | - |---------------------|--------------------------------------| - | RepoRootPath | Root of the repository for the build | - | BuildOutputPath | Base directory for all build output during the build | - | NuGetRepositoryPath | NuGet 'packages' directory for C++ projects using packages.config | - | NuGetOutputPath | Location where NuGet packages created during the build are placed | - | SrcRootPath | Root of the source code for this repository | - | DocsOutputPath | Root path for the generated documentation for the project | - | BinLogsPath | Path to where the binlogs are generated for PR builds to allow diagnosing failures in the automated builds | - | TestResultsPath | Path to where test results are placed. | - | DownloadsPath | Location where any downloaded files, used by the build are placed | - | ToolsPath | Location of any executable tools downloaded for the build (Typically expanded from a compressed download) | - | CurrentBuildKind | Results of a call to Get-CurrentBuildKind | - | MsBuildLoggerArgs | Array of MSBuild arguments, normally this only contains the Logger parameters for the console logger verbosity | - | MSBuildInfo | Information about the found version of MSBuild (from a call to Find-MSBuild) | - | VersionTag | Git tag name for this build if released | -#> - # support common parameters - [cmdletbinding()] - Param([switch]$FullInit, - [switch]$AllowVsPreReleases, - $AddRepoSpecificPropertiesFunc, - $DefaultMsBuildVerbosity="Minimal" - ) - - # Script code should ALWAYS use the global CurrentBuildKind - $currentBuildKind = Get-CurrentBuildKind - - # set/reset legacy environment vars for non-script tools (i.e. msbuild.exe) - $env:IsAutomatedBuild = $currentBuildKind -ne [BuildKind]::LocalBuild - $env:IsPullRequestBuild = $currentBuildKind -eq [BuildKind]::PullRequestBuild - $env:IsReleaseBuild = $currentBuildKind -eq [BuildKind]::ReleaseBuild - - switch($currentBuildKind) + else { - ([BuildKind]::LocalBuild) { $env:CiBuildName = 'ZZZ' } - ([BuildKind]::PullRequestBuild) { $env:CiBuildName = 'PRQ' } - ([BuildKind]::CiBuild) { $env:CiBuildName = 'BLD' } - ([BuildKind]::ReleaseBuild) { $env:CiBuildName = '' } - default { throw "Invalid build kind" } + $bldr.AppendLine(" 'Get-FunctionsToExport',") | Out-Null } - # get the ISO-8601 formatted time stamp of the HEAD commit or the current UTC time for local builds - # for FullInit force a re-capture of the time stamp. - if(!$env:BuildTime -or $FullInit) + for($i = 0; $i -lt $exportedFunctionNames.Length; ++$i) { - if($currentBuildKind -ne [BuildKind]::LocalBuild) + $bldr.Append(" '$($exportedFunctionNames[$i])'") | Out-Null + if($i -lt ($exportedFunctionNames.Length - 1)) { - $env:BuildTime = (git show -s --format=%cI) + $bldr.AppendLine(',') | Out-Null } else { - $env:BuildTime = ([System.DateTime]::UtcNow.ToString("o")) + $bldr.AppendLine() | Out-Null # Trailing comma is an error in PS; So, don't include it } } - $msbuildInfo = Find-MSBuild -AllowVsPreReleases:$AllowVsPreReleases - if( !$msbuildInfo['FoundOnPath'] ) - { - $env:Path = "$env:Path;$($msbuildInfo['BinPath'])" - } + $bldr.Append(')') | Out-Null + return $bldr.ToString() +} + +# force preferences to what is expected +# see: https://github.com/PowerShell/PowerShell/issues/4568 +# there is NO good solution to this beyond setting a global preference OR specifying the preference on EVERY CALL +# '`tis a silly thing' - The whole PS preferences settings is a nightmarish mess. +$InformationPreference = 'Continue' +$ErrorInformationPreference = 'Stop' + +# get public/Private function definition files +$Public = @( Get-ChildItem -Path (Join-path $PSScriptroot 'Public' '*.ps1') -ErrorAction SilentlyContinue ) +$Private = @( Get-ChildItem -Path (Join-path $PSScriptroot 'Private' '*.ps1') -ErrorAction SilentlyContinue ) - $buildInfo = Get-DefaultBuildPaths ([IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))) - if(![string]::IsNullOrEmpty($DefaultMsBuildVerbosity)) +# Dot source the files so they are available in this script +foreach($import in @($Public + $Private)) +{ + try { - $buildInfo['MsBuildLoggerArgs'] = @("/clp:Verbosity=$DefaultMsBuildVerbosity") + . $import.fullname } - else + catch { - $buildInfo['MsBuildLoggerArgs'] = @() + Write-Error -Message "Failed to import function definition $($import.fullname): $_" } - - $buildInfo['CurrentBuildKind'] = $currentBuildKind - $buildInfo['MSBuildInfo'] = $msbuildInfo - $buildInfo['VersionTag'] = Get-BuildVersionTag $buildInfo - return $buildInfo } -function Show-FullBuildInfo -{ -<# -.SYNOPSIS - Displays details of the build information and environment to the information and verbose streams +# Explicitly calling Export-ModuleMember disables the default +# behavior of exporting ALL functions, even if they are marked +# with private scope! However, Listing the functions in the PSD +# FunctionsToExport member will ALSO export them AND make them +# available so PS can auto load this module when they are called +# thus, listing in the PSD file is the preferred method of exporting +# script functions. +# +# Export-ModuleMember Get-FunctionsToExport +#$exportedFunctionNames = Get-ExportedFunctionNames $Public +#foreach($name in $exportedFunctionNames ) +#{ +# Export-ModuleMember $name +#} -.PARAMETER buildInfo - The build information Hashtable for the build. This normally contains the standard and repo specific - properties so that the full details are available in logs. - -.DESCRIPTION - This function displays all the properties of the buildinfo to the information stream. Additionally, - details of the current PATH, the .NET SDKs and runtimes installed is logged to the Verbose stream. -#> - Param($buildInfo) - Write-Information 'Build Info:' - Write-Information ($buildInfo | Format-Table | Out-String) - - Write-Information "MSBUILD:`n$($buildInfo['MsbuildInfo'] | Format-Table -AutoSize | Out-String)" - Write-Information (dir env:Is* | Format-Table -Property Name, value | Out-String) - Write-Information (dir env:GITHUB* | Format-Table -Property Name, value | Out-String) - Write-Information "BuildKind: $($buildInfo['CurrentBuildKind'])" - Write-Information "CiBuildName: $env:CiBuildName" - Write-Verbose 'PATH:' - $($env:Path -split ';') | %{ Write-Verbose $_ } - - Write-Verbose ".NET Runtimes:" - Write-Verbose (dotnet --list-runtimes | Out-String) - - Write-Verbose ".NET SDKs:" - Write-Verbose (dotnet --list-sdks | Out-String) -} - -function Invoke-Git() -{ - git $args - if(!$?) - { - throw "'git $args' command failed ($LASTEXITCODE)" - } -} diff --git a/PsModules/CommonBuild/CommonBuild.pssproj b/PsModules/CommonBuild/CommonBuild.pssproj new file mode 100644 index 0000000000..b9c67bb5ec --- /dev/null +++ b/PsModules/CommonBuild/CommonBuild.pssproj @@ -0,0 +1,71 @@ + + + Debug + 2.0 + 6CAFC0C6-A428-4d30-A9F9-700E829FEA51 + Exe + MyApplication + MyApplication + CommonBuild + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + <_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" /> + + + + + diff --git a/PsModules/CommonBuild/Debug-Module.ps1 b/PsModules/CommonBuild/Debug-Module.ps1 new file mode 100644 index 0000000000..6077a37e96 --- /dev/null +++ b/PsModules/CommonBuild/Debug-Module.ps1 @@ -0,0 +1,16 @@ +# This script is used for debugging the module when using the +# [Powershell Tools for VisualStudio 2022](https://marketplace.visualstudio.com/items?itemName=AdamRDriscoll.PowerShellToolsVS2022) +# It is MOST useful for computing and reporting the set of functions to export from this module for the PSD1 file. +Import-Module $PSScriptRoot\CommonBuild.psd1 -Force -Verbose +CommonBuild\Get-FunctionsToExport + +# This is private and should not be exported - Should generate an error +$buildInfo = @{ BuildOutputPath = 'foo/bar' } +$config = New-CMakeConfig "x64-Release" "release" $buildInfo 'foo/bar/cmake_src' +if(!$config -or $config -isnot [hashtable]) +{ + throw "Null or wrong type returned!" +} + +$testArgs = @('a', 'b', 'c') +Invoke-External where.exe $testArgs diff --git a/PsModules/CommonBuild/Private/Ensure-PathExists.ps1 b/PsModules/CommonBuild/Private/Ensure-PathExists.ps1 new file mode 100644 index 0000000000..c514a8c3e6 --- /dev/null +++ b/PsModules/CommonBuild/Private/Ensure-PathExists.ps1 @@ -0,0 +1,5 @@ +function Ensure-PathExists +{ + param([Parameter(Mandatory=$true, ValueFromPipeLine)]$path) + New-Item -Force -ErrorAction SilentlyContinue -Name $path -ItemType Directory | Out-Null +} diff --git a/PsModules/CommonBuild/Private/Get-BuildVersionXML.ps1 b/PsModules/CommonBuild/Private/Get-BuildVersionXML.ps1 new file mode 100644 index 0000000000..73bcedfc5a --- /dev/null +++ b/PsModules/CommonBuild/Private/Get-BuildVersionXML.ps1 @@ -0,0 +1,20 @@ +function Get-BuildVersionXML +{ +<# +.SYNOPSIS + Retrieves the contents of the BuildVersion.XML file in the RepoRootPath + +.DESCRIPTION + Reads the contents of the BuildVersion.xml file and returns it as XML + for additional processing. + +.PARAMETER buildInfo + Hashtable containing Information about the repository and build. This function + requires the presence of a 'RepoRootPath' property to indicate the root of the + repository containing the BuildVersion.xml file. +#> + [OutputType([xml])] + param ($buildInfo) + + return [xml](Get-Content (Join-Path $buildInfo['RepoRootPath'] 'BuildVersion.xml')) +} diff --git a/PsModules/CommonBuild/Private/Get-DefaultBuildPaths.ps1 b/PsModules/CommonBuild/Private/Get-DefaultBuildPaths.ps1 new file mode 100644 index 0000000000..2c98ed831c --- /dev/null +++ b/PsModules/CommonBuild/Private/Get-DefaultBuildPaths.ps1 @@ -0,0 +1,48 @@ +function Get-DefaultBuildPaths() +{ +<# +.SYNOPSIS + Gets the default set of paths for a build + +.DESCRIPTION + This function initializes a hash table with the default paths for a build. This + allows for standardization of build output locations etc... across builds and repositories + in the organization. The values set are as follows: + + | Name | Description | + |---------------------|--------------------------------------| + | RepoRootPath | Root of the repository for the build | + | BuildOutputPath | Base directory for all build output during the build | + | NuGetRepositoryPath | NuGet 'packages' directory for C++ projects using packages.config | + | NuGetOutputPath | Location where NuGet packages created during the build are placed | + | SrcRootPath | Root of the source code for this repository | + | DocsOutputPath | Root path for the generated documentation for the project | + | BinLogsPath | Path to where the binlogs are generated for PR builds to allow diagnosing failures in the automated builds | + | TestResultsPath | Path to where test results are placed. | + | DownloadsPath | Location where any downloaded files, used by the build are placed | + | ToolsPath | Location of any executable tools downloaded for the build (Typically expanded from a compressed download) | +#> + [OutputType([hashtable])] + param( + [ValidateNotNullorEmpty()] + [string]$repoRoot + ) + + $buildOutputPath = ConvertTo-NormalizedPath (Join-Path $repoRoot 'BuildOutput') + $buildPaths = @{ + RepoRootPath = $repoRoot + BuildOutputPath = $buildOutputPath + NuGetRepositoryPath = Join-Path $buildOutputPath 'packages' + NuGetOutputPath = Join-Path $buildOutputPath 'NuGet' + SrcRootPath = Join-Path $repoRoot 'src' + DocsRepoPath = Join-Path $buildOutputPath 'docs' + DocsOutputPath = Join-Path $buildOutputPath 'docs' + BinLogsPath = Join-Path $buildOutputPath 'BinLogs' + TestResultsPath = Join-Path $buildOutputPath 'Test-Results' + DownloadsPath = Join-Path $repoRoot 'Downloads' + ToolsPath = Join-Path $repoRoot 'Tools' + } + + $buildPaths.GetEnumerator() | %{ Ensure-PathExists $_.Value } + return $buildPaths +} diff --git a/PsModules/CommonBuild/Private/Get-VsArchitecture.ps1 b/PsModules/CommonBuild/Private/Get-VsArchitecture.ps1 new file mode 100644 index 0000000000..e426cdb99a --- /dev/null +++ b/PsModules/CommonBuild/Private/Get-VsArchitecture.ps1 @@ -0,0 +1,11 @@ +function Get-VsArchitecture() +{ + switch([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) + { + X86 {'x86'} + X64 {'x64'} + Arm {'ARM'} + Arm64 {'ARM64'} + default { throw 'Usupported runtime architecture for MSVC'} + } +} diff --git a/PsModules/CommonBuild/Private/ReadMe.md b/PsModules/CommonBuild/Private/ReadMe.md new file mode 100644 index 0000000000..31874adbab --- /dev/null +++ b/PsModules/CommonBuild/Private/ReadMe.md @@ -0,0 +1,5 @@ +# Common Build Private +As the name implies this is where the private script functions used by the common build +support are located. Everything here is considered private, and subject to change at +any time. These are implementation details and helpers that are NOT exported by the +common module. diff --git a/PsModules/CommonBuild/Public/Assert-CMakeInfo.ps1 b/PsModules/CommonBuild/Public/Assert-CMakeInfo.ps1 new file mode 100644 index 0000000000..c66025ddfc --- /dev/null +++ b/PsModules/CommonBuild/Public/Assert-CMakeInfo.ps1 @@ -0,0 +1,14 @@ +function Assert-CmakeInfo([Version]$minVersion) +{ + $cmakeInfo = Invoke-External cmake -E capabilities | ConvertFrom-Json + if(!$cmakeInfo) + { + throw "CMake version not supported. 'cmake -E capabilities' returned nothing" + } + + $cmakeVer = [Version]::new($cmakeInfo.version.major,$cmakeInfo.version.minor,$cmakeInfo.version.patch) + if( $cmakeVer -lt $minVersion ) + { + throw "CMake version not supported. Found: $cmakeVer; Require >= $($minVersion)" + } +} diff --git a/PsModules/CommonBuild/Public/Assert-CMakeList.ps1 b/PsModules/CommonBuild/Public/Assert-CMakeList.ps1 new file mode 100644 index 0000000000..780082f0e9 --- /dev/null +++ b/PsModules/CommonBuild/Public/Assert-CMakeList.ps1 @@ -0,0 +1,8 @@ +function Assert-CMakeList([Parameter(Mandatory=$true)][string] $root) +{ + $cmakeListPath = Join-Path $root CMakeLists.txt + if( !( Test-Path -PathType Leaf $cmakeListPath ) ) + { + throw "'CMakeLists.txt' is missing, '$root' does not appear to be a valid CMAKE source directory" + } +} diff --git a/PsModules/CommonBuild/Public/Assert-OfficialGitRemote.ps1 b/PsModules/CommonBuild/Public/Assert-OfficialGitRemote.ps1 new file mode 100644 index 0000000000..d5547319f0 --- /dev/null +++ b/PsModules/CommonBuild/Public/Assert-OfficialGitRemote.ps1 @@ -0,0 +1,31 @@ +function Assert-OfficialGitRemote +{ +<# +.SYNOPSIS + Verifies the current git remote is the official repository + +.PARAMETER OfficialUrl + URL of the official remote to Verify + +.PARAMETER Activity + String describing the activity. This is used in the exception message stating + that the activity is only valid with the correct remote URL + +.DESCRIPTION + Some operations like, Release tags, and docs updates must only be pushed from a repository with the + official GitHub repository as the origin remote. This, among other things, ensures that the links + to source in the generated docs will have the correct URLs (e.g. docs pushed to the official repository + MUST not have links to source in some private fork). This function is used, primarily in OneFlow release + management scripts to ensure operations are done using the correct remote. +#> + Param( + [Parameter(Mandatory=$True)][string]$OfficialUrl, + [Parameter(Mandatory=$True)][string]$Activity + ) + + $remoteUrl = Invoke-External git ls-remote --get-url + if($remoteUrl -ine $OfficialUrl) + { + throw "$Activity is only allowed when the origin remote is the official repository - current remote is '$remoteUrl'" + } +} diff --git a/PsModules/CommonBuild/Public/BuildVersionInfo.ps1 b/PsModules/CommonBuild/Public/BuildVersionInfo.ps1 new file mode 100644 index 0000000000..469641caa9 --- /dev/null +++ b/PsModules/CommonBuild/Public/BuildVersionInfo.ps1 @@ -0,0 +1,51 @@ +function Get-BuildVersionTag +{ +<# +.SYNOPSIS + Retrieves the git tag name to apply for this build. + +.DESCRIPTION + Reads the contents of the BuildVersion.xml file and generates a git + release tag name for the current build. + + This is a standalone function instead of a property on the build + information Hashtable so that it is always dynamically evaluated + based on the current contents of the BuildVersion.XML file as that + is generally updated when this is needed. + +.PARAMETER buildInfo + Hashtable containing Information about the repository and build. This function + requires the table include an entry for 'RepoRootPath' to indicate the root of the + repository containing the BuildVersion.xml file. + +.PARAMETER ReleaseNameOnly + Returns only the final release name of the current version dropping any pre-release + if present. This is mostly used in cases that need to determine the release Name + before the XML file is updated. Including creation of a release branch where the + change to the XML will occur. +#> + [OutputType([string])] + Param([hashtable]$buildInfo, [switch]$ReleaseNameOnly) + + # determine release tag from the build version XML file in the branch + $buildVersionXml = (Get-BuildVersionXML $buildInfo) + $buildVersionData = $buildVersionXml.BuildVersionData + $preReleaseSuffix="" + if(!$ReleaseNameOnly) + { + if($buildVersionData.PSObject.Properties['PreReleaseName']) + { + $preReleaseSuffix = "-$($buildVersionData.PreReleaseName)" + if($buildVersionData.PSObject.Properties['PreReleaseNumber']) + { + $preReleaseSuffix += ".$($buildVersionData.PreReleaseNumber)" + if($buildVersionData.PSObject.Properties['PreReleaseFix']) + { + $preReleaseSuffix += ".$($buildVersionData.PreReleaseFix)" + } + } + } + } + + return "v$($buildVersionData.BuildMajor).$($buildVersionData.BuildMinor).$($buildVersionData.BuildPatch)$preReleaseSuffix" +} diff --git a/PsModules/CommonBuild/Public/ClassLike_CMakeConfig.ps1 b/PsModules/CommonBuild/Public/ClassLike_CMakeConfig.ps1 new file mode 100644 index 0000000000..6b71539d9e --- /dev/null +++ b/PsModules/CommonBuild/Public/ClassLike_CMakeConfig.ps1 @@ -0,0 +1,199 @@ +<# +There are are just too many issues and edges that don't work well or at all with +PS classes and modules. So this fakes a class like we used to do in the "Good ol' days" +with "C" before any of the modern OO languages were created. + +A CMakeConfig is a hash table with the following properties +| Name | Type | Description +|-------------------|-----------|------------| +| Name | string | Name of the configuration | +| ConfigurationType | string | Build configuration of the CMAKE build | +| BuildRoot | string | Root directory of the Build (Exctracted from a standard BuildInfo hash table) | +| SrcRoot | string | Root directory of the CMAKE specific build | +| Generator | string | Generator to use for this CMAKE build | +| CMakeCommandArgs | ArrayList | Custom args for the CMAKE command | +| CMakeCommandArgs | ArrayList | Custom args for the CMAKE command | +| BuildCommandArgs | ArrayList | Additional build specific args to pass to the underlying build-engine | +| InheritEnvironments | ArrayList | Inherited environments for VS CMAKE settings (Windows ONLY)| +| CMakeBuildVariables | hashtable | Table of CMAKE build variables to set | +#> +function Assert-IsCMakeConfig([hashtable]$self) +{ + if(!$self.ContainsKey('Name') -or + !$self.ContainsKey('ConfigurationType') -or + !$self.ContainsKey('BuildRoot') -or + !$self.ContainsKey('SrcRoot') -or + !$self.ContainsKey('Generator') -or + !$self.ContainsKey('CMakeCommandArgs') -or + !$self.ContainsKey('CMakeCommandArgs') -or + !$self.ContainsKey('BuildCommandArgs') -or + !$self.ContainsKey('InheritEnvironments') -or + !$self.ContainsKey('CMakeBuildVariables') + ) + { + throw 'Not all properties present in hashtable' + } + + if ($self['Name'] -isnot [string] -or + $self['ConfigurationType'] -isnot [string] -or + $self['BuildRoot'] -isnot [string] -or + $self['SrcRoot'] -isnot [string] -or + $self['Generator'] -isnot [string] -or + $self['CMakeCommandArgs'] -isnot [System.Collections.ArrayList] -or + $self['CMakeCommandArgs'] -isnot [System.Collections.ArrayList] -or + $self['BuildCommandArgs'] -isnot [System.Collections.ArrayList] -or + $self['InheritEnvironments'] -isnot [System.Collections.ArrayList] -or + $self['CMakeBuildVariables'] -isnot [hashtable] + ) + { + throw 'Incorrect property type' + } +} + +function New-CMakeConfig($name, [string]$buildConfiguration, [hashtable]$buildInfo, $cmakeSrcRoot) +{ +<# +.SYNOPSIS + Initializes a new instance of the CMakeConfig data structure + +.PARAMETER name + The name of this configuration. Typically this is of the form `-` + (ex: `x64-Release`). This is used to identify this configuration AND to form + the output path for it. + +.PARAMETER buildInfo + Hashtable containing the standard build information. + +.PARAMETER cmakeSrcRoot + Root of the CMAKE source files to build +#> + # start with an empty hash table and build up from there... + $self = @{} + if($IsWindows) + { + #$self['Generator'] = 'Visual Studio 17 2022' + $self['Generator'] = 'Ninja' + } + else + { + # TODO: Select generator for other runtimes... + throw "Unsupported runtime platform '$([System.Environment]::OSVersion.Platform)'" + } + + $self['Name']=$name + $self['ConfigurationType'] = $buildConfiguration + $self['BuildRoot'] = Join-Path $buildInfo['BuildOutputPath'] $self['Name'] + $self['SrcRoot'] = $cmakeSrcRoot + $self['CMakeCommandArgs'] = [System.Collections.ArrayList]@() + $self['BuildCommandArgs'] = [System.Collections.ArrayList]@() + $self['InheritEnvironments'] = [System.Collections.ArrayList]@() + $self['CMakeBuildVariables'] = @{} + + # single config generator; Requires setting the configuration type + # as a build var during generation (Otherwise, debug is assumed...) + if($self['Generator'] -ieq 'Ninja') + { + $self['CMakeBuildVariables']['CMAKE_BUILD_TYPE']=$self['ConfigurationType'] + } + + # Not needed with Ninja builds + #if($IsWindows) + #{ + # # running on ARM64 is not tested or supported + # # This might not be needed now that the build is auto configuring the "VCVARS" + # # Ninja build might also remove the need for this... + # if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq "X64" ) + # { + # $self['CMakeCommandArgs'].Add('-Thost=x64') | Out-Null + # $self['InheritEnvironments'].Add('msvc_x64_x64') | Out-Null + # } + # else + # { + # $self['InheritEnvironments'].Add('msvc_x64') | Out-Null + # } + # + # # pass the /m switch to MSBUILD to enable parallel builds on all available CPU cores + # $self['BuildCommandArgs'].Add('/m') | Out-Null + #} + + Assert-IsCMakeConfig $self + return $self +} + +function Invoke-GenerateCMakeConfig([hashtable]$self, [hashtable] $additionalBuildVars = $null) +{ + Assert-IsCMakeConfig $self + + Write-Information "Generating CMake build support for $($self['Name'])" + # if the output path doesn't exist, create it now + if(!(Test-Path -PathType Container $self['BuildRoot'] )) + { + New-Item -ItemType Container $self['BuildRoot'] | Out-Null + } + + # Construct full set of CMAKE args from fixed options and configuration variables + $cmakeArgs = New-Object System.Collections.ArrayList + $cmakeArgs.AddRange(@('-G', "$($self['Generator'])") ) | Out-Null + $cmakeArgs.AddRange(@('-B', "$($self['BuildRoot'])") ) | Out-Null + foreach( $param in $self['CMakeCommandArgs'] ) + { + $cmakeArgs.Add( $param ) | Out-Null + } + + foreach( $var in $self['CMakeBuildVariables'].GetEnumerator() ) + { + Write-Verbose "CMAKE-VAR: $($var.Key)=$($var.Value)" + $cmakeArgs.Add( "-D$($var.Key)=$($var.Value)" ) | Out-Null + } + + # add any build instance specific vars provided + if($additionalBuildVars) + { + foreach( $var in $additionalBuildVars.GetEnumerator()) + { + if ($var.Key -eq $null -or $var.Value -eq $null) + { + throw 'Invalid build var provided' + } + + Write-Verbose "CMAKE-VAR: $($var.Key)=$($var.Value)" + $cmakeArgs.Add( "-D$($var.Key)=$($var.Value)" ) | Out-Null + } + } + + $cmakeArgs.Add( $self['SrcRoot'] ) | Out-Null + Invoke-TimedBlock "CMAKE Generate" { + Write-Information "cmake $($cmakeArgs -join ' ')" + + # Splat the array of args as distinct elements for external invoke + Invoke-External cmake @cmakeArgs + } +} + +# workaround stupid warning about exported function verbs without an "approved" verb +# in the name. Use a sensible but unapproved alias name instead. [Sigh, what a mess...] +# see: https://github.com/PowerShell/PowerShell/issues/13637 +New-Alias -Name Generate-CMakeConfig -Value Invoke-GenerateCMakeConfig + +function Build-CmakeConfig([hashtable]$self, $targets) +{ + Assert-IsCMakeConfig $self + $cmakeArgs = [System.Collections.ArrayList]@('--build', "$($self['BuildRoot'])", '--config', "$($self['ConfigurationType'])") + if($targets) + { + $cmakeArgs.Add('-t') | Out-Null + $cmakeArgs.AddRange($targets) + } + + if($self['BuildCommandArgs'].Length -gt 0) + { + $cmakeArgs.AddRange( @('--', "$($self['BuildCommandArgs'])") ) + } + + Invoke-TimedBlock "CMake Building $($self['Name'])" { + Write-Information "cmake $($cmakeArgs -join ' ')" + + # Splat the array of args as distinct items for external invoke + Invoke-External cmake @cmakeArgs + } +} diff --git a/PsModules/CommonBuild/Public/ConvertTo-NormalizedPath.ps1 b/PsModules/CommonBuild/Public/ConvertTo-NormalizedPath.ps1 new file mode 100644 index 0000000000..ac5bed9dbc --- /dev/null +++ b/PsModules/CommonBuild/Public/ConvertTo-NormalizedPath.ps1 @@ -0,0 +1,24 @@ +function ConvertTo-NormalizedPath([string]$path ) +{ +<# +.SYNOPSIS + Converts a potentially relative folder path to an absolute one with a trailing delimiter + +.PARAMETER path + Path to convert + +.NOTES + The delimiters in the path are converted to the native system preferred form during conversion +#> + if(![System.IO.Path]::IsPathRooted($path)) + { + $path = [System.IO.Path]::Combine((Get-Location).Path,$path) + } + + $path = [System.IO.Path]::GetFullPath($path) + if( !$path.EndsWith([System.IO.Path]::DirectorySeparatorChar) -and !$path.EndsWith([System.IO.Path]::AltDirectorySeparatorChar)) + { + $path = $path + [System.IO.Path]::DirectorySeparatorChar + } + return $path +} diff --git a/PsModules/CommonBuild/Public/ConvertTo-PropertyList.ps1 b/PsModules/CommonBuild/Public/ConvertTo-PropertyList.ps1 new file mode 100644 index 0000000000..ff3b71760c --- /dev/null +++ b/PsModules/CommonBuild/Public/ConvertTo-PropertyList.ps1 @@ -0,0 +1,8 @@ +function ConvertTo-PropertyList([hashtable]$hashTable) +{ +<# +.SYNOPSIS + Converts a hash table into a semi-colon delimited property list +#> + return ( $hashTable.GetEnumerator() | %{ @{$true=$_.Key;$false= $_.Key + "=" + $_.Value }[[string]::IsNullOrEmpty($_.Value) ] } ) -join ';' +} diff --git a/PsModules/CommonBuild/Public/Expand-ArchiveStream.ps1 b/PsModules/CommonBuild/Public/Expand-ArchiveStream.ps1 new file mode 100644 index 0000000000..c8abafb480 --- /dev/null +++ b/PsModules/CommonBuild/Public/Expand-ArchiveStream.ps1 @@ -0,0 +1,32 @@ +function Expand-ArchiveStream([Parameter(Mandatory=$true, ValueFromPipeLine)]$src, [Parameter(Mandatory=$true)]$OutputPath) +{ +<# +.SYNOPSIS + Expands an archive stream + +.PARAMETER src + Input stream containing compressed ZIP archive data to expand + +.PARAMETER OutputPath + Out put destination for the decompressed data +#> + $zipArchive = [System.IO.Compression.ZipArchive]::new($src) + [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory( $zipArchive, $OutputPath) +} + +function Expand-StreamFromUri([Parameter(Mandatory=$true, ValueFromPipeLine)]$uri, [Parameter(Mandatory=$true)]$OutputPath) +{ +<# +.SYNOPSIS + Downloads and expands a ZIP file to the specified destination + +.PARAMETER uri + URI of the ZIP file to download and expand + +.PARAMETER OutputPath + Output folder to expand the ZIP contents into +#> + $strm = (Invoke-WebRequest -UseBasicParsing -Uri $uri).RawContentStream + Expand-ArchiveStream $strm $OutputPath +} + diff --git a/PsModules/CommonBuild/Public/Find-OnPath.ps1 b/PsModules/CommonBuild/Public/Find-OnPath.ps1 new file mode 100644 index 0000000000..4286de1914 --- /dev/null +++ b/PsModules/CommonBuild/Public/Find-OnPath.ps1 @@ -0,0 +1,33 @@ +function Find-OnPath +{ +<# +.SYNOPSIS + Searches for an executable on the current environment search path + +.PARAMETER exeName + The executable to search for + +.NOTES + This is a simple wrapper around the Get-Command built-in with `-Type Application` to get + the full path of an executable application. This allows invoking the application explicitly + in a runtime neutral form. This applies exception handling to ensure that NOT-FOUND results + in a $null instead of an exception/error message display. (The command might not exist and + scripts must deal with that possibility) +#> + [CmdletBinding()] + [OutputType([string])] + Param( [Parameter(Mandatory=$True,Position=0)][string]$appName) + try + { + $retVal = (Get-command -Type Application $appName -ErrorAction Stop).Source + if ($retVal -is [Array]) + { + return $retVal[0] + } + return $retVal + } + catch + { + return $null + } +} diff --git a/PsModules/CommonBuild/Public/Get-BuildVersionTag.ps1 b/PsModules/CommonBuild/Public/Get-BuildVersionTag.ps1 new file mode 100644 index 0000000000..974aa80753 --- /dev/null +++ b/PsModules/CommonBuild/Public/Get-BuildVersionTag.ps1 @@ -0,0 +1,42 @@ +function Get-BuildVersionTag +{ +<# +.SYNOPSIS + Retrieves the git tag name to apply for this build. + +.DESCRIPTION + Reads the contents of the BuildVersion.xml file and generates a git + release tag name for the current build. + + This is a standalone function instead of a property on the build + information Hashtable so that it is always dynamically evaluated + based on the current contents of the BuildVersion.XML file as that + is generally updated when this is needed. + +.PARAMETER buildInfo + Hashtable containing Information about the repository and build. This function + requires the presence of a 'RepoRootPath' property to indicate the root of the + repository containing the BuildVersion.xml file. +#> + [OutputType([string])] + Param($buildInfo) + + # determine release tag from the build version XML file in the branch + $buildVersionXml = (Get-BuildVersionXML $buildInfo) + $buildVersionData = $buildVersionXml.BuildVersionData + $preReleaseSuffix="" + if($buildVersionData.PSObject.Properties['PreReleaseName']) + { + $preReleaseSuffix = "-$($buildVersionData.PreReleaseName)" + if($buildVersionData.PSObject.Properties['PreReleaseNumber']) + { + $preReleaseSuffix += ".$($buildVersionData.PreReleaseNumber)" + if($buildVersionData.PSObject.Properties['PreReleaseFix']) + { + $preReleaseSuffix += ".$($buildVersionData.PreReleaseFix)" + } + } + } + + return "v$($buildVersionData.BuildMajor).$($buildVersionData.BuildMinor).$($buildVersionData.BuildPatch)$preReleaseSuffix" +} diff --git a/PsModules/CommonBuild/Public/Get-CurrentBuildKind.ps1 b/PsModules/CommonBuild/Public/Get-CurrentBuildKind.ps1 new file mode 100644 index 0000000000..7281586612 --- /dev/null +++ b/PsModules/CommonBuild/Public/Get-CurrentBuildKind.ps1 @@ -0,0 +1,69 @@ +enum BuildKind +{ + LocalBuild + PullRequestBuild + CiBuild + ReleaseBuild +} + +function Get-CurrentBuildKind +{ +<# +.SYNOPSIS + Determines the kind of build for the current environment + +.DESCRIPTION + This function retrieves environment values set by various automated builds + to determine the kind of build the environment is for. The return is one of + the [BuildKind] enumeration values: + + | Name | Description | + |------------------|-------------| + | LocalBuild | This is a local developer build (e.g. not an automated build) + | PullRequestBuild | This is a build from a PullRequest with untrusted changes, so build should limit the steps appropriately | + | CiBuild | This build is from a Continuous Integration (CI) process, usually after a PR is accepted and merged to the branch | + | ReleaseBuild | This is an official release build, the output is ready for publication (Automated builds may use this to automatically publish) | +#> + [OutputType([BuildKind])] + param() + + $currentBuildKind = [BuildKind]::LocalBuild + + # IsAutomatedBuild is the top level gate (e.g. if it is false, all the others must be false) + $isAutomatedBuild = [System.Convert]::ToBoolean($env:CI) ` + -or [System.Convert]::ToBoolean($env:APPVEYOR) ` + -or [System.Convert]::ToBoolean($env:GITHUB_ACTIONS) + + if( $isAutomatedBuild ) + { + # PR and release builds have externally detected indicators that are tested + # below, so default to a CiBuild (e.g. not a PR, And not a RELEASE) + $currentBuildKind = [BuildKind]::CiBuild + + # IsPullRequestBuild indicates an automated buddy build and should not be trusted + $isPullRequestBuild = $env:GITHUB_BASE_REF -or $env:APPVEYOR_PULL_REQUEST_NUMBER + + if($isPullRequestBuild) + { + $currentBuildKind = [BuildKind]::PullRequestBuild + } + else + { + if([System.Convert]::ToBoolean($env:APPVEYOR)) + { + $isReleaseBuild = $env:APPVEYOR_REPO_TAG + } + elseif([System.Convert]::ToBoolean($env:GITHUB_ACTIONS)) + { + $isReleaseBuild = $env:GITHUB_REF -like 'refs/tags/*' + } + + if($isReleaseBuild) + { + $currentBuildKind = [BuildKind]::ReleaseBuild + } + } + } + + return $currentBuildKind +} diff --git a/PsModules/CommonBuild/Public/Get-GitRemotes.ps1 b/PsModules/CommonBuild/Public/Get-GitRemotes.ps1 new file mode 100644 index 0000000000..b8dced212e --- /dev/null +++ b/PsModules/CommonBuild/Public/Get-GitRemotes.ps1 @@ -0,0 +1,42 @@ +function Get-GitRemotes +{ + param($opName = "push") + $remoteLines = ((git remote -v) -split [System.Environment]::NewLine) + $retVal = [System.Collections.ArrayList]@() + foreach($remoteLine in $remoteLines) + { + if( $remoteLine -match '([^\s]+)\s+([^\s]+)\s+\(([^\s]+)\)') + { + if($matches[3] -eq $opName) + { + $hashTable =@{ + Name = $matches[1] + Uri = $matches[2] + Op = $matches[3] + } + + $retVal.Add($hashTable) | Out-Null + } + } + else + { + throw "'$remoteLine' - does not match pattern for a valid git remote..." + } + } + + return $retVal +} + +function Get-GitRemoteName +{ + param([hashtable]$bldInfo, [ValidateSet('official', 'fork')] $kind) + + if($kind -eq 'official') + { + Get-GitRemotes | ? {$_.Uri -eq $bldInfo['OfficialGitRemoteUrl']} | Select -ExpandProperty Name + } + else + { + Get-GitRemotes | ? {$_.Uri -ne $bldInfo['OfficialGitRemoteUrl']} | Select -ExpandProperty Name + } +} diff --git a/PsModules/CommonBuild/Public/GitHubSupport.ps1 b/PsModules/CommonBuild/Public/GitHubSupport.ps1 new file mode 100644 index 0000000000..e4cdcc1135 --- /dev/null +++ b/PsModules/CommonBuild/Public/GitHubSupport.ps1 @@ -0,0 +1,47 @@ +function Get-GitHubReleases($org, $project) +{ +<# +.SYNOPSIS + Gets a collection of the GitHub releases for a project + +.DESCRIPTION + This function retrieves a collection of releases from a given GitHub organization and project. + The result is a collection of GitHub releases as JSON data. + +.PARAMETER org + GitHub organization name that owns the project + +.PARAMETER project + GitHub project to retrieve releases from +#> + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/$org/$project/releases" + foreach($r in $releases) + { + $r + } +} + +function Get-GitHubTaggedRelease($org, $project, $tag) +{ +<# +.SYNOPSIS + Gets a specific tagged release for a GitHub project + +.DESCRIPTION + This function retrieves a single tagged release from a given GitHub organization and project. + The result is a GitHub release as JSON data. + +.PARAMETER org + GitHub organization name that owns the project + +.PARAMETER project + GitHub project to retrieve releases from + +.PARAMETER tag + Tag to find the specific release for +#> + + Get-GithubReleases $org $project | ?{$_.tag_name -eq $tag} +} diff --git a/PsModules/CommonBuild/Public/Initialize-CommonBuildEnvironment.ps1 b/PsModules/CommonBuild/Public/Initialize-CommonBuildEnvironment.ps1 new file mode 100644 index 0000000000..3439c74aa3 --- /dev/null +++ b/PsModules/CommonBuild/Public/Initialize-CommonBuildEnvironment.ps1 @@ -0,0 +1,125 @@ +function Initialize-CommonBuildEnvironment +{ +<# +.SYNOPSIS + Initializes the build environment for the build scripts + +.PARAMETER RepoRoot + Root folder of the repository hosting this build + +.PARAMETER FullInit + Performs a full initialization. A full initialization includes forcing a re-capture of the time stamp for local builds + as well as writes details of the initialization to the information and verbose streams. + +.DESCRIPTION + This script is used to initialize the build environment in a central place, it returns the + build info Hashtable with properties determined for the build. Script code should use these + properties instead of any environment variables. While this script does setup some environment + variables for non-script tools (i.e., MSBuild) script code should not rely on those. + + Environment variables set for non-script tools: + + | Name | Description | + |--------------------|-------------| + | IsAutomatedBuild | "true" if in an automated build environment "false" for local developer builds | + | IsPullRequestBuild | "true" if this is a build from an untrusted pull request (limited build, no publish etc...) | + | IsReleaseBuild | "true" if this is an official release build | + | CiBuildName | Name of the build for Constrained Semantic Version construction | + | BuildTime | ISO-8601 formatted time stamp for the build (local builds are based on current time, automated builds use the time from the HEAD commit) + + This function returns a Hashtable containing properties for the current build with the following properties: + + | Name | Description | + |---------------------|--------------------------------------| + | RepoRootPath | Root of the repository for the build | + | BuildOutputPath | Base directory for all build output during the build | + | NuGetRepositoryPath | NuGet 'packages' directory for C++ projects using packages.config | + | NuGetOutputPath | Location where NuGet packages created during the build are placed | + | SrcRootPath | Root of the source code for this repository | + | DocsOutputPath | Root path for the generated documentation for the project | + | BinLogsPath | Path to where the binlogs are generated for PR builds to allow diagnosing failures in the automated builds | + | TestResultsPath | Path to where test results are placed. | + | DownloadsPath | Location where any downloaded files, used by the build are placed | + | ToolsPath | Location of any executable tools downloaded for the build (Typically expanded from a compressed download) | + | CurrentBuildKind | Results of a call to Get-CurrentBuildKind | + | VersionTag | Git tag name for this build if released | +#> + # support common parameters + [cmdletbinding()] + [OutputType([hashtable])] + Param( + [ValidateNotNullorEmpty()] + [string]$repoRoot, + [switch]$FullInit + ) + + # Script code should ALWAYS use the global CurrentBuildKind + $currentBuildKind = Get-CurrentBuildKind + + # set/reset legacy environment vars for non-script tools (i.e. msbuild.exe) + $env:IsAutomatedBuild = $currentBuildKind -ne [BuildKind]::LocalBuild + $env:IsPullRequestBuild = $currentBuildKind -eq [BuildKind]::PullRequestBuild + $env:IsReleaseBuild = $currentBuildKind -eq [BuildKind]::ReleaseBuild + + switch($currentBuildKind) + { + ([BuildKind]::LocalBuild) { $env:CiBuildName = 'ZZZ' } + ([BuildKind]::PullRequestBuild) { $env:CiBuildName = 'PRQ' } + ([BuildKind]::CiBuild) { $env:CiBuildName = 'BLD' } + ([BuildKind]::ReleaseBuild) { $env:CiBuildName = '' } + default { throw "Invalid build kind" } + } + + # get the ISO-8601 formatted time stamp of the HEAD commit or the current UTC time for local builds + # for FullInit force a re-capture of the time stamp. + if(!$env:BuildTime -or $FullInit) + { + # for any automated builds use the ISO-8601 time stamp of the current commit + if($currentBuildKind -ne [BuildKind]::LocalBuild) + { + $env:BuildTime = (git show -s --format=%cI) + } + else + { + $env:BuildTime = ([System.DateTime]::UtcNow.ToString("o")) + } + } + + # On Windows setup the equivalent of a Developer prompt. + # + # Other platform runners may have different defaulted paths etc... + # to account for here. + if ($IsWindows) + { + Write-Verbose "Configuring VS tools support" + + # For windows force a common VS tools prompt; + # Sadly most of this is undocumented and found from various sites + # spelunking the process. This isn't as bad as it might seem as the + # installer will create persistent use of this as a Windows Terminal + # "profile" and the actual command is exposed. + if($null -eq (Find-OnPath vswhere)) + { + # NOTE: automated builds in Github do NOT include WinGet (for reasons unknown) + # However, they do contain VSWHERE so should not hit this. + winget install Microsoft.VisualStudio.Locator | Out-Null + } + + $vsShellModulePath = vswhere -find **\Microsoft.VisualStudio.DevShell.dll + $vsToolsArch = Get-VsArchitecture + if(!$vsShellModulePath) + { + throw "VS shell module not found!" + } + + Import-Module $vsShellModulePath | Out-Null + $vsInstanceId = vswhere -format value -property InstanceId + Enter-VsDevShell $vsInstanceId -SkipAutomaticLocation -DevCmdArguments "-arch=$vsToolsArch -host_arch=$vsToolsArch" | Out-Null + } + + #Start with standard build paths then add additional values to the hashtable + $buildInfo = Get-DefaultBuildPaths $repoRoot + $buildInfo['CurrentBuildKind'] = $currentBuildKind + $buildInfo['VersionTag'] = Get-BuildVersionTag $buildInfo + return $buildInfo +} diff --git a/PsModules/CommonBuild/Public/Invoke-External.ps1 b/PsModules/CommonBuild/Public/Invoke-External.ps1 new file mode 100644 index 0000000000..4133bc11f9 --- /dev/null +++ b/PsModules/CommonBuild/Public/Invoke-External.ps1 @@ -0,0 +1,46 @@ +function Invoke-External($cmd) +{ +<# +.SYNOPSIS + Invokes an external command with arguments converting any failure (non-zero return) into an exception + +.PARAMETER cmd + The external command to run. This may be a full path, relative path or just the cmd name (assuming it is + executable from the current environment search paths). Any additional arguments are passed to the cmd + as-is. + +.DESCRIPTION + This provides two core features: + 1) Platform neutral execution of an external command that might conflict with a built-in alias. + 2) Detection of success for an external command (as a non-zero return code) and throws an exception + - This is the normal behaviour for command line applications. If the app returns a non-zero value + as a success code, then this script may not be used as it would consider that a failure. +#> + $cmdPath = $cmd + if(!(Test-Path -Type Leaf $cmd)) + { + $cmdPath = Find-OnPath $cmd + } + + if(!$cmdPath -or !(Test-Path -Type Leaf $cmdPath)) + { + throw "External command '$cmd' not found. (Not available on environment's current search path)" + } + + if($args) + { + & $cmdPath $args + if(!$?) + { + throw "'$cmdPath $args' command failed ($LASTEXITCODE)" + } + } + else + { + & $cmdPath + if(!$?) + { + throw "'$cmdPath' command failed ($LASTEXITCODE)" + } + } +} diff --git a/PsModules/CommonBuild/Public/Invoke-TimedBlock.ps1 b/PsModules/CommonBuild/Public/Invoke-TimedBlock.ps1 new file mode 100644 index 0000000000..434de71bf1 --- /dev/null +++ b/PsModules/CommonBuild/Public/Invoke-TimedBlock.ps1 @@ -0,0 +1,38 @@ + +function Invoke-TimedBlock([string]$activity, [ScriptBlock]$block) +{ +<# +.SYNOPSIS + Invokes a script block with a timer + +.PARAMETER activity + Name of the activity to output as part of Write-Information messages for the timer + +.PARAMETER block + Script block to execute with the timer + +.DESCRIPTION + This will print a start (via Write-Information), start the timer, run the script block stop the timer + then print a finish message indicating the total time the script block took to run. +#> + $timer = [System.Diagnostics.Stopwatch]::StartNew() + Write-Information "Starting: $activity" + try + { + Invoke-Command -ScriptBlock $block + } + catch + { + # Everything from the official docs to the various articles in the blog-sphere says this isn't needed + # and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location + # information is retained and the error reported will include the correct source file and line number + # data for the error. Without this, only the error message is retained and the location information is + # Line 1, Column 1, of the outer most script file, which is, of course, completely useless. + throw + } + finally + { + $timer.Stop() + Write-Information "Finished: $activity - Time: $($timer.Elapsed.ToString())" + } +} diff --git a/PsModules/CommonBuild/Public/New-CMakeSettings.ps1 b/PsModules/CommonBuild/Public/New-CMakeSettings.ps1 new file mode 100644 index 0000000000..8e2b61803b --- /dev/null +++ b/PsModules/CommonBuild/Public/New-CMakeSettings.ps1 @@ -0,0 +1,16 @@ +function New-CmakeSettings( [Parameter(Mandatory, ValueFromPipeline)][CMakeConfig] $configuration ) +{ + BEGIN + { + $convertedSettings = [System.Collections.Generic.List[hashtable]]::new( ) + } + PROCESS + { + $convertedSettings.Add( $configuration.ToCMakeSettingsJsonifiable( ) ) + } + END + { + ConvertTo-Json -Depth 4 @{ configurations = $convertedSettings } + } +} + diff --git a/PsModules/CommonBuild/Public/ReadMe.md b/PsModules/CommonBuild/Public/ReadMe.md new file mode 100644 index 0000000000..4264bf33a7 --- /dev/null +++ b/PsModules/CommonBuild/Public/ReadMe.md @@ -0,0 +1,4 @@ +# Common Build Public +As the name implies this is where the public script functions used by the common build +support are located. Everything here is considered public. Every function in these scripts +is exported from the common module. diff --git a/PsModules/CommonBuild/Public/Show-FullBuildInfo.ps1 b/PsModules/CommonBuild/Public/Show-FullBuildInfo.ps1 new file mode 100644 index 0000000000..08ad399d46 --- /dev/null +++ b/PsModules/CommonBuild/Public/Show-FullBuildInfo.ps1 @@ -0,0 +1,37 @@ +function Show-FullBuildInfo +{ +<# +.SYNOPSIS + Displays details of the build information and environment to the information and verbose streams + +.PARAMETER buildInfo + The build information Hashtable for the build. This normally contains the standard and repo specific + properties so that the full details are available in logs. + +.DESCRIPTION + This function displays all the properties of the buildinfo to the information stream. Additionally, + details of the current PATH, the .NET SDKs and runtimes installed is logged to the Verbose stream. +#> + Param($buildInfo) + + Write-Information 'Build Info:' + Write-Information ($buildInfo | Format-Table | Out-String) + + Write-Information "BuildKind: $($buildInfo['CurrentBuildKind'])" + Write-Information "CiBuildName: $env:CiBuildName" + Write-Information "env: Is*" + Write-Information (dir env:Is* | Format-Table -Property Name, value | Out-String) + + # This sort of detail is really only needed when solving problems with a runner + Write-Verbose 'PATH:' + $($env:Path -split ';') | %{ Write-Verbose $_ } + + Write-Verbose ".NET Runtimes:" + Write-Verbose (dotnet --list-runtimes | Out-String) + + Write-Verbose ".NET SDKs:" + Write-Verbose (dotnet --list-sdks | Out-String) + + Write-Verbose "GITHUB vars" + Write-Verbose (dir env:GITHUB* | Format-Table -Property Name, value | Out-String) +} diff --git a/PsModules/CommonBuild/ReadMe.md b/PsModules/CommonBuild/ReadMe.md new file mode 100644 index 0000000000..d5a4d5fd95 --- /dev/null +++ b/PsModules/CommonBuild/ReadMe.md @@ -0,0 +1,30 @@ +# COMMON Build +This contains the common build support for the Ubiquity.NET family of projects. This +PowerShell module is common to ALL projects and repossitories, and therfore ***MUST NOT*** +contain any repository specific support. + +The general pattern is to use a VERY common script as the top level that will "dot source" +all the real functionality and export any of the public functions in those scripts. The +script source code is organized into three folders: +1) This folder + - This contains the core PSD1 and PSM1 files that don't need to change as things are + updated unless adding new exported functions. Then the PSD1 should include those + functions so that Powershell can load the module when used. +2) Public Folder + - This folder contains all of the public "exported" functions. Most functions have a + 1:1 relationship with the name of the file, but occasionally multiple related functions + are grouped into a single source file. To help maintain clarity on that, the scripts + that contain a single function are named for the function following standard Powershell + guidelines for function names. Scripts that contain multiple related functions or type + declarations do not use the standard `-` naming pattern (Though the functions + they contain do) +3) Private folder + - This folder contains the private functions that are local to this module and not + exported from it. These are considers internal helpers and implementation details. + +## Credits +The layout and design of this module was inspired by an [answer](https://stackoverflow.com/a/44512990) +to a StackOverflow question and a linked +[blog article](https://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/). +The implementation here is a unique expression of a generalized form of the ideas presented +in those sources. diff --git a/PsModules/CommonBuild/en-us/about_CommonBuild.help.txt b/PsModules/CommonBuild/en-us/about_CommonBuild.help.txt new file mode 100644 index 0000000000..bf99f6fbe2 --- /dev/null +++ b/PsModules/CommonBuild/en-us/about_CommonBuild.help.txt @@ -0,0 +1,21 @@ +TOPIC + about_CommonBuild + +SHORT DESCRIPTION + A module for common (Repo-independent) functionality of the Ubiquity.NET.* family of projects. + +LONG DESCRIPTION + A module for common (Repo-independent) functionality of the Ubiquity.NET.* family of projects. + This module contains build support scripts that are (ideally) platform agnostic and repository + agnostic. (The platform agnosticism is a work in progress, there are several places where it + does matter and those are currently implemented to detect Windows-X64 and error out if not. + Eventually those should expand to support more/all runtimes) + +EXAMPLES + Examples of how to use the module or how the subject feature works in practice. + +KEYWORDS + Terms or titles on which you might expect your users to search for the information in this topic. + +SEE ALSO + Text-only references for further reading. Hyperlinks can't work in the PowerShell console. diff --git a/Push-Docs.ps1 b/Push-Docs.ps1 index 37175a9d1c..ef3dac503b 100644 --- a/Push-Docs.ps1 +++ b/Push-Docs.ps1 @@ -32,11 +32,11 @@ if(!$canPush) # Docs must only be updated from a build with the official repository as the default remote. # This ensures that the links to source in the generated docs will have the correct URLs # (e.g. docs pushed to the official repository MUST not have links to source in some private fork) -$remoteUrl = git ls-remote --get-url +$remoteUrl = Invoke-Git ls-remote --get-url Write-Information "Remote URL: $remoteUrl" -if(!($remoteUrl -like "https://github.com/UbiquityDotNET/Llvm.NET*")) +if($remoteUrl -ne $buildInfo['OfficialGitRemoteUrl']) { throw "Pushing docs is only allowed when the origin remote is the official source - current remote is '$remoteUrl'" } @@ -56,38 +56,37 @@ if(!$env:docspush_username) Write-Error "Missing docspush_username" -ErrorAction Stop } -pushd .\BuildOutput\docs -ErrorAction Stop +Push-Location .\BuildOutput\docs -ErrorAction Stop try { if($env:docspush_access_token) { - git config --local credential.helper store + Invoke-Git config --local credential.helper store Add-Content "$env:USERPROFILE\.git-credentials" "https://$($env:docspush_access_token):x-oauth-basic@github.com`n" } - git config --local user.email "$env:docspush_email" - git config --local user.name "$env:docspush_username" + Invoke-Git config --local user.email "$env:docspush_email" + Invoke-Git config --local user.name "$env:docspush_username" + + # This repo uses docfx to generate the site content, not Jekyll the site MUST include + # a '.nojekyll' file. This is apparently true for EVERY push, but isn't entirely clear + # as there is almost no documentation of the existence of such a thing, let alone how + # to use it. This makes it a VERY annoying and poorly documented 'feature'. (Disabling + # Jekyll should be a repo setting done once for a repo or organization.) + [DateTime]::UtcNow.ToString('o') | Out-File .nojekyll Write-Information 'Adding files to git' - git add -A - git ls-files -o --exclude-standard | %{ git add $_} - if(!$?) - { - throw "git add failed" - } + Invoke-Git add -A + Invoke-Git ls-files -o --exclude-standard | %{ git add $_} $msg = "CI Docs Update $(Get-BuildVersionTag $buildInfo)" Write-Information "Committing changes to git [$msg]" - git commit -m"$msg" - if(!$?) - { - throw "git commit failed" - } + Invoke-Git commit -m"$msg" if(!$SkipPush) { Write-Information 'Pushing changes to git' - git push + Invoke-Git push } } catch @@ -102,5 +101,5 @@ catch finally { - popd + Pop-Location } diff --git a/README.md b/README.md index 09c1b419ec..2194bf6f5c 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,70 @@ -## Ubiquity.NET.Llvm +# Ubiquity.NET.Llvm +Projection (bindings) library for LLVM to .NET consumption. -#### Build Status +## Library Information +### Build Status [![CI-Build](https://github.com/UbiquityDotNET/Llvm.NET/workflows/CI-Build/badge.svg?branch=master&event=push)](https://github.com/UbiquityDotNET/Llvm.NET/actions?query=workflow%3ACI-Build+branch%3Amaster+is%3Ain_progress) ![Release-Build](https://github.com/UbiquityDotNET/Llvm.NET/workflows/Release-Build/badge.svg) + ### NuGet [![NuGet](https://img.shields.io/nuget/dt/Ubiquity.NET.Llvm.svg)](https://www.nuget.org/packages/Ubiquity.NET.Llvm/) For details of releases, see the [release notes](https://github.com/UbiquityDotNET/Llvm.NET/blob/develop/docfx/ReleaseNotes.md) -### Welcome to Ubiquity.NET.Llvm! -Ubiquity.NET.Llvm provides LLVM language and runtime bindings for .NET based applications. Ubiquity.NET.Llvm's goal is to provide -a robust Class library that accurately reflects the underlying LLVM C++ model. This is done through an extended -LLVM-C API bundled as a native windows DLL (LibLLVM.DLL). Ubiquity.NET.Llvm uses the support of LibLLVM to gain access -to the LLVM class library and project it into a .NET managed library that reflects the original class library -design. +## Welcome to Ubiquity.NET.Llvm! +Ubiquity.NET.Llvm provides LLVM language and runtime bindings for .NET based applications. Ubiquity.NET.Llvm's +goal is to provide a robust Class library that accurately reflects the underlying LLVM C++ model. This is done +through an extended LLVM-C API bundled as a native library (LibLLVM). Ubiquity.NET.Llvm uses the support of +LibLLVM to gain access to the LLVM class library and project it into a .NET managed library that reflects the +original class library design as best as possible. The goal is to match the original class model as closely as possible, while providing an object model to .NET applications that feels familiar and consistent with common styles and patterns in .NET Framework applications. Thus, while class, method and enumeration names are similar to their counterparts in LLVM, they are not always identical. ->NOTE: ->The name of the library (and Nuget packages changed with V10.0) Instead of Llvm.NET* it is now Ubiquity.NET.Llvm*. ->This helps reflect the owning organization better and also helps in identifying a shift in the licensing (LLVM itself ->changed the license for v10*) - ### Brief History -Ubiquity.NET.Llvm was initially developed as a means to leverage LLVM as the back-end for an Ahead-Of-Time (AOT) compilation -tool for .NET applications targeting micro-controllers (e.g. An AOT compiler for the [.NET Micro Framework](https://github.com/NETMF/netmf-interpreter) ). -The initial proof of concept built on Ubiquity.NET.Llvm was successful in delivering on a basic application that could -implement the micro controller equivalent of the classic "Hello World!" application (e.g. Blinky - an app that -blinks an LED) using LLVM as the back-end code generator. This led to the revival of a former project doing AOT -with its own code generator that was tied to the ARMv4 Instruction set. ([Llilum](https://www.github.com/netmf/Llilum)). -Ubiquity.NET.Llvm has continued to evolve and improve and remains a distinct project as it has no dependencies on Llilum -or any of its components. Ubiquity.NET.Llvm is viable for any .NET applications wishing to leverage the functionality of -the LLVM libraries from .NET applications. - -Ubiquity.NET.Llvm began with LLVM 3.4 as a C++/CLI wrapper which enabled a closer binding to the original C++ object model -then the official LLVM-C API supported. As Ubiquity.NET.Llvm progressed so to did LLVM. Eventually the LLVM code base -migrated to requiring C++/11 support in the language to build. This became an issue for the C++/CLI wrapper as the -Microsoft C++/CLI compiler didn't support the C++11 syntax. Thus a change was made to Ubiquity.NET.Llvm to move to an extended -C API with a C# adapter layer to provide the full experience .NET developers expect. While the transition was a -tedious one very little application code required changes. LLVM and Ubiquity.NET.Llvm have continued to progress and Ubiquity.NET.Llvm -is currently based on LLVM 10.0.0 +Ubiquity.NET.Llvm was initially developed as a means to leverage LLVM as the back-end for an Ahead-Of-Time (AOT) +compilation tool for .NET applications targeting micro-controllers (e.g. An AOT compiler for the [.NET Micro +Framework](https://github.com/NETMF/netmf-interpreter) ). +The initial proof of concept built on Ubiquity.NET.Llvm was successful in delivering on a basic application +that could implement the micro controller equivalent of the classic "Hello World!" application (e.g. Blinky: +an app that blinks an LED) using LLVM as the back-end code generator. This led to the revival of a former +project doing AOT with its own code generator that was tied to the ARMv4 Instruction set. +([Llilum](https://www.github.com/netmf/Llilum)). Ubiquity.NET.Llvm has continued to evolve and improve and +remains a distinct project as it has no dependencies on Llilum or any of its components. Ubiquity.NET.Llvm is +viable for any .NET applications wishing to leverage the functionality of the LLVM libraries from .NET +applications. + +Ubiquity.NET.Llvm began with LLVM 3.4 as a C++/CLI wrapper which enabled a closer binding to the original C++ +object model then the official LLVM-C API supported. As Ubiquity.NET.Llvm progressed so to did LLVM. Eventually +the LLVM code base migrated to requiring C++/11 support in the language to build. This became an issue for the +C++/CLI wrapper as the Microsoft C++/CLI compiler didn't support the C++11 syntax. Thus a change was made to +Ubiquity.NET.Llvm to move to an extended C API with a C# adapter layer to provide the full experience .NET +developers expect. While the transition was a tedious one very little application code required changes. +LLVM and Ubiquity.NET.Llvm have continued to progress and Ubiquity.NET.Llvm is currently based on LLVM 20.1.x. +There are a few major goals of the current release that required breaking changes: +1) AOT compilation of applications leveraging this library + - While this goal is not yet fully realized many steps were taken to aid in getting there +2) Platform independence + - Again, not fully realized yet but many steps were taken to aid in getting there. + * The largest impediment is the massive resource requirements of building the native LLVM libraries + for a given runtime. Building them runs afoul of the limitations of every available OSS not to mention + exceeding the size of a NUGET package to host the binaries. Solving this is the major effort needed + for x-plat scenarios. This release goes a LONG way to changing how the native binaries are built and + distributed which hopefully makes true x-plat easier. +3) Performance improvements + - There's a LOT of marshaling (especially for strings) that goes on under the hood and this release + provides a lOT of optimizations for that and other scenarios to reduce the overhead needed. ### Platform Support -Currently Ubiquity.NET.Llvm supports x64 builds targeting the full desktop framework v4.7 and .NET standard 2.0. Ideally -other platforms are possible in the future. To keep life simpler the Ubiquity.NET.Llvm NuGet package is built for the "AnyCPU" -platform and references the Ubiquity.NET.Llvm.Interop package to bring in the native binary support. Ubiquity.NET.Llvm.Interop -contains code to dynamically detect the platform it is running on and load the appropriate native DLL. This allows applications -to build for AnyCPU without creating multiple build configurations and release vehicles for applications. (Any new platforms -would need to update the dynamic loading support and include the appropriate P/Invokable binaries - consuming apps would not -need to change except to pick up a new package version.) +Currently Ubiquity.NET.Llvm supports win-x64 builds targeting .NET 9.0. Ideally, other platforms are possible +in the future. To keep life simpler the Ubiquity.NET.Llvm NuGet package is built for the "AnyCPU" platform and +references the Ubiquity.NET.Llvm.Interop package to bring in the native binary support. +Ubiquity.NET.Llvm.Interop contains code to dynamically detect the platform it is running on and load the +appropriate native library. This allows applications to build for AnyCPU without creating multiple build +configurations and release vehicles for applications. (Any new platforms would need to update the dynamic +loading support and include the appropriate P/Invokable binaries - consuming apps would not need to change +except to pick up a new package version.) ### CI Build NuGet Packages The CI Builds of the NuGet package built from the latest source in the master branch are available as build artifacts from the build. @@ -76,18 +90,21 @@ A Ubiquity.NET.Llvm version of the LLVM sample [Kaleidoscope language tutorial]( ## Building Ubiquity.NET.Llvm ### Prerequisites -* Visual Studio 2019 (16.4+) [Community Edition OK] -* [7-Zip](https://www.7-zip.org/) [Used to unpack the pre-built LLVM libraries] +* Visual Studio 2022 (17.13.5+) [Community Edition OK, earlier versions of VS 2022 may be OK but are not tested] +>[!NOTE] +> Visual Studio Code is NOT officially supported. Such support is welcome as a PR as long as it does not impact +> the use of VS 2022 nor the automated build. [Obviously this comment should be removed if such a PR is made!] + #### Using Visual Studio -The repository contains Visual Studio solution files that allow building the components individually for modifying -Ubiquity.NET.Llvm and LibLLVM, as well as running the available unit tests. This is the primary mode of working with the -Ubiquity.NET.Llvm source code during development. +The repository contains Visual Studio solution files that allow building the components individually for +modifying Ubiquity.NET.Llvm, as well as running the available unit tests and samples. This is the primary mode +of working with the Ubiquity.NET.Llvm source code during development. ### Replicating the automated build -The Automated build support for Ubiquity.NET.Llvm uses Build-All.ps1 PowerShell script to build all the binaries and generate a -NuGet package. To build the full package simply run `Build-All.ps1 -ForceClean` from a PowerShell command prompt with MSBuild tools -on the system search path. +The Automated build support for Ubiquity.NET.Llvm uses Build-All.ps1 PowerShell script to build all the binaries +and generate a NuGet package. To build the full package simply run `Build-All.ps1 -ForceClean` from a PowerShell +command prompt with MSBuild tools on the system search path. ### Code of Conduct This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/) diff --git a/Samples/CodeGenWithDebugInfo/ITargetDependentDetails.cs b/Samples/CodeGenWithDebugInfo/ITargetDependentDetails.cs deleted file mode 100644 index 1e3ce84cf2..0000000000 --- a/Samples/CodeGenWithDebugInfo/ITargetDependentDetails.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; - -using Ubiquity.NET.Llvm; -using Ubiquity.NET.Llvm.Values; - -namespace TestDebugInfo -{ - #region ITargetDependentDetails - internal interface ITargetDependentDetails - { - string ShortName { get; } - - TargetMachine TargetMachine { get; } - - IEnumerable BuildTargetDependentFunctionAttributes( Context ctx ); - - void AddABIAttributesForByValueStructure( IrFunction function, int paramIndex ); - - void AddModuleFlags( BitcodeModule module ); - } - #endregion -} diff --git a/Samples/CodeGenWithDebugInfo/X64Details.cs b/Samples/CodeGenWithDebugInfo/X64Details.cs deleted file mode 100644 index 97b281dbd5..0000000000 --- a/Samples/CodeGenWithDebugInfo/X64Details.cs +++ /dev/null @@ -1,68 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; - -using Ubiquity.NET.Llvm; -using Ubiquity.NET.Llvm.Interop; -using Ubiquity.NET.Llvm.Types; -using Ubiquity.NET.Llvm.Values; - -namespace TestDebugInfo -{ - internal class X64Details - : ITargetDependentDetails - { - public X64Details( ILibLlvm libLLVM ) - { - libLLVM.RegisterTarget( CodeGenTarget.X86 ); - } - - public string ShortName => "x86"; - - public TargetMachine TargetMachine => TargetMachine.FromTriple( new Triple( TripleName ) - , Cpu - , Features - , CodeGenOpt.Aggressive - , RelocationMode.Default - , CodeModel.Small - ); - - public void AddABIAttributesForByValueStructure( IrFunction function, int paramIndex ) - { - if( !( function.Parameters[ paramIndex ].NativeType is IPointerType argType ) || !argType.ElementType.IsStruct ) - { - throw new ArgumentException( "Signature for specified parameter must be a pointer to a structure" ); - } - } - - public void AddModuleFlags( BitcodeModule module ) - { - module.AddModuleFlag( ModuleFlagBehavior.Error, "PIC Level", 2 ); - } - - public IEnumerable BuildTargetDependentFunctionAttributes( Context ctx ) - => new List - { - ctx.CreateAttribute("disable-tail-calls", "false" ), - ctx.CreateAttribute( "less-precise-fpmad", "false" ), - ctx.CreateAttribute( "no-frame-pointer-elim", "false" ), - ctx.CreateAttribute( "no-infs-fp-math", "false" ), - ctx.CreateAttribute( "no-nans-fp-math", "false" ), - ctx.CreateAttribute( "stack-protector-buffer-size", "8" ), - ctx.CreateAttribute( "target-cpu", Cpu ), - ctx.CreateAttribute( "target-features", Features ), - ctx.CreateAttribute( "unsafe-fp-math", "false" ), - ctx.CreateAttribute( "use-soft-float", "false" ), - ctx.CreateAttribute( AttributeKind.UWTable ) - }; - - private const string Cpu = "x86-64"; - private const string Features = "+sse,+sse2"; - private const string TripleName = "x86_64-pc-windows-msvc18.0.0"; - } -} diff --git a/Samples/Kaleidoscope/Chapter4/Chapter4.csproj b/Samples/Kaleidoscope/Chapter4/Chapter4.csproj deleted file mode 100644 index d162c34ca0..0000000000 --- a/Samples/Kaleidoscope/Chapter4/Chapter4.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net5.0 - kls4 - Kaleidoscope - - - - - - - - diff --git a/Samples/Kaleidoscope/Chapter4/CodeGenerator.cs b/Samples/Kaleidoscope/Chapter4/CodeGenerator.cs deleted file mode 100644 index df395bdb52..0000000000 --- a/Samples/Kaleidoscope/Chapter4/CodeGenerator.cs +++ /dev/null @@ -1,314 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -using Kaleidoscope.Grammar; -using Kaleidoscope.Grammar.AST; -using Kaleidoscope.Runtime; - -using Ubiquity.ArgValidators; -using Ubiquity.NET.Llvm; -using Ubiquity.NET.Llvm.Instructions; -using Ubiquity.NET.Llvm.JIT; -using Ubiquity.NET.Llvm.Transforms; -using Ubiquity.NET.Llvm.Values; - -using ConstantExpression = Kaleidoscope.Grammar.AST.ConstantExpression; - -namespace Kaleidoscope.Chapter4 -{ - /// Performs LLVM IR Code generation from the Kaleidoscope AST - public sealed class CodeGenerator - : AstVisitorBase - , IDisposable - , IKaleidoscopeCodeGenerator - { - #region Initialization - public CodeGenerator( DynamicRuntimeState globalState, bool disableOptimization = false, TextWriter? outputWriter = null ) - : base( null ) - { - JIT.OutputWriter = outputWriter ?? Console.Out; - globalState.ValidateNotNull( nameof( globalState ) ); - if( globalState.LanguageLevel > LanguageLevel.SimpleExpressions ) - { - throw new ArgumentException( "Language features not supported by this generator", nameof( globalState ) ); - } - - RuntimeState = globalState; - Context = new Context( ); - DisableOptimizations = disableOptimization; - InitializeModuleAndPassManager( ); - InstructionBuilder = new InstructionBuilder( Context ); - } - #endregion - - #region Dispose - public void Dispose( ) - { - JIT.Dispose( ); - Module?.Dispose( ); - Context.Dispose( ); - } - #endregion - - #region Generate - public OptionalValue Generate( IAstNode ast ) - { - ast.ValidateNotNull( nameof( ast ) ); - - // Prototypes, including extern are ignored as AST generation - // adds them to the RuntimeState so that already has the declarations - if( ast is not FunctionDefinition definition ) - { - return default; - } - - InitializeModuleAndPassManager( ); - Debug.Assert( Module is not null , "Module initialization failed" ); - - var function = ( IrFunction )(definition.Accept( this ) ?? throw new CodeGeneratorException(ExpectValidFunc)); - - if( definition.IsAnonymous ) - { - // eagerly compile modules for anonymous functions as calling the function is the guaranteed next step - ulong jitHandle = JIT.AddEagerlyCompiledModule( Module ); - var nativeFunc = JIT.GetFunctionDelegate( definition.Name ); - var retVal = Context.CreateConstant( nativeFunc( ) ); - JIT.RemoveModule( jitHandle ); - return OptionalValue.Create( retVal ); - } - else - { - // Destroy any previously generated module for this function. - // This allows re-definition as the new module will provide the - // implementation. This is needed, otherwise both the MCJIT - // and OrcJit engines will resolve to the original module, despite - // claims to the contrary in the official tutorial text. (Though, - // to be fair it may have been true in the original JIT and might - // still be true for the interpreter) - if( FunctionModuleMap.Remove( definition.Name, out ulong handle ) ) - { - JIT.RemoveModule( handle ); - } - - // Unknown if any future input will call the function so add it for lazy compilation. - // Native code is generated for the module automatically only when required. - ulong jitHandle = JIT.AddLazyCompiledModule( Module ); - FunctionModuleMap.Add( definition.Name, jitHandle ); - return OptionalValue.Create( function ); - } - } - #endregion - - #region ConstantExpression - public override Value? Visit( ConstantExpression constant ) - { - constant.ValidateNotNull( nameof( constant ) ); - return Context.CreateConstant( constant.Value ); - } - #endregion - - #region BinaryOperatorExpression - public override Value? Visit( BinaryOperatorExpression binaryOperator ) - { - binaryOperator.ValidateNotNull( nameof( binaryOperator ) ); - switch( binaryOperator.Op ) - { - case BuiltInOperatorKind.Less: - { - var tmp = InstructionBuilder.Compare( RealPredicate.UnorderedOrLessThan - , binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "cmptmp" ); - return InstructionBuilder.UIToFPCast( tmp, InstructionBuilder.Context.DoubleType ) - .RegisterName( "booltmp" ); - } - - case BuiltInOperatorKind.Pow: - { - var pow = GetOrDeclareFunction( new Prototype( "llvm.pow.f64", "value", "power" ) ); - return InstructionBuilder.Call( pow - , binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "powtmp" ); - } - - case BuiltInOperatorKind.Add: - return InstructionBuilder.FAdd( binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "addtmp" ); - - case BuiltInOperatorKind.Subtract: - return InstructionBuilder.FSub( binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "subtmp" ); - - case BuiltInOperatorKind.Multiply: - return InstructionBuilder.FMul( binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "multmp" ); - - case BuiltInOperatorKind.Divide: - return InstructionBuilder.FDiv( binaryOperator.Left.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - , binaryOperator.Right.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidExpr ) - ).RegisterName( "divtmp" ); - - default: - throw new CodeGeneratorException( $"ICE: Invalid binary operator {binaryOperator.Op}" ); - } - } - #endregion - - #region FunctionCallExpression - public override Value? Visit( FunctionCallExpression functionCall ) - { - if( Module is null ) - { - throw new InvalidOperationException( "Can't visit a function call without an active module" ); - } - - functionCall.ValidateNotNull( nameof( functionCall ) ); - string targetName = functionCall.FunctionPrototype.Name; - - IrFunction? function; - if( RuntimeState.FunctionDeclarations.TryGetValue( targetName, out Prototype? target ) ) - { - function = GetOrDeclareFunction( target ); - } - else if( !Module.TryGetFunction( targetName, out function ) ) - { - throw new CodeGeneratorException( $"Definition for function {targetName} not found" ); - } - - var args = ( from expr in functionCall.Arguments - select expr.Accept( this ) ?? throw new CodeGeneratorException(ExpectValidExpr) - ).ToArray(); - - return InstructionBuilder.Call( function, args ).RegisterName( "calltmp" ); - } - #endregion - - #region FunctionDefinition - public override Value? Visit( FunctionDefinition definition ) - { - definition.ValidateNotNull( nameof( definition ) ); - var function = GetOrDeclareFunction( definition.Signature ); - if( !function.IsDeclaration ) - { - throw new CodeGeneratorException( $"Function {function.Name} cannot be redefined in the same module" ); - } - - try - { - var entryBlock = function.AppendBasicBlock( "entry" ); - InstructionBuilder.PositionAtEnd( entryBlock ); - NamedValues.Clear( ); - foreach( var param in definition.Signature.Parameters ) - { - NamedValues[ param.Name ] = function.Parameters[ param.Index ]; - } - - var funcReturn = definition.Body.Accept( this ) ?? throw new CodeGeneratorException( ExpectValidFunc ); - InstructionBuilder.Return( funcReturn ); - function.Verify( ); - - FunctionPassManager?.Run( function ); - return function; - } - catch( CodeGeneratorException ) - { - function.EraseFromParent( ); - throw; - } - } - #endregion - - #region VariableReferenceExpression - public override Value? Visit( VariableReferenceExpression reference ) - { - reference.ValidateNotNull( nameof( reference ) ); - if( !NamedValues.TryGetValue( reference.Name, out Value? value ) ) - { - // Source input is validated by the parser and AstBuilder, therefore - // this is the result of an internal error in the generator rather - // then some sort of user error. - throw new CodeGeneratorException( $"ICE: Unknown variable name: {reference.Name}" ); - } - - return value; - } - #endregion - - #region InitializeModuleAndPassManager - private void InitializeModuleAndPassManager( ) - { - Module = Context.CreateBitcodeModule( ); - Module.Layout = JIT.TargetMachine.TargetData; - FunctionPassManager = new FunctionPassManager( Module ); - - if( !DisableOptimizations ) - { - FunctionPassManager.AddInstructionCombiningPass( ) - .AddReassociatePass( ) - .AddGVNPass( ) - .AddCFGSimplificationPass( ); - } - - FunctionPassManager.Initialize( ); - } - #endregion - - #region GetOrDeclareFunction - - // Retrieves a Function for a prototype from the current module if it exists, - // otherwise declares the function and returns the newly declared function. - private IrFunction GetOrDeclareFunction( Prototype prototype ) - { - if( Module is null ) - { - throw new InvalidOperationException( "ICE: Can't get or declare a function without an active module" ); - } - - if( Module.TryGetFunction( prototype.Name, out IrFunction? function ) ) - { - return function; - } - - var llvmSignature = Context.GetFunctionType( Context.DoubleType, prototype.Parameters.Select( _ => Context.DoubleType ) ); - var retVal = Module.CreateFunction( prototype.Name, llvmSignature ); - - int index = 0; - foreach( var argId in prototype.Parameters ) - { - retVal.Parameters[ index ].Name = argId.Name; - ++index; - } - - return retVal; - } - #endregion - - private const string ExpectValidExpr = "Expected a valid expression"; - private const string ExpectValidFunc = "Expected a valid function"; - - #region PrivateMembers - private readonly DynamicRuntimeState RuntimeState; - private readonly Context Context; - private readonly InstructionBuilder InstructionBuilder; - private readonly IDictionary NamedValues = new Dictionary( ); - private FunctionPassManager? FunctionPassManager; - private readonly bool DisableOptimizations; - private BitcodeModule? Module; - private readonly KaleidoscopeJIT JIT = new( ); - private readonly Dictionary FunctionModuleMap = new( ); - #endregion - } -} diff --git a/Samples/Kaleidoscope/Chapter4/Kaleidoscope-ch4.md b/Samples/Kaleidoscope/Chapter4/Kaleidoscope-ch4.md deleted file mode 100644 index 2e4337c40b..0000000000 --- a/Samples/Kaleidoscope/Chapter4/Kaleidoscope-ch4.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -uid: Kaleidoscope-ch4 ---- - -# 4. Kaleidoscope: Adding JIT and Optimizer Support -This chapter of the Kaleidoscope tutorial introduces Just-In-Time (JIT) compilation and simple optimizations -of the generated code. As, such this is the first variant of the language implementation where you can actually -execute the Kaleidoscope code. Thus, this is a bit more fun than the others as you finally get to see the -language working for real! - -## Constant Folding -If you studied the LLVM IR generated from the previous chapters you will see that it isn't particularly -well optimized. There is one case, though, where it does do some nice optimization automatically for us. - -For example: -```Kaleidoscope -def test(x) 1+2+x; -``` - -produces the following LLVM IR: -```llvm -define double @test(double %x) { -entry: - %addtmp = fadd double 3.000000e+00, %x - ret double %addtmp -} -``` - -That's not exactly what the parse tree would suggest. The [InstructionBuilder](xref:Ubiquity.NET.Llvm.Instructions.InstructionBuilder) -automatically performs an optimization technique known as 'Constant Folding'. This optimization is very -important, in fact, many compilers implement the folding directly into the generation of the Abstract -Syntax Tree (AST). With LLVM, that isn't necessary as it is automatically provided for you (no extra -charge!). - -Obviously constant folding isn't the only possible optimization and InstructionBuilder only operates on -the individual instructions as they are built. So, there are limits on what InstructionBuilder can do. - -For example: - -```Kaleidoscope -def test(x) (1+2+x)*(x+(1+2)); -``` - -```llvm -define double @test(double %x) { -entry: - %addtmp = fadd double 3.000000e+00, %x - %addtmp1 = fadd double %x, 3.000000e+00 - %multmp = fmul double %addtmp, %addtmp1 - ret double %multmp -} -``` - -In this case the operand of the additions are identical. Ideally this would generate as -`temp = x+3; result = temp*temp;` rather than computing X+3 twice. This isn't something that -InstructionBuilder alone can do. Ultimately this requires two distinct transformations: - 1. Re-association of expressions to make the additions lexically identical (e.g. recognize that x+3 == 3+x ) - 1. Common Subexpression Elimination to remove the redundant add instruction. - -Fortunately, LLVM provides a very broad set of optimization transformations that can handle this and many -other scenarios. - -## LLVM Optimization Passes -LLVM provides many different optimization passes, each handling a specific scenario with different trade-offs. -One of the values of LLVM as a general compilation back-end is that it doesn't enforce any particular set -of optimizations. By default, there aren't any optimizations (Other than the obvious constant folding built -into the InstructionBuilder). All optimizations are entirely in the hands of the front-end application. -The compiler implementor controls what passes are applied, and in what order they are run. This ensures -that the optimizations are tailored to correctly meet the needs of the language and runtime environment. - -For Kaleidoscope, optimizations are limited to a single function as they are generated when the user types -them in on the command line. Ultimate, whole program optimization is off the table (You never know when the -user will enter the last expression so it is incorrect to eliminate unused functions). In order to support -per-function optimization a [FunctionPassManager](xref:Ubiquity.NET.Llvm.Transforms.FunctionPassManager) is created -to hold the passes used for optimizing a function. The FunctionPassManager supports running the passes -to transform a function into the optimized form. Since a pass manager is tied to the module and, for JIT -support, each function is generated into its own module a new method in the code generator is used to create -the module and initialize the pass manager. - -[!code-csharp[Main](CodeGenerator.cs#InitializeModuleAndPassManager)] - -Creating the pass manager isn't enough to get the optimizations. Something needs to actually provide the -pass manager with the function to optimize. The most sensible place to put that is as the last step of -generating the function. - -```C# -FunctionPassManager.Run( function ); -``` - -This will run the passes defined when the FunctionPassManager was created, resulting in better generated code. - -```llvm -define double @test(double %x) { -entry: - %addtmp = fadd double %x, 3.000000e+00 - %multmp = fmul double %addtmp, %addtmp - ret double %multmp -} -``` - -The passes eliminate the redundant add instructions to produce a simpler, yet still correct representation -of the generated code. LLVM provides a wide variety of optimization passes. Unfortunately not all are well -documented, yet. Looking into what Clang uses is helpful as is using the LLVM 'opt.exe' tool to run passes -individually or in various combinations and ordering to see how well it optimizes the code based on what -your front-end generates. (This can lead to changing the passes and ordering, as well as changes in what -the front-end generates so that the optimizer can handle the input better) This is not an exact science -with a one size fits all kind of solution. There are many common passes that are likely relevant to all -languages. Though the ordering of them may differ depending on the needs of the language and runtime. -Getting, the optimizations and ordering for a given language is arguably where the most work lies in -creating a production quality language using LLVM. - -## Adding JIT Compilation -Now that the code generation produces optimized code, it is time to get to the fun part - executing code! -The basic idea is to allow the user to type in the Kaleidoscope code as supported thus far and it will -execute to produce a result. Unlike the previous chapters, instead of just printing out the LLVM IR -representation of a top level expression it is executed and the results are provided back to the user. - -### Main Driver -The changes needed to the main driver are pretty simple, mostly consisting of removing a couple lines of -code that print out the LLVM IR for the module at the end and for each function when defined. The code -already supported showing the results if it was a floating point value by checking if the generated value -is a [ConstantFP](xref:Ubiquity.NET.Llvm.Values.ConstantFP). We'll see a bit later on why that is a ConstantFP -value. - -### Code Generator -The code generation needs an update to support using a JIT engine to generate and execute the Kaleidescope -code provided by the user. - -To use the Optimization transforms the generator needs a new namespace using declaration. - -```C# -using Ubiquity.NET.Llvm.Transforms; -``` - -#### Generator fields -To begin with, the generator needs some additional members, including the JIT engine. - -[!code-csharp[PrivateMembers](CodeGenerator.cs#PrivateMembers)] - -The JIT engine is retained for the generator to use. The same engine is retained for the lifetime of the -generator so that functions are added to the same engine and can call functions previously added. The JIT -provides a 'handle' for every module added, which is used to reference the module in the JIT, this is -normally used to remove the module from the JIT engine when re-defining a function. Thus, a map of the -function names and the JIT handle created for them is maintained. Additionally, a collection of defined -function prototypes is retained to enable matching a function call to a previously defined function. -Since the JIT support uses a module per function approach, lookups on the current module aren't sufficient. - -#### Generator initialization -The initialization of the generator requires updating to support the new members. - -[!code-csharp[Initialization](CodeGenerator.cs#Initialization)] -The bool indicating if optimizations are enabled or not is stored and an initial module and pass manager -is created. - -The option to disable optimizations is useful for debugging the code generation itself as optimizations -can alter or even eliminate incorrectly generated code. Thus, when modifying the generation itself, it -is useful to disable the optimizations. - -#### JIT Engine -The JIT engine itself is a class provided in the Kaleidoscope.Runtime library derived from the Ubiquity.NET.Llvm -OrcJIT engine. - -[!code-csharp[Kaleidoscope JIT](../../../Samples/Kaleidoscope/Kaleidoscope.Runtime/KaleidoscopeJIT.cs)] - -[OrcJit](xref:Ubiquity.NET.Llvm.JIT.OrcJit) provides support for declaring functions that are external to the JIT -that the JIT'd module code can call. For Kaleidoscope, two such functions are defined directly in -KaleidoscopeJIT (putchard and printd), which is consistent with the same functions used in the official -LLVM C++ tutorial. Thus, allowing sharing of samples between the two. These functions are used to provide -rudimentary console output support. - -> [!WARNING] -> All such methods implemented in .NET must block any exception from bubbling out of the call as the JIT -> engine doesn't know anything about them and neither does the Kaleidoscope language. Exceptions thrown -> in these functions would produce undefined results, at best - crashing the application. - -#### PassManager -Every time a new function definition is processed the generator creates a new module and initializes -the function pass manager for the module. This is done is a new method InitializeModuleAndPassManager() - -[!code-csharp[Initialization](CodeGenerator.cs#InitializeModuleAndPassManager)] - -The module creation is pretty straight forward, of importance is the layout information pulled from the -target machine for the JIT and applied to the module. - -Once the module is created, the [FunctionPassManager](xref:Ubiquity.NET.Llvm.Transforms.FunctionPassManager) is -constructed. If optimizations are not disabled, the optimization passes are added to the pass manager. -The set of passes used is a very basic set since the Kaleidoscope language isn't particularly complex -at this point. - - -#### Generator Dispose -Since the JIT engine is disposable, the code generators Dispose() method must now call the -Dispose() method on the JIT engine. - -[!code-csharp[Dispose](CodeGenerator.cs#Dispose)] - -#### Generate Method -To actually execute the code the generated modules are added to the JIT. If the function is an -anonymous top level expression, it is eagerly compiled and a delegate is retrieved from the JIT -to allow calling the compiled function directly. The delegate is then called to get the result. Once an anonymous function produces -a value, it is no longer used so is removed from the JIT and the result value returned. For other functions -the module is added to the JIT and the function is returned. - -For named function definitions, the module is lazy added to the JIT as it isn't known if/when the functions -is called. The JIT engine will compile modules lazy added into native code on first use. (Though if the -function is never used, then creating the IR module was wasted. ([Chapter 7.1](xref:Kaleidoscope-ch7.1) has a -solution for even that extra overhead - truly lazy JIT). Since Kaleidoscope is generally a dynamic language -it is possible and reasonable for the user to re-define a function (to fix an error, or provide a completely -different implementation all together). Therefore, any named functions are removed from the JIT, if they -existed, before adding in the new definition. Otherwise the JIT resolver would still resolve to the previously -compiled instance. - -[!code-csharp[Generate](CodeGenerator.cs#Generate)] - -Keeping all the JIT interaction in the generate method isolates the rest of the generation from any -awareness of the JIT. This will help when adding truly lazy JIT compilation in [Chapter 7.1](xref:Kaleidoscope-ch7.1) -and AOT compilation in [Chapter 8](xref:Kaleidoscope-ch8) - -#### Function call expressions -Since functions are no longer collected into a single module the code to find the target for a function -call requires updating to lookup the function from a collection of functions mapped by name. - -[!code-csharp[Main](CodeGenerator.cs#FunctionCallExpression)] - -This will lookup the function prototype by name and call the GetOrDeclareFunction() with the prototype -found. If the prototype wasn't found then it falls back to the previous lookup in the current module. -This fall back is needed to support recursive functions where the referenced function actually is in the -current module. - -#### GetOrDeclareFunction() -Next is to update the GetOrDeclareFunction() to handle mapping the functions prototype and re-definition -of functions. - -[!code-csharp[Main](CodeGenerator.cs#GetOrDeclareFunction)] - -This distinguishes the special case of an anonymous top level expression as those are never added to the -prototype maps. They are only in the JIT engine long enough to execute once and are then removed. Since -they are, by definition, anonymous they can never be referenced by anything else. - -#### Function Definitions -Visiting a function definition needs to add a call to the function pass manager to run the optimization -passes for the function. This, makes sense to do, immediately after completing the generation of the function. - -[!code-csharp[Main](CodeGenerator.cs#FunctionDefinition)] - - -## Conclusion -While the amount of words needed to describe the changes to support optimization and JIT execution here -isn't exactly small, the actual code changes required really are. The Parser and JIT engine do all the -heavy lifting. Ubiquity.NET.Llvm provides a clean interface to the JIT that fits with common patterns and runtime -support for .NET. Very cool, indeed! diff --git a/Samples/Kaleidoscope/Chapter5/Chapter5.csproj b/Samples/Kaleidoscope/Chapter5/Chapter5.csproj deleted file mode 100644 index b27593ab20..0000000000 --- a/Samples/Kaleidoscope/Chapter5/Chapter5.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net5.0 - kls5 - Kaleidoscope - - - - - - - - diff --git a/Samples/Kaleidoscope/Chapter7.1/Kaleidoscope-ch7.1.md b/Samples/Kaleidoscope/Chapter7.1/Kaleidoscope-ch7.1.md deleted file mode 100644 index 4639bd9bc1..0000000000 --- a/Samples/Kaleidoscope/Chapter7.1/Kaleidoscope-ch7.1.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -uid: Kaleidoscope-ch7.1 ---- -> [!CAUTION] -> This sample is presently not functional. There are bugs in the LLVM OrcJIT support on Windows platforms -> that prevents the fully lazy function generation callbacks from working. (See LLVM -> bugs [25493](https://bugs.llvm.org/show_bug.cgi?id=25493) and [28699](https://bugs.llvm.org/show_bug.cgi?id=28699) for details.) - -# 7. Kaleidoscope: Extreme Lazy JIT -In the previous chapters the code generation took an AST, converted it to LLVM IR, handed the IR to the -JIT, which then generated the native code. For a top level anonymous expression that is pretty much all -you need. But what if a function is defined but not used (yet)? The process of generating the IR, and then -subsequently the native code, is all wasted overhead in such a case. That's not really following through on -the "Just-In-Time" part of the JIT. This chapter focuses on resolving that with truly lazy JIT that doesn't -even generate the LLVM IR for a function until it is called for the first time. - -## Performance trade-offs -As with many things in software, there are trade-offs involved. In this case the trade-off is when you JIT -compile vs. lazy compile. This choice is a major element to efficient use of a JIT. The more you have to JIT -before anything can actually run the slower the application startup is. If you defer too much then the execution -slows down as everything needs to compile code. Ultimately, there is no one "right" solution as many factors -contribute to the results, including the level of optimizations applied during generation. (e.g. it might -achieve better results to generate unoptimized code during startup, and later regenerate optimized versions -of the most frequently used code.) - -The approach to balancing the trade-offs taken in this chapter is to eagerly compile top level expressions -as it is obvious they are going to be called, and discarded afterwards. For function definitions, it isn't -clear if the functions will or won't be called. While, the code generation could scan the function to find -all functions it calls to generate them all at the same time - there is no guarantee that the input arguments -to the function will go through a path that needs them all. Thus, for Kaleidoscope, function definitions are -all lazy compiled on first use. - -## General Concept of Lazy Compilation -The general idea is that the language runtime registers every lazy JIT function with the JIT by name with a -callback function to handle generating code for that function. This does two things in the JIT: - 1. Adds the name to the function symbol table in the JIT - 2. Creates a stub implementation function in native code that will call back to the JIT when application -code calls the function. - -The stub is implemented by the JIT to call back into the JIT in a way that includes the information needed -to identify the correct function to generate code for. The JIT will do some of it's own internal setup and -then call the code generation callback registered by the runtime code generator. This callback is what actually -generates the LLVM IR, and ultimately the native code, for the function. - -Once the function is generated the generator uses the JIT to update the stub so that, in the future, it will just -call to the generated function directly. One somewhat confusing aspect of this is that there are two symbols in -the JIT for what is really only one function. One, is the stub that remains at a fixed location (to allow pointer -to function patterns to work) the other is the JIT compiled actual implementation of the function. They can't both -have the same name so the code generation for the implementation must use a unique name. - -## Code changes for lazy JIT -Since the lazy JIT registers the callback stub with the function's name when the actual function is generated -it needs a new name. So, we add a new helper method to effectively clone a FunctionDefinition AST node while -renaming it. This only needs a shallow clone so there isn't a lot of overhead for it. - -[!code-csharp[CloneAndRenameFunction](CodeGenerator.cs#CloneAndRenameFunction)] - -The name used is the original function name plus the suffix `$impl` tacked onto the end. - -The next requirement is to change how we generate the functions. For an anonymous function the generation -is pretty much the same. There's really no point in going through the process of setting up the lazy JIT -when the next thing to do is get the address of the function and call it. For other definitions, though, -things get different as they are selected for lazy JIT. - -[!code-csharp[Generate](CodeGenerator.cs#Generate)] - -Function definitions for lazy JIT are first cloned and renamed, as discussed previously. Then a lazy -function generator is registered for the name of the function. This creates the stub function exported -by the function's name with a callback that knows how to generate the LLVM IR for the function. The -actual code generation call back is a lambda that simply initializes a new module and pass manager, -generates the function using the visitor pattern and returns the function's implementation name and -the containing module as a tuple. (This is where keeping the code generation ignorant of the JIT comes -in handy as the same code is called to generate a module and doesn't need to care if it is eager or lazy) - -The JIT implementation will do the following after the generator -callback returns: - 1. Add the returned module to the JIT - 2. Generate native code for the module - 3. Get the address of the implementation function - 4. Update the stub for the function with the address of the function instead of the internal callback - 5. return the address to the JIT engine so it can ultimately call the function and continue on it's merry way. - -## Conclusion -Implementing Lazy JIT support with Ubiquity.NET.Llvm is pretty simple and straight forward. It took many times more -words to describe then actual lines of code. Efficiently, supporting lazy JIT is a much more complex matter. -There are trade-offs doing things lazy, in particular the application can stall for a period, while the -system generates new code to run "on the fly". Optimizations, when fully enabled, add additional time to -the code generation. While, for some applications, it may be obvious whether these factors matter or not, in -general it's not something that can be known, thus the quest for optimal efficiency includes decisions -on eager vs lazy JIT as well as optimized JIT or not. This can include lazy JIT with minimal optimization -during startup of an app. Once things are up and going the engine can come back to re-generate the functions -with full optimization. All sorts of possibilities exist, but the basics of how the lazy and eager generation -works doesn't change no matter what approach a given language or runtime wants to use. diff --git a/Samples/Kaleidoscope/Chapter8/Chapter8.csproj b/Samples/Kaleidoscope/Chapter8/Chapter8.csproj deleted file mode 100644 index 51e581f96d..0000000000 --- a/Samples/Kaleidoscope/Chapter8/Chapter8.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - Exe - net5.0 - kls8 - Kaleidoscope - - - - - Always - - - - - - - - - diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/AstVisitorBase.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/AstVisitorBase.cs deleted file mode 100644 index 1dbd387f19..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/AstVisitorBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Diagnostics.CodeAnalysis; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class AstVisitorBase - : IAstVisitor - where TResult : class - { - public virtual TResult? Visit( RootNode root ) => VisitChildren( root ); - - public virtual TResult? Visit( ErrorNode errorNode ) => default; - - public virtual TResult? Visit( Prototype prototype ) => VisitChildren( prototype ); - - public virtual TResult? Visit( FunctionDefinition definition ) => VisitChildren( definition ); - - public virtual TResult? Visit( ConstantExpression constant ) => VisitChildren( constant ); - - public virtual TResult? Visit( VariableReferenceExpression reference ) => VisitChildren( reference ); - - public virtual TResult? Visit( FunctionCallExpression functionCall ) => VisitChildren( functionCall ); - - public virtual TResult? Visit( BinaryOperatorExpression binaryOperator ) => VisitChildren( binaryOperator ); - - public virtual TResult? Visit( VarInExpression varInExpression ) => VisitChildren( varInExpression ); - - public virtual TResult? Visit( ParameterDeclaration parameterDeclaration ) => VisitChildren( parameterDeclaration ); - - public virtual TResult? Visit( ConditionalExpression conditionalExpression ) => VisitChildren( conditionalExpression ); - - public virtual TResult? Visit( ForInExpression forInExpression ) => VisitChildren( forInExpression ); - - public virtual TResult? Visit( LocalVariableDeclaration localVariableDeclaration ) => VisitChildren( localVariableDeclaration ); - - public virtual TResult? VisitChildren( IAstNode node ) - { - node.ValidateNotNull( nameof( node ) ); - TResult? aggregate = DefaultResult; - foreach( var child in node.Children ) - { - aggregate = AggregateResult( aggregate, child.Accept( this ) ); - } - - return aggregate; - } - - protected AstVisitorBase( [AllowNull] TResult defaultResult ) - { - DefaultResult = defaultResult; - } - - protected virtual TResult? AggregateResult( TResult? aggregate, TResult? newResult ) - { - return newResult; - } - - protected TResult? DefaultResult { get; } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ConstantExpression.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ConstantExpression.cs deleted file mode 100644 index b9fdd76408..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ConstantExpression.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class ConstantExpression - : IExpression - { - public ConstantExpression( SourceSpan location, double value ) - { - Value = value; - Location = location; - } - - public double Value { get; } - - public SourceSpan Location { get; } - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public IEnumerable Children => Enumerable.Empty( ); - - public override string ToString( ) - { - return Value.ToString( CultureInfo.CurrentCulture ); - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNode.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNode.cs deleted file mode 100644 index 2215cf5acf..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNode.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Linq; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class ErrorNode - : IAstNode - , IExpression - { - public ErrorNode( SourceSpan location, string err ) - { - Location = location; - Error = err.ValidateNotNullOrWhiteSpace( nameof( err ) ); - } - - public SourceSpan Location { get; } - - public string Error { get; } - - public IEnumerable Children { get; } = Enumerable.Empty( ); - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public override string ToString( ) => $"<{Location}:{Error}>"; - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNodeCollector.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNodeCollector.cs deleted file mode 100644 index 409d5a21bf..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ErrorNodeCollector.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; - -namespace Kaleidoscope.Grammar.AST -{ - public class ErrorNodeCollector - : AstVisitorBase - { - public ErrorNodeCollector( ) - : base( string.Empty ) - { - } - - public override string? Visit( ErrorNode errorNode ) - { - ErrorList.Add( errorNode ); - return DefaultResult; - } - - public IReadOnlyCollection Errors => ErrorList.AsReadOnly( ); - - private readonly List ErrorList = new(); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/FunctionCallExpression.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/FunctionCallExpression.cs deleted file mode 100644 index 4ff42854eb..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/FunctionCallExpression.cs +++ /dev/null @@ -1,60 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class FunctionCallExpression - : IExpression - { - public FunctionCallExpression( SourceSpan location, Prototype functionPrototype, IEnumerable args ) - { - Location = location; - FunctionPrototype = functionPrototype; - Arguments = args.ToImmutableArray( ); - } - - public FunctionCallExpression( SourceSpan location, Prototype functionPrototype, params IExpression[ ] args ) - : this( location, functionPrototype, ( IEnumerable )args ) - { - } - - public SourceSpan Location { get; } - - public Prototype FunctionPrototype { get; } - - public IReadOnlyList Arguments { get; } - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public IEnumerable Children - { - get - { - yield return FunctionPrototype; - } - } - - public override string ToString( ) - { - if( Arguments.Count == 0 ) - { - return $"Call({FunctionPrototype})"; - } - - return $"Call({FunctionPrototype}, {string.Join( ",", Arguments.Select( a => a.ToString( ) ) )})"; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstNode.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstNode.cs deleted file mode 100644 index 06f5c26643..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstNode.cs +++ /dev/null @@ -1,62 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; - -using OpenSoftware.DgmlTools.Model; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - /// Root interface for nodes in the Abstract Syntax Tree - public interface IAstNode - { - /// Gets the source location covering the original source for the node - SourceSpan Location { get; } - - /// Gets a collection of children for the node - IEnumerable Children { get; } - - /// Visitor pattern support for implementations to dispatch the concrete node type to a visitor - /// Result type for the visitor - /// Visitor to dispatch the concrete type to - /// Result of visiting this node - TResult? Accept( IAstVisitor visitor ) - where TResult : class; - } - - /// Extensions for IAstNode - /// - /// While default interface methods seems like a great idea, it's not yet complete enough to be useful. - /// In particular there's pretty much no debugger support for evaluating such things, leaving you - /// with no way to see what they produce when used as a property. Hopefully, that will be resolved in - /// the future - but for now it is more a hindrance than it is a help. - /// - public static class AstNodeExtensions - { - /// Gets the complete collection of errors for this node and children - /// Node to traverse for errors - /// Traverses the node hierarchy to find all error node at any depth - /// Collection of errors found - public static IReadOnlyCollection CollectErrors( [ValidatedNotNull] this IAstNode node ) - { - node.ValidateNotNull( nameof( node ) ); - var collector = new ErrorNodeCollector(); - node.Accept( collector ); - return collector.Errors; - } - - public static DirectedGraph CreateGraph( [ValidatedNotNull] this IAstNode node ) - { - node.ValidateNotNull( nameof( node ) ); - - var generator = new AstGraphGenerator(); - node.Accept( generator ); - return generator.Graph; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstVisitor.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstVisitor.cs deleted file mode 100644 index 357bd7fb5e..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/IAstVisitor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -namespace Kaleidoscope.Grammar.AST -{ - public interface IAstVisitor - where TResult : class - { - TResult? Visit( RootNode root ); - - TResult? Visit( ErrorNode errorNode ); - - TResult? Visit( Prototype prototype ); - - TResult? Visit( FunctionDefinition definition ); - - TResult? Visit( ConstantExpression constant ); - - TResult? Visit( VariableReferenceExpression reference ); - - TResult? Visit( FunctionCallExpression functionCall ); - - TResult? Visit( BinaryOperatorExpression binaryOperator ); - - TResult? Visit( VarInExpression varInExpression ); - - TResult? Visit( ParameterDeclaration parameterDeclaration ); - - TResult? Visit( ConditionalExpression conditionalExpression ); - - TResult? Visit( ForInExpression forInExpression ); - - TResult? Visit( LocalVariableDeclaration localVariableDeclaration ); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ParameterDeclaration.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ParameterDeclaration.cs deleted file mode 100644 index 36b8f536a8..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/ParameterDeclaration.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Linq; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class ParameterDeclaration - : IVariableDeclaration - { - public ParameterDeclaration( SourceSpan location, string name, int index ) - { - Location = location; - Name = name; - Index = index; - } - - public SourceSpan Location { get; } - - public string Name { get; } - - public int Index { get; } - - public bool CompilerGenerated => false; - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public IEnumerable Children => Enumerable.Empty( ); - - public override string ToString( ) - { - return Name; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/RootNode.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/RootNode.cs deleted file mode 100644 index 25419dad8a..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/RootNode.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Collections.Immutable; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class RootNode - : IAstNode - { - public RootNode( SourceSpan location, IAstNode child ) - : this( location, new IAstNode[ ] { child } ) - { - } - - public RootNode( SourceSpan location, IEnumerable children ) - { - Location = location; - ChildNodes = children.ToImmutableArray( ); - } - - public SourceSpan Location { get; } - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public IEnumerable Children => ChildNodes; - - public override string ToString( ) - { - return string.Join( ' ', Children ); - } - - private readonly ImmutableArray ChildNodes; - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/SourceSpan.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/SourceSpan.cs deleted file mode 100644 index a897a94739..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/SourceSpan.cs +++ /dev/null @@ -1,61 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; - -namespace Kaleidoscope.Grammar -{ - // FUTURE: COnvert this to C# record for simplification - public struct SourceSpan - : IEquatable - { - public SourceSpan( int startLine, int startColumn, int endLine, int endColumn ) - { - StartLine = startLine; - StartColumn = startColumn; - EndLine = endLine; - EndColumn = endColumn; - } - - public int StartLine { get; } - - public int StartColumn { get; } - - public int EndLine { get; } - - public int EndColumn { get; } - - public override bool Equals( object? obj ) - { - return obj is SourceSpan span - && Equals( span ); - } - - public bool Equals( SourceSpan other ) - { - return StartLine == other.StartLine - && StartColumn == other.StartColumn - && EndLine == other.EndLine - && EndColumn == other.EndColumn; - } - - public override string ToString( ) - { - return StartLine == EndLine && StartColumn == EndColumn - ? $"({StartLine},{StartColumn})" - : $"({StartLine},{StartColumn},{EndLine},{EndColumn})"; - } - - public override int GetHashCode( ) - { - return HashCode.Combine( StartLine, StartColumn, EndLine, EndColumn ); - } - - public static bool operator ==( SourceSpan span1, SourceSpan span2 ) => span1.Equals( span2 ); - - public static bool operator !=( SourceSpan span1, SourceSpan span2 ) => !( span1 == span2 ); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/VariableReferenceExpression.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/VariableReferenceExpression.cs deleted file mode 100644 index 53318f1d34..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/AST/VariableReferenceExpression.cs +++ /dev/null @@ -1,47 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar.AST -{ - public class VariableReferenceExpression - : IExpression - { - public VariableReferenceExpression( SourceSpan location, IVariableDeclaration declaration ) - { - Location = location; - Declaration = declaration; - } - - public SourceSpan Location { get; } - - public IVariableDeclaration Declaration { get; } - - public string Name => Declaration.Name; - - public TResult? Accept( IAstVisitor visitor ) - where TResult : class - { - return visitor.ValidateNotNull( nameof( visitor ) ).Visit( this ); - } - - public IEnumerable Children - { - get - { - yield return Declaration; - } - } - - public override string ToString( ) - { - return $"Load({Name})"; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/DisposableAction.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/DisposableAction.cs deleted file mode 100644 index 01444a6387..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/DisposableAction.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; - -namespace Kaleidoscope.Grammar -{ - /// Disposable type that runs a specified action on dispose - /// - /// This is used in a C++ RAII pattern style code instead of try/finally. - /// It is most valuable when the scope extends beyond a single function - /// where a try/finally simply won't work. - /// - internal struct DisposableAction - : IDisposable - { - /// Initializes a new instance of the struct. - /// Action to run when is called. - public DisposableAction( Action onDispose ) - { - OnDispose = onDispose ?? throw new ArgumentNullException( nameof( onDispose ) ); - } - - /// Runs the action provided in the constructor ( - public void Dispose( ) - { - OnDispose( ); - } - - /// Gets a Default disposable action that does nothing - public static DisposableAction Nop => new( ( ) => { } ); - - private readonly Action OnDispose; - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/IParseErrorListener.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/IParseErrorListener.cs deleted file mode 100644 index 9b53a68e1e..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/IParseErrorListener.cs +++ /dev/null @@ -1,13 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -namespace Kaleidoscope.Grammar -{ - public interface IParseErrorListener - { - void SyntaxError( SyntaxError syntaxError ); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/ParseErrorCollector.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/ParseErrorCollector.cs deleted file mode 100644 index 72084be1ba..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/ParseErrorCollector.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System.Collections.Generic; - -using Kaleidoscope.Grammar.AST; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar -{ - public class ParseErrorCollector - : IParseErrorListener - { - public void SyntaxError( SyntaxError syntaxError ) - { - syntaxError.ValidateNotNull( nameof( syntaxError ) ); - Errors.Add( new ErrorNode( syntaxError.Location, syntaxError.ToString( ) ) ); - } - - public IReadOnlyCollection ErrorNodes => Errors.AsReadOnly( ); - - private readonly List Errors = new(); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/SyntaxError.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/SyntaxError.cs deleted file mode 100644 index 2eb3790cde..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/Error Handling/SyntaxError.cs +++ /dev/null @@ -1,51 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar -{ - public enum ParseErrorSource - { - Lexer, - Parser, - } - - public class SyntaxError - { - public SyntaxError( ParseErrorSource source, string sourceFile, int id, string symbol, SourceSpan location, string message, Exception? exception ) - { - Source = source; - SourceFile = sourceFile.ValidateNotNull( nameof( sourceFile ) ); - Id = id; - Symbol = symbol.ValidateNotNull( nameof( symbol ) ); - Location = location; - Message = message.ValidateNotNull( nameof( message ) ); - Exception = exception; - } - - public ParseErrorSource Source { get; } - - public string SourceFile { get; } - - public string Symbol { get; } - - public int Id { get; } - - public SourceSpan Location { get; } - - public string Message { get; } - - public Exception? Exception { get; } - - public override string ToString( ) - { - return $"{SourceFile}({Location}): error: {Source}{Id:D04} {Message}"; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/Kaleidoscope.Grammar.csproj b/Samples/Kaleidoscope/Kaleidoscope.Parser/Kaleidoscope.Grammar.csproj deleted file mode 100644 index e8bd927a53..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/Kaleidoscope.Grammar.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - - net5.0 - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/LocationExtensions.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/LocationExtensions.cs deleted file mode 100644 index 8f832d7b8c..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/LocationExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using Antlr4.Runtime; -using Antlr4.Runtime.Tree; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Grammar -{ - internal static class LocationExtensions - { - public static SourceSpan GetSourceSpan( [ValidatedNotNull] this ParserRuleContext ctx ) - { - ctx.ValidateNotNull( nameof( ctx ) ); - return new SourceSpan( ctx.Start.Line - , ctx.Start.Column - , ctx.Stop.Line - , ctx.Stop.Column - ); - } - - public static SourceSpan GetSourceSpan( [ValidatedNotNull]this RuleContext ctx ) - { - ctx.ValidateNotNull( nameof( ctx ) ); - if( ctx is ParserRuleContext ruleCtx ) - { - GetSourceSpan( ruleCtx ); - } - - return default; - } - - public static SourceSpan GetSourceSpan( [ValidatedNotNull] this ITerminalNode node ) - { - node.ValidateNotNull( nameof( node ) ); - return GetSourceSpan( node.Symbol ); - } - - public static SourceSpan GetSourceSpan( [ValidatedNotNull] this IToken token ) - { - token.ValidateNotNull( nameof( token ) ); - return new SourceSpan( token.Line, token.Column, token.Line, token.Column + token.Text.Length ); - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Parser/ScopeStack.cs b/Samples/Kaleidoscope/Kaleidoscope.Parser/ScopeStack.cs deleted file mode 100644 index b7e1c55faf..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Parser/ScopeStack.cs +++ /dev/null @@ -1,84 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Kaleidoscope.Grammar -{ - /// Simple implementation of common Variable scoping - /// Type of the values to associate with the symbol name - /// - /// In essence, this is a stack of Dictionaries that is intended for use in code generation. - /// Most languages have some sort of notion of symbol scopes and name lookups. This implements - /// the common case of nested scopes where a new 'local scope' may override some of the symbols - /// in a parent scope. Any values in any parent not overridden by the child are visible to the - /// child scope. - /// - internal class ScopeStack - { - /// Initializes a new instance of the class. - /// - /// The stack is initialized with a "global" scope ready for use without additional - /// initialization. Subsequent scopes for the stack are generated by calling the - /// method. - /// - public ScopeStack( ) - { - Scopes.Push( new Dictionary( ) ); - } - - /// Starts a new scope - /// to enable automatic restore of the scope in RAII style patterns - /// - /// A new scope is pushed on the stack and remains active until the method - /// of the return is called. Normally, code generation does this with a language provided means of ensuring the - /// Dispose method is called even when an exception occurs. (i.e. C# 'using' or try/finally) - /// - public IDisposable EnterScope( ) - { - Scopes.Push( new Dictionary( ) ); - return new DisposableAction( ( ) => Scopes.Pop( ) ); - } - - /// Gets or sets the value of a symbol in scope - /// name of the symbol - /// Value for the symbol - /// - /// Getting a symbol value searches all scopes, starting with the current scope. - /// Setting a symbol value will only set the value in the current scope. (e.g. - /// if the value does not exist in the current scope a new entry is made for it - /// in the current scope, even if some outer scope has the same name. - /// - /// If the wasn't found in the active or parent scopes - public T this[ string name ] - { - get => TryGetValue( name, out T? retVal ) ? retVal : throw new KeyNotFoundException( name ); - set => Scopes.Peek( )[ name ] = value; - } - - /// Attempts to retrieve a value from the stack - /// Name of the value - /// Value - /// if the symbol was found from a search of the scopes - public bool TryGetValue( string name, [MaybeNullWhen( false )] out T value ) - { - foreach( var scope in Scopes ) - { - if( scope.TryGetValue( name, out value ) ) - { - return true; - } - } - - value = default!; - return false; - } - - private readonly Stack> Scopes = new(); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/CodeGeneratorException.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/CodeGeneratorException.cs deleted file mode 100644 index 7bf8d85337..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/CodeGeneratorException.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Runtime.Serialization; - -namespace Kaleidoscope.Runtime -{ - [Serializable] - public class CodeGeneratorException - : Exception - { - public CodeGeneratorException( ) - { - } - - public CodeGeneratorException( string message ) - : base( message ) - { - } - - public CodeGeneratorException( string message, Exception inner ) - : base( message, inner ) - { - } - - protected CodeGeneratorException( SerializationInfo info, StreamingContext context ) - : base( info, context ) - { - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/ColoredConsoleParseErrorLogger.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/ColoredConsoleParseErrorLogger.cs deleted file mode 100644 index 7ee0e63e3b..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/ColoredConsoleParseErrorLogger.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; - -using Kaleidoscope.Grammar.AST; - -using Ubiquity.ArgValidators; - -namespace Kaleidoscope.Runtime -{ - public class ColoredConsoleParseErrorLogger - : IParseErrorLogger - { - public void ShowError( ErrorNode node ) - { - node.ValidateNotNull( nameof( node ) ); - ShowError( node.ToString( ) ); - } - - public void ShowError( string msg ) - { - if( !string.IsNullOrWhiteSpace( msg ) ) - { - var color = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine( msg ); - Console.ForegroundColor = color; - } - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/IKaleidoscopeCodeGenerator.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/IKaleidoscopeCodeGenerator.cs deleted file mode 100644 index 688625bfc5..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/IKaleidoscopeCodeGenerator.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using Kaleidoscope.Grammar.AST; - -namespace Kaleidoscope.Runtime -{ - /// Interface for a Kaleidoscope code generator - /// Result type of the generation - /// - /// For eager JIT and AOT compilation is normally - /// . Though any type is viable. - /// - public interface IKaleidoscopeCodeGenerator - : IDisposable - { - /// Generates output from the tree - /// Tree to generate - /// Generated result - /// - /// The behavior of this method depends on the implementation. The common case is to - /// actually generate an LLVM module for the JIT engine. Normally, any anonymous expressions - /// are JIT compiled and executed. The result of executing the expression is returned. - /// For Function definitions or declarations, the is returned. - /// However, that's not required. In a simple syntax analyzer, the generate may do nothing - /// more than generate diagrams or other diagnostics from the input tree. - /// For a lazy compilation JIT the generator will defer the actual generation of code and instead - /// will create stubs for each function definition. When those functions are called, the stubs trigger a - /// callback to the application that will then generate the code for the function "on the fly". In this case, - /// only a top level expression is immediately generated/executed to produce a value. - /// - OptionalValue Generate( IAstNode ast ); - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/IParseErrorLogger.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/IParseErrorLogger.cs deleted file mode 100644 index 7662d73272..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/IParseErrorLogger.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using Kaleidoscope.Grammar.AST; - -namespace Kaleidoscope.Runtime -{ - public interface IParseErrorLogger - { - void ShowError( ErrorNode node ); - - void ShowError( string msg ); - - bool CheckAndShowParseErrors( IAstNode node ) - { - var errors = node.CollectErrors( ); - if( errors.Count == 0 ) - { - return false; - } - - foreach( var err in errors ) - { - ShowError( err ); - } - - return true; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope-Runtime.md b/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope-Runtime.md deleted file mode 100644 index 7d3f12fafc..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope-Runtime.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -uid: Kaleidoscope-Runtime ---- - -# Kaleidoscope.Runtime Library -The Kaleidoscope.Runtime Library provides a set of common support libraries to aid in keeping the -tutorial chapter code focused on the code generation and JIT support in Ubiquity.NET.Llvm rather then the -particulars of parsing or the Kaleidoscope language in general. - -## REPL Loop infrastructure -The Kaleidoscope.Runtime library contains the basic infrastructure for the classic Read, Evaluate, -Print, Loop (REPL) common for interactive/interpreted/JIT language runtimes. - -### TextReaderExtensions class -The TextReaderExtensions class provides a means to read lines from a TextReader as an `IEnumerable`. -Additionally it supports reading statements from an `IEnumerable` to `IEnumerable<(string Txt, bool IsPartial)>` -the `Txt` member of the tuple is the text from a line of input and the bool `IsPartial` indicates if, the line is -terminated by a semicolon. The extension correctly handles lines with additional text following a semicolon by -splitting the input into multiple lines. Everything up to, and including, the semicolon is yielded with IsPatial -set to false. Then the rest of the line up to the next new line is yielded with IsPartial set to true. (The -implementation supports an arbitrary number of semicolons on the same line and yields complete statements for -each one) - -## Parser -Kaleidoscope for Ubiquity.NET.Llvm leverages ANTLR4 for parsing the language into a parse tree. Since ANTRL is a -generalized parsing infrastructure it has a lot of flexibility depending on the needs of a language and -application. While that flexibility is generally a value, it does come with added complexity. The ParserStack -class encapsulates the complexity as needed for the Kaleidoscope language code generation to minimize -the repetition of boiler-plate code, and in particular the correct set of listeners for errors and user -operators. This stack supports two distinct modes of parsing, interactive REPL, and FULL source as used -for ahead of time compilation. - -## Kaleidoscope JIT engine -The JIT engine used for Kaleidoscope is based on the Ubiquity.NET.Llvm OrcJit, which, unsurprisingly, uses the LLVM -OrcJit functionality to provide On Request Compilation (ORC). For most of the chapters, the JIT uses a -moderately lazy compilation technique where the source language is parsed, converted to LLVM IR and submitted -to the JIT engine. The JIT engine does not immediately generate native code from the module, however. Instead -it stores the module, and whenever compiled code calls to a symbol exported by the IR module, it will then -generate the native code for the function "on the fly". This has the advantage of not paying the price of -converting IR to native code if it is never actually used, though it does have the cost of converting the -source language to IR, even if the code will never execute. - -### Really lazy compilation -While the basic lazy compilation of IR to native code has performance benefits over a pure interpreter, it -still has the potential for wasted overhead converting the parsed language to LLVM IR. Fortunately, the LLVM -and Ubiquity.NET.Llvm OrcJit support truly lazy compilation. This is done by asking the JIT to create a stub for -a named symbol and then, whenever code calls that symbol the stub calls back to the JIT which then calls back -the application to generate the IR, add the module to the JIT and trigger compilation to native. Thus, achieving -true Just-In-Time compilation. - ->[!CAUTION] -> The truly lazy functionality is currently disabled due to a bug in the underlying LLVM OrcJIT -> implementation on Windows. diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope.Runtime.csproj b/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope.Runtime.csproj deleted file mode 100644 index 0f6097f367..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope.Runtime.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net5.0 - - - true - - - true - - - - - - diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/KaleidoscopeJIT.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/KaleidoscopeJIT.cs deleted file mode 100644 index 28268d941f..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/KaleidoscopeJIT.cs +++ /dev/null @@ -1,115 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Runtime.InteropServices; - -using Ubiquity.NET.Llvm; -using Ubiquity.NET.Llvm.JIT; - -namespace Kaleidoscope.Runtime -{ - /// JIT engine for Kaleidoscope language - /// - /// This engine uses the engine to support lazy - /// compilation of LLVM IR modules added to the JIT. - /// - public sealed class KaleidoscopeJIT - : OrcJit - { - /// Initializes a new instance of the class. - public KaleidoscopeJIT( ) - : base( BuildTargetMachine( ) ) - { - AddInteropCallback( "putchard", new CallbackHandler1( PutChard ) ); - AddInteropCallback( "printd", new CallbackHandler1( Printd ) ); - } - - /// Gets or sets the output writer for output from the program. - /// The default writer is . - public TextWriter OutputWriter { get; set; } = Console.Out; - - /// Delegate for an interop callback taking no parameters - /// value for the function - [UnmanagedFunctionPointer( CallingConvention.Cdecl )] - public delegate double CallbackHandler0( ); - - /// Delegate for an interop callback taking one parameters - /// First parameter - /// value for the function - [UnmanagedFunctionPointer( CallingConvention.Cdecl )] - public delegate double CallbackHandler1( double arg1 ); - - /// Delegate for an interop callback taking two parameters - /// First parameter - /// Second parameter - /// value for the function - [UnmanagedFunctionPointer( CallingConvention.Cdecl )] - public delegate double CallbackHandler2( double arg1, double arg2 ); - - /// Delegate for an interop callback taking three parameters - /// First parameter - /// Second parameter - /// Third parameter - /// value for the function - [UnmanagedFunctionPointer( CallingConvention.Cdecl )] - public delegate double CallbackHandler3( double arg1, double arg2, double arg3 ); - - /// Delegate for an interop callback taking four parameters - /// First parameter - /// Second parameter - /// Third parameter - /// Fourth parameter - /// value for the function - [UnmanagedFunctionPointer( CallingConvention.Cdecl )] - public delegate double CallbackHandler4( double arg1, double arg2, double arg3, double arg4 ); - - [SuppressMessage( "Design", "CA1031:Do not catch general exception types", Justification = "Native callback *MUST NOT* surface managed exceptions" )] - private double Printd( double x ) - { - // STOP ALL EXCEPTIONS from bubbling out to JIT'ed code - try - { - OutputWriter.WriteLine( x ); - return 0.0F; - } - catch - { - return 0.0; - } - } - - [SuppressMessage( "Design", "CA1031:Do not catch general exception types", Justification = "Native callback *MUST NOT* surface managed exceptions" )] - private double PutChard( double x ) - { - // STOP ALL EXCEPTIONS from bubbling out to JIT'ed code - try - { - OutputWriter.Write( ( char )x ); - return 0.0F; - } - catch - { - return 0.0; - } - } - - private static TargetMachine BuildTargetMachine( ) - { - string hostTriple = Triple.HostTriple.ToString( ); - return Target.FromTriple( hostTriple ) - .CreateTargetMachine( hostTriple - , /*cpu*/null - , /*features*/null - , CodeGenOpt.Default - , RelocationMode.Default - , CodeModel.JitDefault - ); - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/OptionalValue.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/OptionalValue.cs deleted file mode 100644 index 29a96fe2d3..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/OptionalValue.cs +++ /dev/null @@ -1,88 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Kaleidoscope.Runtime -{ - // FUTURE: Convert this to a C# record for simplification - - /// Simple Value type that provides for null safe optional generic operations - /// Type of value - /// - /// This is used in place of trying to add nullability attributes to - /// as that's not allowed. The default constructor is used for cases when there is no value. - /// - public struct OptionalValue - : IEquatable> - { - /// Initializes a new instance of the struct with a valid value. - /// Value to store. For reference types this may not be - /// is . - /// - /// This constructor is used to initialize a new instance and validate that, for reference types, the provided - /// value is not . This is used to provide the guarantee that, for reference types, - /// is not when is . - /// And, conversely, that is whenever is - /// . - /// - public OptionalValue( T value ) - { - HasValue = true; - - // caller indicates a value, but didn't provide one => error - if( value is null ) - { - throw new ArgumentNullException( nameof( value ) ); - } - - Value = value!; - } - - /// Gets a value indicating whether is valid and, for reference types, is not - public bool HasValue { get; } - - /// Gets the value, which, for reference types, may be null if, and only, if is - [MaybeNull] - public T Value { get; } - - public override bool Equals( object? obj ) - { - return obj is OptionalValue other && Equals( other ); - } - - public override int GetHashCode( ) - { - return HasValue ? Value!.GetHashCode( ) : 0; - } - - public static bool operator ==( OptionalValue left, OptionalValue right ) => left.Equals( right ); - - public static bool operator !=( OptionalValue left, OptionalValue right ) => !( left == right ); - - public bool Equals( OptionalValue other ) - { - return HasValue - && other.HasValue - && Value!.Equals( other.Value ); - } - - public void Deconstruct( out bool hasValue, [MaybeNull] out T value) - { - hasValue = HasValue; - value = Value; - } - } - - public static class OptionalValue - { - public static OptionalValue Create( T value ) - { - return new OptionalValue( value ); - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/ReadEvaluatePrintLoopBase.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/ReadEvaluatePrintLoopBase.cs deleted file mode 100644 index 4d0897e307..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/ReadEvaluatePrintLoopBase.cs +++ /dev/null @@ -1,75 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.IO; -using System.Linq; - -using Kaleidoscope.Grammar; -using Kaleidoscope.Grammar.AST; - -namespace Kaleidoscope.Runtime -{ - public abstract class ReadEvaluatePrintLoopBase - { - public LanguageLevel LanguageFeatureLevel { get; } - - public virtual void ShowPrompt( ReadyState state ) - { - Console.Write( state == ReadyState.StartExpression ? "Ready>" : ">" ); - } - - public abstract IKaleidoscopeCodeGenerator CreateGenerator( DynamicRuntimeState state ); - - public IParseErrorLogger ErrorLogger { get; } - - public abstract void ShowResults( T resultValue ); - - public void Run( TextReader input, DiagnosticRepresentations diagnostics = DiagnosticRepresentations.None ) - { - var parser = new Parser( LanguageFeatureLevel, diagnostics ); - using var generator = CreateGenerator( parser.GlobalState ); - - ShowPrompt( ReadyState.StartExpression ); - - // Create sequence of parsed AST RootNodes to feed the REPL loop - var replSeq = from stmt in input.ToStatements( ShowPrompt ) - let node = parser.Parse( stmt ) - where !ErrorLogger.CheckAndShowParseErrors( node ) - select node; - - foreach( IAstNode node in replSeq ) - { - try - { - var result = generator.Generate( node ); - if( result.HasValue ) - { - ShowResults( result.Value! ); - } - } - catch( CodeGeneratorException ex ) - { - // This is an internal error that is not recoverable. - // Show the error and stop additional processing - ErrorLogger.ShowError( ex.ToString( ) ); - break; - } - } - } - - protected ReadEvaluatePrintLoopBase( LanguageLevel level ) - : this( level, new ColoredConsoleParseErrorLogger() ) - { - } - - protected ReadEvaluatePrintLoopBase( LanguageLevel level, IParseErrorLogger logger ) - { - LanguageFeatureLevel = level; - ErrorLogger = logger; - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Utilities.cs b/Samples/Kaleidoscope/Kaleidoscope.Runtime/Utilities.cs deleted file mode 100644 index 27195b7b56..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Runtime/Utilities.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) Ubiquity.NET Contributors. All rights reserved. -// -// ----------------------------------------------------------------------- - -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Text; - -using Ubiquity.ArgValidators; - -[assembly: SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1652:Enable XML documentation output", Justification = "Sample application" )] - -namespace Kaleidoscope.Runtime -{ - public static class Utilities - { - /// replaces any characters in a name that are invalid for a file name - /// name to convert - /// name with invalid characters replaced with '_' - public static string GetSafeFileName( string name ) - { - name.ValidateNotNullOrWhiteSpace( nameof( name ) ); - var bldr = new StringBuilder( name.Length ); - char[ ] invalidChars = Path.GetInvalidFileNameChars( ); - foreach( char c in name ) - { - bldr.Append( invalidChars.Contains( c ) ? '_' : c ); - } - - return bldr.ToString( ); - } - } -} diff --git a/Samples/Kaleidoscope/Kaleidoscope.Tests/SimpleExpressions.kls b/Samples/Kaleidoscope/Kaleidoscope.Tests/SimpleExpressions.kls deleted file mode 100644 index 68660afcb4..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Tests/SimpleExpressions.kls +++ /dev/null @@ -1,13 +0,0 @@ -# simple top level expression -4+5; - -# function definitions -def foo(a b) a*a + 2*a*b + b*b; - -def bar(a) foo(a, 4.0) + bar(31337); - -# external declaration -extern cos(x); - -# calling external function -cos(1.234); diff --git a/Samples/Kaleidoscope/Kaleidoscope.Tests/fibi.kls b/Samples/Kaleidoscope/Kaleidoscope.Tests/fibi.kls deleted file mode 100644 index 705e0ef1a8..0000000000 --- a/Samples/Kaleidoscope/Kaleidoscope.Tests/fibi.kls +++ /dev/null @@ -1,23 +0,0 @@ -# Define ':' for sequencing: as a low-precedence operator that ignores operands -# and just returns the RHS. (NOTE: assignment is at precedence 2 so this is even -# lower precedence than assignment) -def binary: 1 (x y) y; - -# Recursive fib, we could do this as of Chapter 5. -def fib(x) - if (x < 3) then - 1 - else - fib(x-1)+fib(x-2); - -# Iterative form of fib - new possibility for Chapter 7 -def fibi(x) - var a = 1, b = 1, c in - (for i = 3, i < x in - c = a + b : - a = b : - b = c) : - b; - -# Call it. -fibi(10); diff --git a/Ubiquity.NET.ruleset b/Ubiquity.NET.ruleset deleted file mode 100644 index d7055c5bd9..0000000000 --- a/Ubiquity.NET.ruleset +++ /dev/null @@ -1,743 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docfx/current/api/.gitignore b/docfx/.gitignore similarity index 52% rename from docfx/current/api/.gitignore rename to docfx/.gitignore index e8079a3bef..c0ea994751 100644 --- a/docfx/current/api/.gitignore +++ b/docfx/.gitignore @@ -1,5 +1,6 @@ ############### # temp file # ############### -*.yml -.manifest +**/api/**.yml +**/api/.manifest +!**-xref.yml diff --git a/docfx/ReadMe.md b/docfx/ReadMe.md new file mode 100644 index 0000000000..c4b0c0beea --- /dev/null +++ b/docfx/ReadMe.md @@ -0,0 +1,75 @@ +# Documentation +>NOTE: +> This file itself does NOT particpate in the generated docs output. It documents the input +> to the doc generation and the process for maintainers of this library (Who clearly don't +> have great memories or are otherwise easily confused. :nerd_face:) + +DocFX is used to generate the documentation for this library. There is confusion on +what the statictoc template means and requires. It is ***LITERALLY*** that the Table +of Contents (TOC) is staticly generated. So that the entire site is servable from a +file path. This ***DOES NOT*** mean that the default+modern template is unusable for +hosted static site scenarios like 'gh-pages' in GitHub. It only means that the TOC +support will ***require*** a hosted site to provide the contents needed by the generated +TOC client side scripting. That's it. Don't fear the built-in templates (Despite the lack +of decent docs explaining the details [Yeah, this project previously fell into those gaps +and even constructed a custom template to deal with it... Sigh, what a waste of time... +:facepalm: ]) + +DocFX has obsoleted the `docfxconsole` NuGet pacakge that was used to run docfx for +a project via MSBUILD. Instead it focused on a .NET tool to do it all via the +command line. Ultimately the docfx.json serves as the "project" file for the +different site builds. The PowerShell script `Build-Docs.ps1` was updated to use the +new tool directly. Using that script should have little or no impact on the overall +flow. + +## Files used by the docs generation +There are a lot of files used to generate the docs and the concept of a Table of Contents +(TOC) gets confusing fast when using docfx. So this tries to explain them all. + +### .gitignore +This marks the files that are generated as ignored for GIT operations (Don't include +generated sources in the repo - the automated build will generate them) Some spcific +files are excluded from this but most of the api-* folders are ignored. + +### docfx.json +This file serves as the "project" file for docs generation. Sadly, docfx has deprecated +old docfxconsole that supported creation of a csproj project file to generate the docs +from. So this file serves as the only equivalent of a project file. Unfortunately it is +in JSON format and unlike any other project is unusable directly in an IDE as they don't +understand the format of such a thing. + +### favicon.ico +This provides the standard web brower icon for the site as a whole. + +### index.md +Markdown for the index (Home) of this site. + +### toc.yml +YAML file containing the links for the Table of contents for the SITE as a whole (across +all child pages/folders). This is for the TOP row navigation on the site. (It has NOTHING +to do with the left navigation for any given sub folder) + +### Folders +There are a few folders containing input for the site generation. + +#### api-* +These folders contain the Generated contents for each project as (YAML) metadata files +parsed and generated from the source. + +##### api-*/Index.md +This contains the main landing page for a given library it has the top bar navigation +from [toc.yml](#tocyml) AND the left navigation from the generated +[toc.yml](#generated-left-nav-tocyml) for this API library. + +#### Generated left nav TOC.YML +This version of the overloaded toc.yml file name is used to produce the left navigation +for an API surface. This is generated from the source files and normally contains the +set of namespaces, types, enums etc... Each library project generates it's own version +of this file. Since this is generated it is listed in the [.gitignore](#gitignore) file. + +#### Library Content +These folders (named after the `*` portion of the [api-*](#api-*) folder names contains +manually written additional files, articles, samples etc... related to a given library. +>NOTE +> The TOC.YML file format used in these topics is DIFFERENT from what is auto generated. +> diff --git a/docfx/Ubiquity.NET.Llvm.Docfx.sln b/docfx/Ubiquity.NET.Llvm.Docfx.sln deleted file mode 100644 index d6721d4696..0000000000 --- a/docfx/Ubiquity.NET.Llvm.Docfx.sln +++ /dev/null @@ -1,67 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ubiquity.NET.Llvm.Docfx.API", "current\Ubiquity.NET.Llvm.Docfx.API.csproj", "{E0C93719-295E-4A6B-B9FD-63A18FC4E447}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ubiquity.NET.Llvm.Docfx.Index", "index\Ubiquity.NET.Llvm.Docfx.Index.csproj", "{D6505E72-CAEA-476B-B7E2-68DA1CDB9243}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{F1EECBFA-02CE-4B14-A370-FFFA080250C6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ubiquity", "Ubiquity", "{9FAA1D8A-F467-472E-9AF2-6BDB22F5D475}" - ProjectSection(SolutionItems) = preProject - templates\Ubiquity\readme.md = templates\Ubiquity\readme.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "partials", "partials", "{353D26CA-B7C2-4037-9070-2CE87DEFD86F}" - ProjectSection(SolutionItems) = preProject - templates\Ubiquity\partials\affix.tmpl.partial = templates\Ubiquity\partials\affix.tmpl.partial - templates\Ubiquity\partials\class.header.tmpl.partial = templates\Ubiquity\partials\class.header.tmpl.partial - templates\Ubiquity\partials\class.tmpl.partial = templates\Ubiquity\partials\class.tmpl.partial - templates\Ubiquity\partials\collection.tmpl.partial = templates\Ubiquity\partials\collection.tmpl.partial - templates\Ubiquity\partials\enum.tmpl.partial = templates\Ubiquity\partials\enum.tmpl.partial - templates\Ubiquity\partials\footer.tmpl.partial = templates\Ubiquity\partials\footer.tmpl.partial - templates\Ubiquity\partials\namespace.tmpl.partial = templates\Ubiquity\partials\namespace.tmpl.partial - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "styles", "styles", "{D3F068B8-4A6C-46C4-8F1D-82AA544CB7E4}" - ProjectSection(SolutionItems) = preProject - templates\Ubiquity\styles\main.css = templates\Ubiquity\styles\main.css - templates\Ubiquity\styles\main.js = templates\Ubiquity\styles\main.js - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{80950FE7-B0E4-419E-87CC-1EE58C76421B}" - ProjectSection(SolutionItems) = preProject - DragonSharp.png = DragonSharp.png - DragonSharp48x48.png = DragonSharp48x48.png - favicon.ico = favicon.ico - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E0C93719-295E-4A6B-B9FD-63A18FC4E447}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E0C93719-295E-4A6B-B9FD-63A18FC4E447}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E0C93719-295E-4A6B-B9FD-63A18FC4E447}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E0C93719-295E-4A6B-B9FD-63A18FC4E447}.Release|Any CPU.Build.0 = Release|Any CPU - {D6505E72-CAEA-476B-B7E2-68DA1CDB9243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D6505E72-CAEA-476B-B7E2-68DA1CDB9243}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D6505E72-CAEA-476B-B7E2-68DA1CDB9243}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D6505E72-CAEA-476B-B7E2-68DA1CDB9243}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9FAA1D8A-F467-472E-9AF2-6BDB22F5D475} = {F1EECBFA-02CE-4B14-A370-FFFA080250C6} - {353D26CA-B7C2-4037-9070-2CE87DEFD86F} = {9FAA1D8A-F467-472E-9AF2-6BDB22F5D475} - {D3F068B8-4A6C-46C4-8F1D-82AA544CB7E4} = {9FAA1D8A-F467-472E-9AF2-6BDB22F5D475} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4E9321AC-4ABF-4522-89CE-CD9A98005C92} - EndGlobalSection -EndGlobal diff --git a/docfx/antlr-utils/api/index.md b/docfx/antlr-utils/api/index.md new file mode 100644 index 0000000000..0c6621115d --- /dev/null +++ b/docfx/antlr-utils/api/index.md @@ -0,0 +1,2 @@ +# Ubiquity.NET.ANTLR.Utils +API folder index.md... diff --git a/docfx/antlr-utils/index.md b/docfx/antlr-utils/index.md new file mode 100644 index 0000000000..3033551023 --- /dev/null +++ b/docfx/antlr-utils/index.md @@ -0,0 +1,24 @@ +# About +This library provides general extensions to ANTLR including adapter bindings +for the Ubiquity.NET.Runtime library. + +# Key usage +* Get a SourceLocation from various ANTLR types (rule,tokens,terminals) + - This provides an adaptation to the abstract SourceLocation +* Debug trace listener + - Provides debug TRACE support for any parser by listening for every rule and using + Debug.Trace() to generate a string representation of that rule. This is VERY useful + when developing or debugging a grammar. +* Adapter for parse error listeners to a unified and abstract + `Ubiquity.NET.Runtime.IParseErrorListener`. + - This allows building consumers that deal with errors and remain indepent of the parsing + technology. +* Extension functions that provides commonly used support for ANTLR + - Get a character interval from a ParserRuleContext with support for the standard EOF + rule. + - Get the source stream from an IRecognizer + - Get the source text from a rule context and recognizer that produced it. + - Get source text from a rule context and stream that it was parsed from. + - Get a unique ID for a parse tree + * Useful for building graphs of the result of parsing as many graphing representations + require a unique node id for every node in the graph. diff --git a/docfx/antlr-utils/toc.yml b/docfx/antlr-utils/toc.yml new file mode 100644 index 0000000000..21725efca2 --- /dev/null +++ b/docfx/antlr-utils/toc.yml @@ -0,0 +1,5 @@ +# TOC (Left nav) for LLVM folder +- name: Overview + href: index.md +- name: Namespaces + href: api/toc.yml diff --git a/docfx/current/Ubiquity.NET.Llvm.Docfx.API.csproj b/docfx/current/Ubiquity.NET.Llvm.Docfx.API.csproj deleted file mode 100644 index b328b0fda7..0000000000 --- a/docfx/current/Ubiquity.NET.Llvm.Docfx.API.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - net5.0 - - - - - - - - - - - - - - - - - - - - - - - Warning - $(IntermediateOutputPath)DocFx-Metadata.log - $(DocParameters) --globalMetadata="{_buildVersion:\"$(FullBuildNumber)\"}" - $(DocParameters) --intermediateFolder="$(IntermediateOutputPath.TrimEnd('\'))" - $(DocParameters) --xref="@(DocFxCrossRefMap->'%(FullPath)',',')" - @(DocFxTemplate, ',') - - - diff --git a/docfx/current/api/index.md b/docfx/current/api/index.md deleted file mode 100644 index bb78d3d77a..0000000000 --- a/docfx/current/api/index.md +++ /dev/null @@ -1,27 +0,0 @@ -# Namespaces -This library is divided into several namespaces based on general use and purpose - -#### [Ubiquity.NET.Llvm](xref:Ubiquity.NET.Llvm) -This is the top level namespace containing the basic classes and types for initializing and -shutting down the underlying LLVM library. - -#### [Ubiquity.NET.Llvm.DebugInfo](xref:Ubiquity.NET.Llvm.DebugInfo) -This namespace contains the Debug metadata support for defining debug source information in generated code. - -#### [Ubiquity.NET.Llvm.Instructions](xref:Ubiquity.NET.Llvm.Instructions) -This namespace contains the instruction classes and instruction builder for the LLVM IR instructions - -#### [Ubiquity.NET.Llvm.JIT](xref:Ubiquity.NET.Llvm.JIT) -This namespace contains the support for the LLVM Just-In-Time compilation engine. - -#### [Ubiquity.NET.Llvm.Transforms](xref:Ubiquity.NET.Llvm.Transforms) -This namespace contains the support for the pass manager and various code transformation passes. - -#### [Ubiquity.NET.Llvm.Types](xref:Ubiquity.NET.Llvm.Types) -This namespace contains the support for defining and querying LLVM types - -#### [Ubiquity.NET.Llvm.Values](xref:Ubiquity.NET.Llvm.Values) -This namespace contains support for manipulating LLVM Values and the hierarchy of value object types - -#### Ubiquity.NET.Llvm.Interop -This namespace contains the low level interop layer for the native LLVM C API surface diff --git a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Configuration.md b/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Configuration.md deleted file mode 100644 index 09a33b7395..0000000000 --- a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Configuration.md +++ /dev/null @@ -1,82 +0,0 @@ -# LlvmBindings Configuration -The bindings generator uses a configuration data structure that consists of a -number of tables used by the code generation passes for resolving the various -ambiguities of generating the code. The configuration is an instance of the -`LlvmBindingsGenerator.Configuration.GeneratorConfig` class. - -The GeneratorConfig class holds all the data used by the various passes in -LlvmBindingsGenerator to transform the AST so that subsequent stages will -generate correct .NET interop code. There are several tables of information -used in the various passes. (See: [YAML Configuration format](LlvmBindingsGenerator-YAML-Config-format.md) -For details of the configuration file YAML format) - -## StatusReturningFunctions -This table is a simple [SortedSet](xref:System.Collections.Generic.SortedSet`1) of -strings. Each entry is the name of an LLVM function that semantically returns a status. -The return type is transformed to an LLVMStatus to differentiate such types from a -boolean or other integral values (in the case of a C Declaration that simply returns an -int as a status value) This helps to keep the interop layer clean, and semantically self -consistent, without actually breaking the ABI of the LLVM-C APIs. - -## MarshalingInfo -This is a collection of `LlvmBindingsGenerator.IMarshalInfo` instances -used to transform function parameter and return types as well as add appropriate marshaling -attributes for them. This is the most complex implementations of all the passes. Though -declaring the table is made a lot easier by the various IMarshalInfo implementation types. -(see: [YAML Configuration format](LlvmBindingsGenerator-YAML-Config-format.md) for details) - -## DeprecatedFunctionToMessageMap -This property consists of a dictionary that maps the name of a deprecated LLVM-C API to -a message explaining what the alternative is, if there is one. This is used to create the -declaration with the Obsolete attribute in .NET. ->[!NOTE] ->At present this table is only used to completely filter out the deprecated functions so they ->do not even appear in the generated output or EXPORTS. - -## InternalFunctions -This property contains a dictionary of functions that are considered internal or just completely -ignored for the interop layer. Generally these are the disposal methods that are used in the -generated handle types or custom string marshalers. Sometimes they are for functions declared in -the headers but not implemented in the library. (the LTO APIs for instance are not included as they -are intended for use in an actual linker) The values in the dictionary are a boolean that indicates -if the function is just completely ignored (true) or used internally only (false). The -MarkFunctionsInternalPass uses this information to mark the functions appropriately for subsequent -passes. - -## HandleToTemplateMap -The LlvmBindings generator supports creating safe handles for all of the LLVM object handle types. -Since there are a few distinct patterns for handles the HandleToTemplate map is used to map between -the LLVM-C typedef handle name and the actual template that will generate the code for that -particular handle. The HandleToTemplateMap property is a keyed collection of IHandleCodeTemplate -that is keyed by the HandleName. There are, at present two general template types: - -### GlobalHandleTemplate -This is used for handles that have a global application defined lifetime and therefore have a -distinct disposal method. The generated handle types derive from the .NET SafeHandle types for -marshaling. The following is a summary of the handle types (for more details see -[Handle Marshaling](llvm-handles.md)) - -#### Alias handles -On occasion there are places where the LLVM-C APIs retrieve a handle to an object that is just -an alias (e.g. not ultimately owned by the caller after the call completes). These are usually, -but not always, child objects returning a reference to the parent. In such cases the application -should never dispose the handle as it doesn't own it. - -### ContextHandleTemplate -Contextual handles are those that are always references to objects owned by a parent container -of some sort. Most objects in LLVM are owned by an LLVM Context or module and only disposed of -when the parent container itself is disposed. - -## AnonymousEnumNames -The AnonymousEnumNames is a dictionary that maps the name of the first enumerated value of an -anonymous enum with the typedef type that is used to represent it. This table is used to -effectively de-anonymize the enum so that a .NET enum with the correct underlying type is -generated. - -## IgnoredHeaders -This contains a list of header files that the IgnoreSystemHeadersPass uses to mark some of the -LLVM headers as ignored. Such headers will not produce any generated output for .NET. - -## AliasReturningFunctions -This property contains a list of function names that return a handle, that is, in reality, an -alias that the caller **MUST NOT** destroy/dispose. diff --git a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Overview.md b/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Overview.md deleted file mode 100644 index 67490db230..0000000000 --- a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-Overview.md +++ /dev/null @@ -1,59 +0,0 @@ -# LlvmBindingsGenerator -LlvmBindingsGenerator is a tool used in building the Ubiquity.NET.Llvm.Interop library. As the -name implies, this tool generates the base level bindings between the native LibLlvm -library and managed code (e.g. It generates all the P/Invoke signatures, structure and -enumeration definitions and declarations). - -## Auto generation -The generator uses the [CppSharp library](https://github.com/mono/CppSharp) to parse -LLVM headers and the LibLLVM extended headers to get all the required declarations into -an Abstract Syntax Tree (AST). The generator then uses the AST to generate the required -.NET interop code. - -## Challenges of interop for LLVM-C -Auto generation of the interop layer saves time and errors by moving tedious and error prone -hand coding into a tool that is run as part of the regular automated builds. However, the -job of auto generating for LLVM is not without some challenges. The original support for the -interop in Ubiquity.NET.Llvm was done using a different tool that generated code, which then needed -significant hand editing thereby defeating much of the value of the automated generation. In -the process several challenges for the LLVM library were defined: - - 1. There are several different ways that strings are provided as either out parameters or -return value. - 1. As a raw const char*, this basically is just an alias that the managed code must then copy - into a managed System.String. - 2. As an allocated buffer that the managed application will need to release through some - sort of Dispose type API. - 1. There are actually several such dispose APIs so the caller has to know which one to use - 1. LLVM-C uses a typedef LLVMBool as a return value for many functions, however the semantics - of the return value depend on the function used. In some cases it is literally a boolean - success(non-zero)/failure(zero) indication. While other functions use it as a status where - success is zero and failure is any non zero value. This causes a great deal of confusion, - especially if LLVMBool is blindly transformed to System.Boolean. - 1. LLVM-C uses typedefs for opaque pointers for objects in the underlying C++ implementation. - Unfortunately, not all of the typedefs are consistent in how the are declared. Most are - declared with the pointer as part of the typedef, but some are not. This makes automated - generation more complex. - 1. Input and output array handling in the LLVM-C API is inconsistent. - 1. There are cases where the length required is retrieved from a function and other cases - where it is an out parameter. - 1. Some flags type enumerations use a typedef to an unsigned value, since C limits the range - of an enumerated value to an integer. Thus, what should be an enum with an underlying unsigned - value ends up as a typedef to an unsigned. - 1. In many cases function pointer declarations (i.e. call back delegates) and function declarations - in the LLVM-C headers don't include names for the parameters. - 1. In array vs out scalar semantics are not expressible in C (e.g. void foo(int* baz) could declare - a function with a single integer as an out parameter, or it could mean a function that accepts an - array of integers, with a fixed known size or possibly some sort of tag value to indicate the end, - etc...) - -## Configuration -LlvmBindings takes care of all of the challenges by using various custom passes on the AST to -correct or transform it to handle the special cases. Since it is not possible to automatically -determine the correct choice on many of the cases (like in vs. out array semantics, if a string -needs dispose, and which dispose method is needed, etc...) the LlvmBindings generator uses a -configuration data structure to determine the correct action. It also uses a set of validation -passes to ensure that any ambiguities without entries in the configuration trigger errors. It is -hoped that moving forward the only thing needed to update to a new version of LLVM is to update -the [configuration](LlvmBindingsGenerator-Configuration.md) tables. Although it is always possible -that some new pattern of code will require some new pass to handle. diff --git a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-YAML-Config-format.md b/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-YAML-Config-format.md deleted file mode 100644 index 5b522e62b6..0000000000 --- a/docfx/current/articles/InternalDetails/LlvmBindingsGenerator-YAML-Config-format.md +++ /dev/null @@ -1,181 +0,0 @@ -# YAML Configuration file format -## FunctionBindings -The functionBindings contains all the information for properly marshaling the native C APIs especially disambiguation of -overly generic data types like `char*` (Is that a pointer to a single character, an array of characters, a zero terminated string, -or perhaps a pointer to an unsigned byte value... [Inquiring minds want to know ;)]). This section is responsible for providing -the details for any data types that don't have an implicit mapping into .NET. The properties for the entry are: - -| Name | Type | Description | -|-------------|--------|-------------| -| Name | string | Exported "C" name of the function this binding applies to | -| IsObsolete | bool | Indicates if the function is considered obsolete [default is false] | -| DeprecationMessage | string | Deprecation message to apply to the projection (Required if IsObsolete is true) | -| IsExported | bool | Indicates if the function is exported from LibLLVM.dll [Default is true] | -| IsProjected | bool | Indicates if the function is projected to .NET via Ubiquity.NET.Llvm.Interop.dll [Default is true] | -| ReturnTransform | MarshalingInfo | Optional marshaling info for the return value | -| ParamTransforms | Collection of MarshalingInfo | Optional collection of marshaling info for any parameters that need special handling | - -Example: -```yaml -FunctionBindings: -# LLVMConstGEP2 appears in Core.h, but has no implementation [Go, Figure!] - - Name: LLVMConstGEP2 - IsExported: False - IsProjected: False - - - Name: LLVMTargetMachineEmitToFile - ReturnTransform: !Status {} - ParamTransforms: - - !String {Name: ErrorMessage, Semantics: Out, Kind: DisposeMessage } - - !String {Name: Filename, Semantics: In, Kind: CopyAlias } - - - Name: LLVMDIBuilderCreateExpression - ParamTransforms: - - !Array {SubType: I8, Name: Addr, Semantics: In } - -``` -### MarshalingInfo -The marshaling info in the FunctionBindings table provides details on the special marshaling -type transforms and attributes required to implement correct call semantics for the interop code. -All entries in the table implement the IMarshalInfo interface which, abstracts the details of -each case. There are several different implementations of the marshaling interface for the -various patterns in the LLVM-C headers. - -#### Base properties for marshaling -There are a number of different types of marshaling with a common set of properties for all. Thus a base type is defined for them -and each individual marshaling type includes these common properties. - -| Property name | Description | -|---------------|-------------| -| Name | Name of the parameter (ignored for return types) | -| IsAlias | Indicates if the return is an alias handle (Only applies to a return) | -| IsUnsafe | Indicates if the return type in managed code is unsafe (Only applies to return)| -| Semantics | Declares the semantics of the parameter (Return, In, Out, InOut - see table below for details) | - -##### Semantics -The semantics property defines the usage pattern for the parameter and may be one of the following values: - -| Value | Description | -|--------|-------------| -| Return | The binding applies to the return of the function (Implicitly applied for all Return Transforms and invalid on any ParamTransforms) | -| In | The binding is for an input parameter | -| Out | The binding is for an output parameter | -| InOut | The binding is for an in/out (e.g., 'ref') parameter | - - -#### Marshaling Info derived types -There are several types of derived marshaling info for specific uses these types are detailed here. - -##### Status -This is used to transform the native return type to a projected LLVMStatus to ensure it isn't confused -with a generic bool or integral value. Normally this is applied to some functions returning LLVMBool, -where the semantics of the returned value is 0 == success. There are a few APIs in the LLVM-C surface -where the native API returns a simple integral value. Using this in the return forces treatment as a -status value instead of a bool with success == true. - -> This marshaling info is only valid if Semantics == Return - -##### Alias -Indicates the return of a function is an alias pointer/handle type that the caller cannot release -and should not store. - -##### Unsafe -Indicates the return is an unsafe pointer type (This basically disables the validation pass check -for unsafe types for this function) - -##### String -Indicates the parameter or return is a string along with specifics about the kind of string which -implies the method of releasing the string data (if any). This type has the additional Kind property: - -###### String.Kind -Tye kind property declares the type of marshaling to apply for the string. In all cases the string is -copied to a managed string during marshaling. [A future optimization might be able to provide a means -to use a Span of UTF8 chars for in-place zero-copy strings but that is not yet implemented] The following -table provides details of the different kinds of string marshaling supported: - -| Name | Description | -|-----------|-------------| -| CopyAlias | Copy only, no release of the native string | -| DisposeMessage | Calls LLVMDisposeMessage() to release the native string | -| OrcDisposeMangledSymbol | Calls LLVMOrcDisposeMangledSymbol() to release the native string | -| DisposeErrorMessage | Calls LLVMDisposeErrorMessage() to release the native string | - -##### Primitive -Indicates the value is marshaled as a CppSharp.AST.PrimitiveType these all have internal mappings to -equivalent .NET types. - -##### Array -This indicates marshaling of an array of data, including a size. This type of marshaling info contains -the following additional properties: - -| Name | Description | -|------|-------------| -| SubType | The unmanaged type of the elements of the array (See: System.Runtime.InteropServices.UnmanagedType enum for details) | -| SizeParam | Integer index of an out parameter that contains the size of the array (required for out parameters, optional for in parameters | - -## HandleMap -The HandleMap table provides the link between various "handle" types in the native API and the means for properly -closing/disposing of them. This information is used to generate the appropriate .NET SafeHandle types used at the interop layer. -The elements in the map are either a `GlobalHandle` or a `ContextHandle`. - -> A key aspect of handles in the OO projection is the -automatic interning of the projected type such that there is a 1:1 correlation between a handle and an instance of a wrapper for -that handle. This ensures that reference equality of the projected types "just works" no matter how the instances are acquired. - -Example: -```yaml -HandleMap: - - !GlobalHandle {HandleName: LLVMModuleRef, Disposer: LLVMDisposeModule, Alias: True} - - !ContextHandle {HandleName: LLVMTypeRef } -# ... -``` - -### GlobalHandle -A global handle is one that is not implicitly owned by a container type. In LLVM most, but not all, handles refer to an object owned -by some other container. A Global handle is used for types that don't conform to that model and are instead basically free standing -allocations. These require dispose/close cleanup code when no longer needed. Thus, a global handle will have support for automatic -release of the resource. Additionally, sometimes a type normally represented via a global handle may appear as an out parameter or -return value of a function but that value is only an alias of an internally owned handle (like a reference in C++). In such cases a -special handle type with the suffix `Alias` is used to clearly indicate the intent. The alias handle types do NOT perform any cleanup -as they don't actually own the data. The properties of a GlobalHandle entry in the HandleMap table are: - -| Name | Description | -|------|-------------| -| HandleName | Name of the handle type (in "C")) | -| Disposer | Name of the C function that disposes this handle type | -| Alias | Boolean flag to indicate if this handle requires an alias type for use as a return value or out parameter | - -### ContextHandle -Context Handles are assumed owned by some context (Often, but not always, the literal `Context` type). These are simpler types -as they are always aliases and never perform any cleanup. The only property for a ContextHandle is the `HandleName`, which provides -the name of the type in `C` to allow generation of a properly named projected safe handle. - -## AnonymousEnums -C/C++ allow anonymous enums that effectively introduce a const into the parent namespace. -Generally, that's not desired for the interop. In fact the only known use cases for this -in the LLVM-C headers is to handle some FLAGS type enums where the language limits the values -of an enum to the range of an int. So what LLVM does is define the anonymous enum, and then -define a typedef of unsigned to a name for the enum. (see: Core.h\LLVMAttributeIndex as an -example) - -This table maps the name of the first element of an anonymous enum with the name of the -typedef used to represent the enumeration. This is used to find the correct declaration -in the AST - -Example: -```Yaml -AnonymousEnums: - LLVMAttributeReturnIndex: LLVMAttributeIndex - LLVMMDStringMetadataKind: LLVMMetadataKind -``` -## IgnoredHeaders -This section lists any headers in the input folders that do not participate in code generation and should be ignored. - -Example: -```yaml -IgnoredHeaders: - - llvm-c/LinkTimeOptimizer.h - - llvm-c/lto.h - - llvm-c/Remarks.h -``` - diff --git a/docfx/current/articles/InternalDetails/handleref-interning.md b/docfx/current/articles/InternalDetails/handleref-interning.md deleted file mode 100644 index 6f184fdf1f..0000000000 --- a/docfx/current/articles/InternalDetails/handleref-interning.md +++ /dev/null @@ -1,48 +0,0 @@ -# Interning LLVM handle to Managed wrappers -Many of the underlying object instances in LLVM are interned/uniqued. That is, -there will only be one instance of a type with a given value within some scope. - -In LLVM the most common scope for uniqueing is the [Context](xref:Ubiquity.NET.Llvm.Context) type. -In essence this class is an interning class factory for the LLVM IR system for a given -thread. Most object instances are ultimately owned by the context or a -[BitcodeModule](xref:Ubiquity.NET.Llvm.BitcodeModule). LLVM-C APIs use opaque pointers for LLVM -objects. This is projected to the low level Ubiquity.NET.Llvm.Native namespace as structs that wrap -an IntPtr to enforce some type safety. Furthermore, the LLVM-C API uses the highest level -of an object inheritance graph that it can when declaring the opaque type for the return -or arguments of functions. Thus, it is not possible to know the exact derived type from -the base opaque pointer alone. - -In addition to opaque pointers an additional challenge exists in mapping such pointers -to projected instances. Any projection is essentially a wrapper around the opaque -pointer. However, when an API returns an opaque pointer the interop layer needs to -determine what to do with it. A naive first approach (actually used in Ubiquity.NET.Llvm early - versions) is to simply create a new instance of the wrapper type giving it the -opaque pointer to work from. This will work for a while, until the code needs to compare -two instances. Ordinarily reference types are compared with reference equality. However, -if two projected instances are the same then reference equality will fail. While it is -plausible to add equality checks via IEquatable, they are not without problems, particularly -with respect to computing the hash code when the type is fully mutable. Furthermore, for -instances that may be enumerated multiple times (i.e. in a tree traversal or visitor pattern) -multiple instances of wrappers for the same underlying instance would be created, thus wasting -memory. - -To solve these problems Ubiquity.NET.Llvm uses an interning approach that mirrors the underlying LLVM -implementation by maintaining a mapping of the raw opaque pointers to a single managed instance. This -means that whenever an interop API retrieves an opaque pointer it can look up the wrapper and -provide that to the caller. Thus, reference equality "Just works". If there was no instance then -the interning system will create one. In order to create one it must know the concrete most -derived type for the opaque pointer to construct the wrapper type. The LLVM-C API generally defines -a reference handle type only for the top most base class rather than a distinct handle for each -derived type. Thus the mapping must know how to determine the correct derived type for the -projected wrapper. Fortunately, LLVM uses a custom type tagging mechanism to optimize such cases -internally (e.g. safe dynamic down casting by keeping a TypeKind value). While, the actual -implementation in LLVM is not as simplistic as that the details aren't relevant to the projection. -The projection wrapping can use the LLVM support to determine the correct type. Ubiquity.NET.Llvm uses -this to manage the mapping and creation of types and consumers can remain blissfully ignorant -of these details. - -## LLVM Internal implementation of interning -Internally Ubiquity.NET.Llvm uses a common Generic type Ubiquity.NET.Llvm.HandleInterningMap to -handle the interning. This type accepts a factory function and optional disposer function to -abstract the desired projected type factory and support for disposing items where the lifetime -is not implicitly managed by some container. diff --git a/docfx/current/articles/InternalDetails/index.md b/docfx/current/articles/InternalDetails/index.md deleted file mode 100644 index fbcc8c7622..0000000000 --- a/docfx/current/articles/InternalDetails/index.md +++ /dev/null @@ -1,6 +0,0 @@ -# Internal details -This section is focused on providing internal details of the Ubiquity.NET.Llvm implementation for -developers contributing to the contents of the Ubiquity.NET.Llvm library itself. If you are only -interested in using the Ubiquity.NET.Llvm APIs you don't need this information, though it may -satisfy curiosity 8^). - diff --git a/docfx/current/articles/InternalDetails/toc.md b/docfx/current/articles/InternalDetails/toc.md deleted file mode 100644 index 241f87c386..0000000000 --- a/docfx/current/articles/InternalDetails/toc.md +++ /dev/null @@ -1,10 +0,0 @@ -# LLVM-C API Handles -## [Wrapping LLVM-C Handles](llvm-handles.md) -## [Interning LLVM-C Handles](handleref-interning.md) -# Marshaling LLVM types -## [Marshaling Strings](marshal-string.md) -## [Marshaling LLVMBool](marshal-LLVMBool.md) -# LLVMBindingsGenerator -## [Bindings Overview](LlvmBindingsGenerator-Overview.md) -## [Configuration](LlvmBindingsGenerator-Configuration.md) -### [Marshaling Info Table](LlvmBindingsGenerator-YAML-Config-format.md) diff --git a/docfx/current/articles/Samples/index.md b/docfx/current/articles/Samples/index.md deleted file mode 100644 index 20dec53350..0000000000 --- a/docfx/current/articles/Samples/index.md +++ /dev/null @@ -1,24 +0,0 @@ -# Samples -Ubiquity.NET.Llvm provides multiple samples to aid in understanding how to use the Ubiquity.NET.Llvm library. -These samples are designed and intended to illustrate some aspect(s) of using Ubiquity.NET.Llvm itself -and are not generally considered production quality. - -- Basic Code Generation - * [CodeGenerator With Debug Information](xref:code-generation-with-debug-info) -- Kaleidoscope Tutorial - The Kaleidoscope tutorial walks through the implementation of a simple functional langguage -including, JIT execution, optimizations and Ahead of Time (AOT) compilation - * [Chapter 1 - Language Introduction](xref:Kaleidoscope-Overview) - * [Chapter 2 - Implementing the parser](xref:Kaleidoscope-ch2) - * [Chapter 3 - Generating LLVM IR](xref:Kaleidoscope-ch3) - * [Chapter 4 - JIT and Optimizer Support](xref:Kaleidoscope-ch4) - * [Chapter 5 - Control Flow](xref:Kaleidoscope-ch5) - * [Chapter 6 - User Defined Operators](xref:Kaleidoscope-ch6) - * [Chapter 7 - Mutable Variables](xref:Kaleidoscope-ch7) - * [Chapter 7.1 - Extreme Lazy JIT](xref:Kaleidoscope-ch7.1) - * [Chapter 8 - AOT Compilation](xref:Kaleidoscope-ch8) - * [Chapter 9 - Debug Information](xref:Kaleidoscope-ch9) - * Appendix - - [ParseTree Visitor](xref:Kaleidoscope-ParseTreeVisitor) - - [ParseTree Examples](xref:Kaleidoscope-Parsetree-examples) - - [AST](xref:Kaleidoscope-AST) diff --git a/docfx/current/articles/Samples/toc.md b/docfx/current/articles/Samples/toc.md deleted file mode 100644 index 6cb48a0eeb..0000000000 --- a/docfx/current/articles/Samples/toc.md +++ /dev/null @@ -1,20 +0,0 @@ -# Samples -## Code Generation -## [CodeGenerator With Debug Information](xref:code-generation-with-debug-info) -# Kaleidoscope -## [Chapter 1 - Language Introduction](xref:Kaleidoscope-Overview) -## [Chapter 2 - Implementing the parser](xref:Kaleidoscope-ch2) -## [Chapter 3 - Generating LLVM IR](xref:Kaleidoscope-ch3) -## [Chapter 4 - JIT and Optimizer Support](xref:Kaleidoscope-ch4) -## [Chapter 5 - Control Flow](xref:Kaleidoscope-ch5) -## [Chapter 6 - User Defined Operators](xref:Kaleidoscope-ch6) -## [Chapter 7 - Mutable Variables](xref:Kaleidoscope-ch7) -## [Chapter 7.1 - Extreme Lazy JIT](xref:Kaleidoscope-ch7.1) -## [Chapter 8 - AOT Compilation](xref:Kaleidoscope-ch8) -## [Chapter 9 - Debug Information](xref:Kaleidoscope-ch9) -## Appendix -### [Kaleidoscope.Runtime](xref:Kaleidoscope-Runtime) -### [Kaleidoscope AST](xref:Kaleidoscope-AST) -### [ParseTree Visitor](xref:Kaleidoscope-ParseTreeVisitor) -### [ParseTree Examples](xref:Kaleidoscope-Parsetree-examples) - diff --git a/docfx/current/docfx.json b/docfx/current/docfx.json deleted file mode 100644 index bb7fe74421..0000000000 --- a/docfx/current/docfx.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "metadata": [ - { - "src": [ - { - "files": [ - "src/Ubiquity.NET.Llvm/Ubiquity.NET.Llvm.csproj", - "src/Ubiquity.NET.Llvm.JIT/Ubiquity.NET.Llvm.JIT.csproj" - ], - "src": "../.." - } - ], - "dest": "api" - } - ], - "build": { - "xrefService": ["https://xref.docs.microsoft.com/query?uid={uid}"], - "content": [ - { - "files": [ - "toc.yml", - "index.md", - "ReleaseNotes.md", - "api/**.md", - "api/**.yml", - "articles/**.md", - "articles/**.yml" - ], - "exclude": [ "obj/**" ] - }, - { - "files": [ - "Kaleidoscope/**/Kaleidoscope*.md", - "CodeGenWithDebugInfo/codegeneration.md" - ], - "src": "../../samples", - "dest": "articles/samples" - } - ], - "resource": [ - { - "files": [ - "favicon.ico", - "DragonSharp48x48.png", - "DragonSharp.png" - ], - "src": ".." - }, - { - "files": [ - "/samples/**/*.svg" - ], - "dest": "articles", - "src": "../.." - } - ], - "overwrite": [ - { - "files": [ "apidoc/**.md" ], - "exclude": [ "obj/**" ] - } - ], - "dest": "../../BuildOutput/docs/current", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "postProcessors": [], - "noLangKeyword": false, - "globalMetadata": { - "_appTitle": "Ubiquity.NET.Llvm", - "_appFooter": "Copyright (C) 2017-2021, Ubiquity.NET Contributors", - "_appLogoPath": "DragonSharp48x48.png", - "_disableBreadcrumb": true, - "llvmVersion": "10.0.0", - "assemblies": [ - "Ubiquity.NET.Llvm" - ], - "_gitContribute": { - "repo": "https://github.com/UbiquityDotNET/Llvm.NET", - "branch": "develop" - }, - "_gitUrlPattern": "github" - } - } -} diff --git a/docfx/current/index.md b/docfx/current/index.md deleted file mode 100644 index 39affcee32..0000000000 --- a/docfx/current/index.md +++ /dev/null @@ -1,37 +0,0 @@ -# Ubiquity.NET.Llvm ->[!Important] -> It is important to point out that the Ubiquity.NET.Llvm documentation is not a substitute -> for the official LLVM documentation itself. That is, the content here is focused on -> using Ubiquity.NET.Llvm and how it maps to the underlying LLVM. The LLVM documentation is, -> generally speaking, required reading to understand Ubiquity.NET.Llvm. The topics here often -> contain links to the official LLVM documentation to help in further understanding the -> functionality of the library. - -## Table of Contents -* [Release Notes](ReleaseNotes.md) -* [Library API Documentation](api/index.md) -* [Samples](articles/Samples/index.md) - - Code Generation - * [CodeGenerator With Debug Information](xref:code-generation-with-debug-info) - * Kaleidoscope Tutorial - - [Chapter 1 - Language Introduction](xref:Kaleidoscope-Overview) - - [Chapter 2 - Implementing the parser](xref:Kaleidoscope-ch2) - - [Chapter 3 - Generating LLVM IR](xref:Kaleidoscope-ch3) - - [Chapter 4 - JIT and Optimizer Support](xref:Kaleidoscope-ch4) - - [Chapter 5 - Control Flow](xref:Kaleidoscope-ch5) - - [Chapter 6 - User Defined Operators](xref:Kaleidoscope-ch6) - - [Chapter 7 - Mutable Variables](xref:Kaleidoscope-ch7) - - [Chapter 7.1 - Extreme Lazy JIT](xref:Kaleidoscope-ch7.1) - - [Chapter 8 - AOT Compilation](xref:Kaleidoscope-ch8) - - [Chapter 9 - Debug Information](xref:Kaleidoscope-ch9) - * Appendix - - [ParseTree Visitor](xref:Kaleidoscope-ParseTreeVisitor) - - [ParseTree Examples](xref:Kaleidoscope-Parsetree-examples) - - [AST](xref:Kaleidoscope-AST) -* [Internal Details](articles/InternalDetails/index.md) - - LLVM-C API Handles - * [Wrapping LLVM-C Handles](articles/InternalDetails/llvm-handles.md) - * [Interning LLVM-C Handles](articles/InternalDetails/handleref-interning.md) - - Marshaling LLVM types - * [Marshaling Strings](articles/InternalDetails/marshal-string.md) - * [Marshaling LLVMBool](articles/InternalDetails/marshal-LLVMBool.md) diff --git a/docfx/current/llvm-xref.yml b/docfx/current/llvm-xref.yml deleted file mode 100644 index 51ae2bd7af..0000000000 --- a/docfx/current/llvm-xref.yml +++ /dev/null @@ -1,22 +0,0 @@ -references: - - uid: llvm_langref - name: LLVM Language reference - href: http://releases.llvm.org/10.0.0/docs/LangRef.html - - uid: llvm_sourceleveldebugging - name: LLVM Source Level Debugging - href: http://releases.llvm.org/10.0.0/docs/SourceLevelDebugging.html - - uid: llvm_docs_garbagecollection - name: LLVM Garbage Collection - href: http://releases.llvm.org/10.0.0/docs/GarbageCollection.html - - uid: llvm_docs_passes - name: LLVM Analysis and Transform Passes - href: http://releases.llvm.org/10.0.0/docs/Passes.html - - uid: llvm_exception_handling - name: LLVM Exception Handling - href: http://releases.llvm.org/10.0.0/docs/ExceptionHandling.html - - uid: llvm_misunderstood_gep - name: LLVM The Misunderstood GEP Instruction - href: http://releases.llvm.org/10.0.0/docs/GetElementPtr.html - - uid: llvm_sourcelevel_debugging - name: LLVM Source Level Debugging - href: http://releases.llvm.org/10.0.0/docs/SourceLevelDebugging.html diff --git a/docfx/current/toc.yml b/docfx/current/toc.yml deleted file mode 100644 index 07e134629c..0000000000 --- a/docfx/current/toc.yml +++ /dev/null @@ -1,10 +0,0 @@ -# This is the top level TOC, it describes the entries along the horizontal top menu of the main landing page for the versioned Docs -- name: API Documentation - href: api/ - homepage: api/index.md -- name: Source Repository - href: https://github.com/UbiquityDotNET/Llvm.NET -- name: LLVM.org - href: http://LLVM.org -- name: LLVM Docs - href: http://releases.llvm.org/10.0.0/docs/index.html diff --git a/docfx/docfx.json b/docfx/docfx.json new file mode 100644 index 0000000000..bd8fcf7927 --- /dev/null +++ b/docfx/docfx.json @@ -0,0 +1,207 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + // [Sigh] - docfx tooling will process and report warnings etc.. on projects found BEFORE it + // deals with the exclusion list so a simple glob for all csproj files doesn't work. It creates + // warnings and returns a non-success value which stops the build. + { + // LLVM OO Wrappers library + "memberLayout":"separatePages", + "namespaceLayout":"nested", + "src": [ + { + "src": "../src/Ubiquity.NET.Llvm", + "files": ["**.csproj"] + } + ], + "dest": "llvm/api" + }, + { + // Runtime utilities library + "memberLayout":"separatePages", + "namespaceLayout":"nested", + "src": [ + { + "src": "../src/Ubiquity.NET.Runtime.Utils", + "files": ["**.csproj"] + } + ], + "dest": "runtime-utils/api" + }, + { + // Extensions library + "memberLayout":"separatePages", + "namespaceLayout":"nested", + "src": [ + { + "src": "../src/Ubiquity.NET.Extensions", + "files": ["**.csproj"] + } + ], + "dest": "extensions/api" + }, + { + // ANTLR Utilities library + "memberLayout":"separatePages", + "namespaceLayout":"nested", + "src": [ + { + "src": "../src/Ubiquity.NET.ANTLR.Utils", + "files": ["**.csproj"] + } + ], + "dest": "antlr-utils/api" + }, + { + // Interop helpers library + "memberLayout":"separatePages", + "namespaceLayout":"nested", + "src": [ + { + "src": "../src/Ubiquity.NET.InteropHelpers", + "files": ["**.csproj"] + } + ], + "dest": "interop-helpers/api" + } + ], + "build": { + // xref YAML files that contain mappings from an XREF ID to the actual URL for the content in an external source + "xref": [ + "llvm/api/llvm-xref.yml", + "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" + ], + // NOTE: Unless overridden by a "src" key, All File paths are relative to the location of this docfx.json file + "content": [ + { + // Root of the site; only uses the index and TOC explicitly named (NO GLOBS to confuse things!) + "files": [ + "index.md", + "toc.yml" + ] + }, + { + // LLVM project additional content, Includes the generated metadata API folder + "files": [ + "llvm/**.{md,yml}" + ], + // Exclude the namespace overwrites and XREF maps as they are listed explicitly elsewhere + "exclude": [ + "**/namespaces/**.md", + "**/*-xref.yml" + ] + }, + { + // Pull in the LLVM samples as content. The LLVM core project is more complex in the docfx.json as it + // includes the samples but they are NOT participating in metadata generation. They only contribute + // additional MD file content. That content is located in the source WITH the sample. This eases the + // maintenance and editing of such files but makes referencing them in the final generated docs more + // complex as a distinct content node (this node) is needed to identify them. + // + // NOTE: DOCFX does NOT support use of ".." as the leading portion of a path in the "files" property + // instead it is ONLY allowed in the "src" property. + // Thus, file paths are relative to the samples directory + "files": [ + "**.{md,yml}" + ], + "src": "../src/Samples", + "dest": "llvm/articles/Samples", + "exclude": [ + "**/ReadMe.md" + ] + }, + { + // Runtime Utils project additional content, Includes the generated metadata API folder + // NOTE: File paths are relative to the location of this file + "files": [ + "runtime-utils/**.{md,yml}" + ], + // Exclude the namespace overwrites and XREF maps as they are listed explicitly elsewhere + "exclude": [ + "**/namespaces/**.md", + "**/*-xref.yml" + ] + }, + { + // extensions project additional content, Includes the generated metadata API folder + "files": [ + "extensions/**.{md,yml}" + ], + // Exclude the namespace overwrites and XREF maps as they are listed explicitly elsewhere + "exclude": [ + "**/namespaces/**.md", + "**/*-xref.yml" + ] + }, + { + // ANTLR Utils project additional content, Includes the generated metadata API folder + "files": [ + "antlr-utils/**.{md,yml}" + ], + // Exclude the namespace overwrites and XREF maps as they are listed explicitly elsewhere + "exclude": [ + "**/namespaces/**.md", + "**/*-xref.yml" + ] + }, + { + // InteropHelpers project additional content, Includes the generated metadata API folder + "files": [ + "interop-helpers/**.{md,yml}" + ], + // Exclude the namespace overwrites and XREF maps as they are listed explicitly elsewhere + "exclude": [ + "**/namespaces/**.md", + "**/*-xref.yml" + ] + } + ], + "resource": [ + { + // General site resources + "files": [ + "favicon.ico", + "**.png", + "**.svg" + ] + }, + { + // LVM library samples specific resources + // FUTURE: Move the samples folder so this is included above + "files": [ + "**.png", + "**.svg" + ], + "src": "../src/Samples", + "dest": "llvm/articles/Samples" + } + ], + "overwrite": [ + { + "files": [ + "**/apidocs/**.md", + "**/namespaces/**.md" + ] + } + ], + "template": [ + "default", + "modern", + "templates/Ubiquity" + ], + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "postProcessors": [], + "globalMetadata": { + "_appTitle": "Ubiquity.NET", + "_appFooter": "Copyright (C) 2017-2025, Ubiquity.NET Contributors", + "_appLogoPath": "llvm/DragonSharp48x48.png", // TODO: change this to something more generic... + "_disableBreadcrumb": true, + "_gitContribute": { + "repo": "https://github.com/UbiquityDotNET/Llvm.NET", + "branch": "develop" + }, + "_gitUrlPattern": "github" + } + } +} diff --git a/docfx/documentation.msbuildproj b/docfx/documentation.msbuildproj new file mode 100644 index 0000000000..9b903b079c --- /dev/null +++ b/docfx/documentation.msbuildproj @@ -0,0 +1,59 @@ + + + net9.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docfx/extensions/api/index.md b/docfx/extensions/api/index.md new file mode 100644 index 0000000000..d49036db5e --- /dev/null +++ b/docfx/extensions/api/index.md @@ -0,0 +1,3 @@ +# About +Ubiquity.NET.Extensions API overview + diff --git a/docfx/extensions/index.md b/docfx/extensions/index.md new file mode 100644 index 0000000000..77039746df --- /dev/null +++ b/docfx/extensions/index.md @@ -0,0 +1,33 @@ +# About +Ubiquity.NET.Extensions contains general extensions for .NET. This is +a bit of a [grab bag](https://www.merriam-webster.com/dictionary/grab%20bag) of functionality used by +but not actually part of multiple other Ubiquity.NET projects. + +## Key support +* Computing a hash code for a ReadOnlySpan of bytes using + [System.IO.System.IO.Hashing.XxHash3](https://learn.microsoft.com/en-us/dotnet/api/system.io.hashing.xxhash3) +* DisposableAction for building actions that must occur on Dispose + - This is useful for implementing the RAII pattern in .NET. +* MustUseReturnValueAttribute that is compatible with the [MustUseRetVal](https://github.com/mykolav/must-use-ret-val-fs) + package. +* StringNormalizer extensions to support converting line endings of strings + for interoperability. +* A custom ValidatedNotNullAttribute to allow compiler to assume a parameter + value is validated as not null. +* Fluent style parameter value validation extensions. + - These are useful when passing parameters to a function that produces a + result that is fed to the base constructor. These are also useful in body + expressions to validate input parameters. +* DictionaryBuilder to enable dictionary initializer style initialization of + `ImmutableDictionary` with significantly reduced overhead. + - This leverages an `ImmutableDictionary.Builder` under the hood to build the dictionary. + When the `ToImmutable()` method is called the builder is converted to the immutable state without any + overhead of a copy or re-construction of hash tables etc... +* KvpArrayBuilder to enable array initializer style initialization of + `ImmutableArray>` with significantly reduced overhead. + - This leverages an `ImmutableArray.Builder` under the hood to build the array directly. + When the `ToImmutable()` method is called the builder is converted to the immutable state without any + overhead of a copy. + - Since this is an array and not a dictionary there is no overhead for allocating, initializing or copying + any hash mapping for the keys. + diff --git a/docfx/extensions/toc.yml b/docfx/extensions/toc.yml new file mode 100644 index 0000000000..262c8b726b --- /dev/null +++ b/docfx/extensions/toc.yml @@ -0,0 +1,5 @@ +# TOC (Left nav) for the 'extensions' folder +- name: Overview + href: index.md +- name: Namespaces + href: api/toc.yml diff --git a/docfx/index.md b/docfx/index.md new file mode 100644 index 0000000000..55b576d855 --- /dev/null +++ b/docfx/index.md @@ -0,0 +1,17 @@ +# Ubiquity.NET +Ubiquity.NET family of libraries provides support for a number of scenarios but the primary focus is +AOT code generation of .NET for Embedded systems. We aren't quite there yet, but are rather close. In +the mean time this set of libraries provides the building blocks needed for creating a Domain Specific +Language (DSL) implementation including JIT execution. Several useful generalized librareis are also +included. + +## The Libraries in this repository +(At least the ones generating docs at this point anyway! :grin:) + +| Library | Description | +|---------|-------------| +| [Ubiquity.NET.Llvm](llvm/index.md) | This library contains The core of the LLVM projection to .NET | +| [Ubiquity.NET.Runtime.Utils](runtime-utils/index.md) | This library contains common support for DSL runtime and language implementors | +| [Ubiquity.NET.Extensions](extensions/index.md) | This library contains general extensions and helpers for many sceanrios using .NET | +| [Ubiquity.NET.Antlr.Utils](antlr-utils/index.md) | This library contains extensions and helpers for using ANTLR with .NET | +| [Ubiquity.NET.InteropHelpers](interop-helpers/index.md) | This library contains extensions and helpers for implementing interop support for native libraries | diff --git a/docfx/index/ReadMe.md b/docfx/index/ReadMe.md deleted file mode 100644 index f39ad50687..0000000000 --- a/docfx/index/ReadMe.md +++ /dev/null @@ -1,9 +0,0 @@ -# Docs Index -This folder contains the DocFx project for the top level index for this repo. -The top level index contains a general high level overview of the project as -well as links to the version specific full documentation. (e.g. this is -intentionally a small project that doesn't change except to include links -to newer versions of the documentation.) Older documentation is NOT wiped -out by new versions. (For final releases anyway, pre-release docs overwrite -any previous pre-releases to keep the number of docs versions to a sane level. - diff --git a/docfx/index/Ubiquity.NET.Llvm.Docfx.Index.csproj b/docfx/index/Ubiquity.NET.Llvm.Docfx.Index.csproj deleted file mode 100644 index a6fb74b986..0000000000 --- a/docfx/index/Ubiquity.NET.Llvm.Docfx.Index.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net5.0 - - - - - - - - - - - - - - Warning - $(IntermediateOutputPath)DocFx-Metadata.log - $(DocParameters) --globalMetadata="{_buildVersion:\"$(FullBuildNumber)\"}" - $(DocParameters) --intermediateFolder="$(IntermediateOutputPath.TrimEnd('\'))" - $(DocParameters) --xref="@(DocFxCrossRefMap->'%(FullPath)',',')" - statictoc,$(Pkgmemberpage)\content,$(BuildRootDir)docfx\templates\Ubiquity - - - diff --git a/docfx/index/docfx.json b/docfx/index/docfx.json deleted file mode 100644 index 2fd873f55b..0000000000 --- a/docfx/index/docfx.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "build": { - "content": [ - { - "files": [ - "toc.yml", - "index.md" - ] - } - ], - "resource": [ - { - "files": [ - "favicon.ico", - "DragonSharp48x48.png", - "DragonSharp.png" - ], - "src": ".." - } - ], - "dest": "../../BuildOutput/docs", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "postProcessors": [], - "noLangKeyword": false, - "globalMetadata": { - "_appTitle": "Ubiquity.NET.Llvm", - "_appFooter": "Copyright (C) 2017-2021, Ubiquity.NET Contributors", - "_appLogoPath": "DragonSharp48x48.png", - "_disableBreadcrumb": true, - "llvmVersion": "10.0.0", - "_gitContribute": { - "repo": "https://github.com/UbiquityDotNET/Llvm.NET", - "branch": "develop" - }, - "_gitUrlPattern": "github" - } - } -} diff --git a/docfx/index/index.md b/docfx/index/index.md deleted file mode 100644 index 6150d2821e..0000000000 --- a/docfx/index/index.md +++ /dev/null @@ -1,32 +0,0 @@ -![Logo](../DragonSharp.png) - -# Ubiquity.NET.Llvm -Ubiquity.NET.Llvm is a managed wrapper around an extended LLVM-C API including an Object Oriented model that closely matches -the underlying LLVM internal object model. This allows for building code generation and other utilities -leveraging LLVM from .NET applications. - -## Guiding principles - - 1. Mirror the underlying LLVM model as much as possible while - providing a well behaved .NET projection including: - 1. Class names and hierarchies - 2. Object identity and reference equality - 3. [Fluent](https://en.wikipedia.org/wiki/Fluent_interface) APIs when plausible and appropriate - 2. Hide low-level interop details and the raw LLVM-C API. - The native model for LLVM is a C++ class hierarchy and not the LLVM-C API used for most - language/runtime bindings. Ubiquity.NET.Llvm is designed to provide an OO model that faithfully reflects the - underlying LLVM model while fitting naturally into .NET programming patterns. - 3. Leverage existing LLVM-C APIs underneath whenever possible - 1. Extend only when needed with custom wrappers - 4. FxCop/Code Analysis Clean - -## Features -* LLVM Cross target code generation from .NET code -* JIT engine support for creating dynamic domain specific language - runtimes with JIT support. -* Ahead of time compilation with support for Link time optimization and debug information -* Object model that reflects the underlying LLVM classes - -## Releases -[Current](xref:current-release) -[v8.0.1](xref:release-8-0-1) diff --git a/docfx/index/toc.yml b/docfx/index/toc.yml deleted file mode 100644 index 9f5bdffbfe..0000000000 --- a/docfx/index/toc.yml +++ /dev/null @@ -1,7 +0,0 @@ -# This is the top level TOC, it describes the entries along the horizontal top menu of the main landing page for the Docs -- name: Current Release - href: xref:current-release -- name: Source Repository - href: https://github.com/UbiquityDotNET/Llvm.NET -- name: LLVM.org - href: http://LLVM.org diff --git a/docfx/index/versions-xref.yml b/docfx/index/versions-xref.yml deleted file mode 100644 index 95bf29bd82..0000000000 --- a/docfx/index/versions-xref.yml +++ /dev/null @@ -1,7 +0,0 @@ -references: - - uid: current-release - name: Current Release - href: current/index.html - - uid: release-8-0-1 - name: v8.0.1 - href: v8.0.1/index.html diff --git a/docfx/interop-helpers/api/index.md b/docfx/interop-helpers/api/index.md new file mode 100644 index 0000000000..c468db81c2 --- /dev/null +++ b/docfx/interop-helpers/api/index.md @@ -0,0 +1,2 @@ +# About +Ubiquity.NET.InteropHelpers API overview... diff --git a/docfx/interop-helpers/index.md b/docfx/interop-helpers/index.md new file mode 100644 index 0000000000..cd527b041d --- /dev/null +++ b/docfx/interop-helpers/index.md @@ -0,0 +1,26 @@ +# About +Ubiquity.NET.InteropHelpers helper support common to low level interop libraries. While this +library is intended to support the Ubiquity.NET.Llvm interop requirements there isn't anything +bound to that library in the support here. That is it is independent and a useful library for +any code base providing interop support. + +# Key Features +* String handling + * A lot of interop deals with strings in some form or another and handling them + is a major amount of effort for most interop libraries. The support provided here + enables lazy evaluation/marshalling and encoding of native strings and managed strings. + These allow a simple `byte[]` to store a native string and ONLY marshals to a UTF16 + managed string once when needed. This allows storing and passing strings in their + native form for FAST retrieval from a native call and then providing that same string + as an `in` parameter in another call. All without the need to marshal from native and + then back again just for the call. This is a MAJOR performance enhancement for APIs + that deal in strings. +* Delegates and NativeCallbacks as Function pointers + * Function pointers are a new feature of C# that makes for very high performance interop + scenarios. However, sometimes the callback for a function pointer actually needs + additional data not part of the parameters of the function to work properly. This library + provides support for such scenarios where a delegate is used to "capture" the data while + still supporting AOT scenarios. (NOTE: Marshal.GetFunctionPointerForDelegate() must + dynamically emit a thunk that contains the proper signature and the captured "this" pointer + so is NOT AOT friendly) The support offered in this library, though a bit more tedious, + is AOT friendly. diff --git a/docfx/interop-helpers/toc.yml b/docfx/interop-helpers/toc.yml new file mode 100644 index 0000000000..21725efca2 --- /dev/null +++ b/docfx/interop-helpers/toc.yml @@ -0,0 +1,5 @@ +# TOC (Left nav) for LLVM folder +- name: Overview + href: index.md +- name: Namespaces + href: api/toc.yml diff --git a/docfx/DragonSharp.png b/docfx/llvm/DragonSharp.png similarity index 100% rename from docfx/DragonSharp.png rename to docfx/llvm/DragonSharp.png diff --git a/docfx/DragonSharp48x48.png b/docfx/llvm/DragonSharp48x48.png similarity index 100% rename from docfx/DragonSharp48x48.png rename to docfx/llvm/DragonSharp48x48.png diff --git a/docfx/current/ReleaseNotes.md b/docfx/llvm/ReleaseNotes.md similarity index 87% rename from docfx/current/ReleaseNotes.md rename to docfx/llvm/ReleaseNotes.md index 0c262750b8..6943f11bd2 100644 --- a/docfx/current/ReleaseNotes.md +++ b/docfx/llvm/ReleaseNotes.md @@ -1,10 +1,36 @@ # Release Notes -# V13.0.0.Alpha -Split JIT support into a distinct Library. The JIT support is an area undergoing a LOT -of active development in LLVM. This leaves many aspects of it non-functional or partially -functional on some platforms. To ensure that the main core of Llvm.NET is able to progress, -the JIT support now lives in a distinct package (That has a dependency on the core so that -only one reference is needed. +# V20.0.1.Alpha +Major re-work to support LLVM 20 and .NET 9 and later with focus on performance and heading towards AOT. +Major changes include: +* OrcJIT v2 support + - Including functioning lazy materialization from the AST + - Even on Microsoft Windows! :wink: +* Opaque pointers + - Underlying LLVM uses only opaque pointers however these wrappers account for + that as much as possible without significant impact on calling code. The wrappers, + when used with debug information, support tracking the LLVM type of the `pointee` + for you in most cases. Though if not using any debug information or otherwise dealing + in the raw types applications will need to keep track of the type of a pointer + instead of relying on the LLVM IR to do that for you. +* Dropped reference equality to support multi-threaded nature of OrcJIT. + - Things got complicated and broke around chapter 5 of the Kaleidoscope tutorials. + The basic problem with interning is that it doesn't account for ownership. In fact + it downright ignores the point. This is a serious problem when dealing with a + multi-threaded JIT engine as you might end up disposing something you own that + was transferred to the native API or worse an alias is resolved to an owned + instance which is then destroyed - OOPS! [Bad idea - seemed like a good idea + at the time! :facepalm: ] +* Consumers need to consider IDispose and "ownership" in general + - Usually this is as simple as a `using` statement to properly handle + cleanup in scope if there is an exception. Sometimes it takes a bit more + thought to handle properly. (.NET, C# and the general community have no concept of the + common native code "move semantics" pattern) + +[Additional notes on this release go here...] +## Breaking changes +This is a major release and there are a LOT of changes though they are all fairly small. A look +at the samples and test code will show that the core of the library didn't change but some things +about how you use it did. # v10.0.0 @@ -69,7 +95,7 @@ Some APIs had inconsistent, misspelled or confusing names and were updated. ### Obsoleted APIs | Obsolete API | Alternative API | Justification | |---------------------------|--------------------------------|---------------| -| BitcodeModule.AddFunction | BitcodeModule.CreateFunction() | The Create vs Add between debug info and raw native was always confusing | +| Module.AddFunction | Module.CreateFunction() | The Create vs Add between debug info and raw native was always confusing | ### Types no longer disposable Some types are no longer disposable. The LLVMObjectRef type uses SafeHandle, which eliminates the need @@ -197,12 +223,12 @@ Ubiquity.NET.Llvm library: ensure that a function pass manager, which is bound to a module, is destroyed before the module it is bound to. Failure to do so can result in app crashes from access violations in the native LLVM code. -7. BitcodeModule +7. Module 1. MakeShared and shared refs of modules is removed. (This was created for OrcJIT use of shared_ptr under the hood, which is no longer used. OrcJit now uses the same ownership transfer model as the legacy engines. E.g. the ownership for the module is transferred to the JIT engine) - 2. BitCodeModule is now Disposable backed by a safe handle, this allows for detaching and + 2. Module is now Disposable backed by a safe handle, this allows for detaching and invalidating the underlying LLVMModuleRef when the module is provided to the JIT 3. CreateFunction() signature changed, Dropped the default null node parameters not supported by the LLVM-C implementation. diff --git a/docfx/llvm/api/DragonSharp.png b/docfx/llvm/api/DragonSharp.png new file mode 100644 index 0000000000..9e9b8a918b Binary files /dev/null and b/docfx/llvm/api/DragonSharp.png differ diff --git a/docfx/llvm/api/DragonSharp48x48.png b/docfx/llvm/api/DragonSharp48x48.png new file mode 100644 index 0000000000..1820208e57 Binary files /dev/null and b/docfx/llvm/api/DragonSharp48x48.png differ diff --git a/docfx/llvm/api/index.md b/docfx/llvm/api/index.md new file mode 100644 index 0000000000..b2f2f83b47 --- /dev/null +++ b/docfx/llvm/api/index.md @@ -0,0 +1,2 @@ +# Ubiquity.NET.Llvm +API/namespace docs overview... diff --git a/docfx/llvm/api/llvm-xref.yml b/docfx/llvm/api/llvm-xref.yml new file mode 100644 index 0000000000..78e204d3fa --- /dev/null +++ b/docfx/llvm/api/llvm-xref.yml @@ -0,0 +1,25 @@ +references: + - uid: llvm_langref + name: LLVM Language reference + href: https://releases.llvm.org/20.1.0/docs/LangRef.html + - uid: llvm_sourceleveldebugging + name: LLVM Source Level Debugging + href: https://releases.llvm.org/20.1.0/docs/SourceLevelDebugging.html + - uid: llvm_docs_garbagecollection + name: LLVM Garbage Collection + href: https://releases.llvm.org/20.1.0/docs/GarbageCollection.html + - uid: llvm_docs_passes + name: LLVM Analysis and Transform Passes + href: https://releases.llvm.org/20.1.0/docs/Passes.html + - uid: llvm_exception_handling + name: LLVM Exception Handling + href: https://releases.llvm.org/20.1.0/docs/ExceptionHandling.html + - uid: llvm_misunderstood_gep + name: LLVM The Misunderstood GEP Instruction + href: https://releases.llvm.org/20.1.0/docs/GetElementPtr.html + - uid: llvm_sourcelevel_debugging + name: LLVM Source Level Debugging + href: https://releases.llvm.org/20.1.0/docs/SourceLevelDebugging.html + - uid: llvm_kaleidoscope_tutorial + name: LLVM Kaledoscope Tutorial + href: https://releases.llvm.org/20.1.0/docs/tutorial/MyFirstLanguageFrontend/index.html diff --git a/docfx/llvm/articles/InternalDetails/index.md b/docfx/llvm/articles/InternalDetails/index.md new file mode 100644 index 0000000000..dc3beb6a6f --- /dev/null +++ b/docfx/llvm/articles/InternalDetails/index.md @@ -0,0 +1,16 @@ +# Internal details +This section is focused on providing internal details of the Ubiquity.NET.Llvm implementation for +developers contributing to the contents of the Ubiquity.NET.Llvm library itself. If you are only +interested in using the Ubiquity.NET.Llvm APIs you don't need this information, though it may +satisfy curiosity 8^). + +## Generate Handles +The source for the handles is generated from the headers by the LibLLVM repository build. They +are created and published in the `Ubiquity.NET.Llvm.Interop.Handles` NuGet package. This package +has dependencies on types in the `Ubiquity.NET.Llvm.Interop` namespace and library so there's +naturally some tensions or issues with coherency there. The intent is to move ALL of the LIBLLVM +support into this repo. But doing so means careful use of the build to ensure only the parts that +have changed are built. (Specifically, that changes to the managed code portions of the wrappers +DO NOT re-build the LLVM library. [THat's a HUGE beast that takes significant resources to build, +but changes rarely so a rebuild after a release should be kept to a minimum.]) Until the +unification happens, the tension exists. diff --git a/docfx/current/articles/InternalDetails/llvm-handles.md b/docfx/llvm/articles/InternalDetails/llvm-handles.md similarity index 99% rename from docfx/current/articles/InternalDetails/llvm-handles.md rename to docfx/llvm/articles/InternalDetails/llvm-handles.md index 6e17115971..9fdaffcee3 100644 --- a/docfx/current/articles/InternalDetails/llvm-handles.md +++ b/docfx/llvm/articles/InternalDetails/llvm-handles.md @@ -1,3 +1,7 @@ +--- +title: LLVM-C Handle Wrappers +--- + ## LLVM-C Handle wrappers Handles for LLVM are just opaque pointers. They generally come in one of three forms. diff --git a/docfx/current/articles/InternalDetails/marshal-LLVMBool.md b/docfx/llvm/articles/InternalDetails/marshal-LLVMBool.md similarity index 100% rename from docfx/current/articles/InternalDetails/marshal-LLVMBool.md rename to docfx/llvm/articles/InternalDetails/marshal-LLVMBool.md diff --git a/docfx/current/articles/InternalDetails/marshal-string.md b/docfx/llvm/articles/InternalDetails/marshal-string.md similarity index 100% rename from docfx/current/articles/InternalDetails/marshal-string.md rename to docfx/llvm/articles/InternalDetails/marshal-string.md diff --git a/docfx/llvm/articles/InternalDetails/toc.yml b/docfx/llvm/articles/InternalDetails/toc.yml new file mode 100644 index 0000000000..d51b8c3123 --- /dev/null +++ b/docfx/llvm/articles/InternalDetails/toc.yml @@ -0,0 +1,5 @@ +# TOC (left nave) for the internal details +- name: LLVM Handles + href: llvm-handles.md +- name: Marshalling Bool + href: marshal-LLVMBool.md diff --git a/docfx/llvm/articles/Samples/index.md b/docfx/llvm/articles/Samples/index.md new file mode 100644 index 0000000000..cb36040bd0 --- /dev/null +++ b/docfx/llvm/articles/Samples/index.md @@ -0,0 +1,7 @@ +# Samples +Ubiquity.NET.Llvm provides multiple samples to aid in understanding how to use the Ubiquity.NET.Llvm library. +These samples are designed and intended to illustrate some aspect(s) of using Ubiquity.NET.Llvm itself +and are not generally considered production quality. They serve to illustrate usage of some aspect with as +little extraneous overhead as possible. The Kaleidsocope examples all use a common runtime, which is not +necessary to understand usage of the Ubiquity.NET.Llvm library. Though they can serve as an example of how +to implement something similar. diff --git a/docfx/llvm/articles/Samples/toc.yml b/docfx/llvm/articles/Samples/toc.yml new file mode 100644 index 0000000000..a9c257c9f3 --- /dev/null +++ b/docfx/llvm/articles/Samples/toc.yml @@ -0,0 +1,41 @@ +# TOC (left Nav) for the LLVM Samples +- name: CodeGenerator With Debug Information + uid: code-generation-with-debug-info +- name: OrcV2VeryLazy + uid: orcjitv2-very-lazy +- name: Kaleidoscope + items: + - name: Chapter 1 - Language Introduction + uid: Kaleidoscope-ch1 + - name: Chapter 2 - Implementing the parser + uid: Kaleidoscope-ch2 + - name: Chapter 3 - Generating LLVM IR + uid: Kaleidoscope-ch3 + items: + - name: Chapter 3.5 - Optimization + uid: Kaleidoscope-ch3.5 + - name: Chapter 4 - JIT and Optimizer Support + uid: Kaleidoscope-ch4 + - name: Chapter 5 - Control Flow + uid: Kaleidoscope-ch5 + - name: Chapter 6 - User Defined Operators + uid: Kaleidoscope-ch6 + - name: Chapter 7 - Mutable Variables + uid: Kaleidoscope-ch7 + items: + - name: Chapter 7.1 - Extereme Lazy JIT + uid: Kaleidoscope-ch7.1 + - name: Chapter 8 - AOT Compilation + uid: Kaleidoscope-ch8 + - name: Chapter 9 - Debug Information + uid: Kaleidoscope-ch9 + - name: Appendix + items: + - name: Kaleidoscope.Runtime + uid: Kaleidoscope-Runtime + - name: Kaleidoscope.AST + uid: Kaleidoscope-AST + - name: ParseTree Visitor + uid: Kaleidoscope-ParseTreeVisitor + - name: ParseTree Examples + uid: Kaleidoscope-Parsetree-examples diff --git a/docfx/llvm/index.md b/docfx/llvm/index.md new file mode 100644 index 0000000000..17322796cf --- /dev/null +++ b/docfx/llvm/index.md @@ -0,0 +1,112 @@ +# Ubiquity.NET.Llvm +Ubiquity.NET.Llvm is a managed wrapper around an extended LLVM-C API including an Object Oriented model that closely matches +the underlying LLVM internal object model. This allows for building code generation, JIT and other utilities leveraging LLVM +from .NET applications. + +## Guiding principles + + 1. Mirror the underlying LLVM model as much as possible while + providing a well behaved .NET projection including: + 1. Class names and hierarchies + 2. Object identity and reference equality + 3. [Fluent](https://en.wikipedia.org/wiki/Fluent_interface) APIs when plausible and appropriate + 2. Hide low-level interop details and the raw LLVM-C API. + The native model for LLVM is a C++ class hierarchy and not the LLVM-C API used for most + language/runtime bindings. Ubiquity.NET.Llvm is designed to provide an OO model that faithfully reflects the + underlying LLVM model while fitting naturally into .NET programming patterns. + 3. Leverage existing LLVM-C APIs underneath whenever possible + 1. Extend only when needed with custom wrappers + 4. FxCop/Code Analysis Clean + +## Features +* LLVM Cross target code generation from .NET code +* JIT engine support for creating dynamic domain specific language + runtimes with JIT support. +* Ahead of time compilation with support for Link time optimization and debug information +* Object model that reflects the underlying LLVM classes + +>[!Important] +> It is important to point out that the Ubiquity.NET.Llvm documentation is not a substitute +> for the official LLVM documentation itself. That is, the content here is focused on +> using Ubiquity.NET.Llvm and how it maps to the underlying LLVM. The LLVM documentation is, +> generally speaking, required reading to understand Ubiquity.NET.Llvm. The topics here often +> contain links to the official LLVM documentation to help in further understanding the +> functionality of the library. + +## Breaking changes from prior versions +In Version 20.1.0 a number of issues were resolved using newer .NET as well as in the LLVM +design itself that allows for a fundamentally new implementation. While there isn't a LOT of +code that consumers have to change (See the samples and compare against older versions) there +are important factors to consider in the new library: +1) Ownership + - The previous variants of the library did NOT generally consider ownership carefully. It + routinely provided types that under some circumstances require disposal, and others did + not (Alias). This caused problems for the interning of projected types as the behavior + of the first instance interned was used. (Usually leading to leaks or strange crashes at + obscure unrelated times that made testing extremely difficult [Worst case scenario, it + works fine in all in-house testing but breaks in the field!). +3) No Interning of projected types + - Projected types are no longer interned, this dramatically increases performance and + reduces the complexity of maintenance of this library. Generally it should have little + impact as anything that produces an alias where the type might in other cases require + the owner to dispose it should now produce an interface that is not disposable. Anything + the caller owns IS an IDisposable. + - Move semantics are handled internally where the provided instance is invalidated but + the Dispose remains a safe NOP. This helps prevent leaks or confusion when transfer is + unable to complete due to an exception. The caller still owns the resource. Either way, + Dispose() is called to clean it up, which is either a safe NOP, or an actual release of + the native resource. +2) Assumption of Reference Equality + 1) In the new library there is NO guarantee of reference equality for reference types. + - Such types MAY be value equal if they refer to the same underlying native instance. + +### Ownership and IDisposable +When dealing with native interop the concept of ownership is of critical importance. The underlying +resources are NOT controlled by a Garbage collector, and therefore require care to avoid access violations +and other app crash scenarios. This library aims to make that much easier by using IDisposable for these +scenarios. It is ***HIGHLY** recommended to use the [IDisposableAnalyzers](https://www.nuget.org/packages/IDisposableAnalyzers/) +in ANY project that consumes this library. (It was/is used internally to find and fix issues across the +library that were tedious to identify otherwise). + +#### Ownership transfer (move semantics) +Sometimes an API will transfer ownership to a containing type or native code in general. In C++ terminology +that is known as 'move semantics' and typically handled with `std::move()` but .NET and C# have no such +concept. To make life easier and keep usage of disposable types consistent, when a method follows the move +semantics it should be documented as such and, more importantly, it will set the value provided as invalid +BUT calling `Dispose()` is still a NOP. This keeps usage consistent even if ownership is transferred. +Attempting to use an instance after it is transferred will result in an `ObjectDisposedException`. + +Example from [OrcV2VeryLazy](xref:orcjitv2-very-lazy) sample application +``` C# +// ownership of this Materialization Unit (MU) is "moved" to the JITDyLib in the +// call to Define. Applying a "using" ensures it is released even if an exception +// occurs that prevents completion of the transfer. When transfer completes the +// MU is marked as disposed but a call to Dispose() is a safe NOP. Thus, this handles +// all conditions consistently +using var fooMu = new CustomMaterializationUnit("FooMU", Materialize, fooSym); +jit.MainLib.Define(fooMu); +``` + +### Unowned references (alias) +For an unowned reference to an underlying resource an interface is defined such as [IModule](xref:Ubiquity.NET.Llvm.IModule). +When a property returns an interface only it is not Disposable and ownership remains with the source. +Care is required on the part of a consumer to not store that instance anywhere and treat it as if it was a +`ref struct` (That is, only held on the stack). While the GC is free to clean up such an instance at any time +this prevents attempts to use the interface after the containing object is destroyed. + +### Equality +In prior releases of this library a complex scheme of interning projection wrappers was used to support +reference equality. When you had an instance of class 'foo' you could just compare it to any other using reference +equality. For any two that referred to the same native instance they'd be the same object. While this had convenience +for the user it had a multitude of hidden flaws. The biggest is the concept of ownership. [See discussion above]. If +objects are interned then you would end up with whatever instance was first created, ignoring the ownership completely. +If the first instance was an unowned alias, then it would leak as nothing owns it... If it was NOT an alias, then, +when retrieved from interning when an alias is needed to be the result, you could end up with premature disposal... +It was all confusing on whether you are supposed to call Dispose() or not. (Exact opposite of recommended best practice +for IDisposable). + +Thus, this version of the library eliminates the confusion and complexity by use of objects that are disposable, +interfaces and a usage pattern that ensures Dispose() is idempotent and a NOP when already disposed. In the current +release no interning is performed, and instead wrapping types implement [`IEquatable`](xref:System.IEquatable`1) +to allow value equality to compare the underlying native handle and resolve them as the same underlying instance or +not. diff --git a/docfx/llvm/llvm-xref.yml b/docfx/llvm/llvm-xref.yml new file mode 100644 index 0000000000..df55569ab8 --- /dev/null +++ b/docfx/llvm/llvm-xref.yml @@ -0,0 +1,25 @@ +references: + - uid: llvm_langref + name: LLVM Language reference + href: https://releases.llvm.org/20.1.0/docs/LangRef.html + - uid: llvm_sourceleveldebugging + name: LLVM Source Level Debugging + href: https://releases.llvm.org/20.1.0/docs/SourceLevelDebugging.html + - uid: llvm_docs_garbagecollection + name: LLVM Garbage Collection + href: https://releases.llvm.org/20.1.0/docs/GarbageCollection.html + - uid: llvm_docs_passes + name: LLVM Analysis and Transform Passes + href: https://releases.llvm.org/20.1.0/docs/Passes.html + - uid: llvm_exception_handling + name: LLVM Exception Handling + href: https://releases.llvm.org/20.1.0/docs/ExceptionHandling.html + - uid: llvm_misunderstood_gep + name: LLVM The Misunderstood GEP Instruction + href: https://releases.llvm.org/20.1.0/docs/GetElementPtr.html + - uid: llvm_sourcelevel_debugging + name: LLVM Source Level Debugging + href: https://releases.llvm.org/20.1.0/docs/SourceLevelDebugging.html + - uid: llvm_kaleidoscope_tutorial + name: LLVM Kaleidoscope Tutorial + href: https://releases.llvm.org/20.1.0/docs/tutorial/index.html diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.DebugInfo.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.DebugInfo.md new file mode 100644 index 0000000000..bc751f6dc0 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.DebugInfo.md @@ -0,0 +1,18 @@ +--- +uid: Ubiquity.NET.Llvm.DebugInfo +remarks: *content +--- +This namespace contains all the support for the LLVM representation of debugging information. + +## Differences from previous release +A critical difference is that a [Module](xref:Ubiquity.NET.Llvm.Module) does NOT own a [DIBuilder](xref:Ubiquity.NET.Llvm.DebugInfo.DIBuilder) +That was a customized extension that was more accidental as a result of the former releases using object +interning. However, once that was removed it was found that Module instances were attempting to hold fields +or properties of things that were NOT part of the underlying native object. So, the pattern of use was +changed to better match how the underlying LLVM API worked. In particular a DIBuilder is a `ref struct` +that is ONLY allowed on the stack as it is intended for short term use. It can (and does) own a +[DICompileUnit](xref:Ubiquity.NET.Llvm.DebugInfo.DICompileUnit) and it can reference the module it was +created from. Since it is a `ref struct` it is not allowed to store instances of a DIBuilder in the heap in +any way. ([Kaleidoscope Chapter 9](xref:Kaleidoscope-ch9) provides a sample of use in a visitor pattern where the +instance is provided as an `in` parameter to `ref readonly` functions. This ability was added to the visitor +pattern specifically for this case.) diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Instructions.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Instructions.md new file mode 100644 index 0000000000..f17b6aa050 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Instructions.md @@ -0,0 +1,6 @@ +--- +uid: Ubiquity.NET.Llvm.Instructions +remarks: *content +--- +This namespace hosts all of the wrappers for the LLVM Instructions, extensions and the +capacity to build instruction sequences in a basic block. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Metadata.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Metadata.md new file mode 100644 index 0000000000..31abb0a62b --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Metadata.md @@ -0,0 +1,16 @@ +--- +uid: Ubiquity.NET.Llvm.Metadata +remarks: *content +--- +This namespace hosts all of the wrappers for the LLVM Metadata. The namespace contains the root +of the metadata type system [IrMetadata](xref:Ubiquity.NET.Llvm.Metadata.IrMetadata). + +>[NOTE] +> The name [IrMetadata](xref:Ubiquity.NET.Llvm.Metadata.IrMetadata) is used to help deal with a +> number of naming issues and conflicts with existing types or namespaces. +> ([CA1724](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1724) +> is not your friend on this one. The name `Metadata` conflicts with namespace +> [System.Runtime.Remoting.Metadata](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.remoting.metadata?view=netframework-4.8.1), +> which apparently is ONLY a legacy desktop framework namespace, but the tooling still complains +> about a conflict between the namespace name and a type name). To resolve this the type is named +> as closely as possible without causing the conflict. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.ObjectFile.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.ObjectFile.md new file mode 100644 index 0000000000..8cec4a51ea --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.ObjectFile.md @@ -0,0 +1,8 @@ +--- +uid: Ubiquity.NET.Llvm.ObjectFile +remarks: *content +--- +This namespace provides wrappers for the LLVM ObjectFile manipulation support. While it is +mostly used for object files, it also applies to libraries as well as LLVM IR. In LLVM terms +these are all categorixed as a [TargetBinary](xref:Ubiquity.NET.Llvm.ObjectFile.TargetBinary). + diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.OrcJITv2.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.OrcJITv2.md new file mode 100644 index 0000000000..49f8134e42 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.OrcJITv2.md @@ -0,0 +1,6 @@ +--- +uid: Ubiquity.NET.Llvm.OrcJITv2 +remarks: *content +--- +This namespace contains the support for LLVM On Request Compile (ORC) v2 JIT. This supports +early compiled as well as lazy compiled JIT execution models. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Transforms.Legacy.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Transforms.Legacy.md new file mode 100644 index 0000000000..cd08876b77 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Transforms.Legacy.md @@ -0,0 +1,9 @@ +--- +uid: Ubiquity.NET.Llvm.Transforms.Legacy +remarks: *content +--- +This namespace contains the wrappers for supporting the "legacy" pass management. This is NOT +normally used by applications as the new pass manager support is built into [Module](xref:Ubiquity.NET.Llvm.Module) +and [Function](xref:Ubiquity.NET.Llvm.Values.Function) via one of the overloads of +`TryRunPasses(...)`. Generally the legacy pass manager support is only used for final target +code generation and not exposed for LLVM-C consumption and therefore not of any real use. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Types.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Types.md new file mode 100644 index 0000000000..224e05f6c8 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Types.md @@ -0,0 +1,10 @@ +--- +uid: Ubiquity.NET.Llvm.Types +remarks: *content +--- +This namespace contains the support for native types in this library. Of particular interest +is the use of an interface [ITypeRef](xref:Ubiquity.NET.Llvm.Types.ITypeRef) for consumption +of types. This is done to allow consumption of a type with debugging information attached or +just a native LLVM type in the same manner. This is a very useful convenience for code +generation that also helps to solve the "type tracking of pointers" to support the new LLVM +opaque pointer model. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Values.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Values.md new file mode 100644 index 0000000000..1d9daee143 --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.Values.md @@ -0,0 +1,6 @@ +--- +uid: Ubiquity.NET.Llvm.Values +remarks: *content +--- +This namespace contains the support for LLVM Values. These are the real core of the LLVM +system and what applications generally work with the most. diff --git a/docfx/llvm/namespaces/Ubiquity.NET.Llvm.md b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.md new file mode 100644 index 0000000000..a4cc5b295e --- /dev/null +++ b/docfx/llvm/namespaces/Ubiquity.NET.Llvm.md @@ -0,0 +1,7 @@ +--- +uid: Ubiquity.NET.Llvm +remarks: *content +--- +This is the root namespace of all of the library support, it contains the entirety of the wrapped +projection of LLVM for .NET consumers. There are several core items in this namespace as well as +distinct child namespaces for various purposes. diff --git a/docfx/llvm/toc.yml b/docfx/llvm/toc.yml new file mode 100644 index 0000000000..0beaae145f --- /dev/null +++ b/docfx/llvm/toc.yml @@ -0,0 +1,11 @@ +# TOC (Left nav) for LLVM folder +- name: Overview + href: index.md +- name: Samples + href: articles/Samples/toc.yml +- name: Namespaces + href: api/toc.yml +- name: Release Notes + href: ReleaseNotes.md +- name: Internal Details + href: articles/InternalDetails/toc.yml diff --git a/docfx/runtime-utils/api/index.md b/docfx/runtime-utils/api/index.md new file mode 100644 index 0000000000..bea727351d --- /dev/null +++ b/docfx/runtime-utils/api/index.md @@ -0,0 +1,2 @@ +# Ubiquity.NET.Runtime.Utils +API folder index.md... \ No newline at end of file diff --git a/docfx/runtime-utils/index.md b/docfx/runtime-utils/index.md new file mode 100644 index 0000000000..1bd7d2627b --- /dev/null +++ b/docfx/runtime-utils/index.md @@ -0,0 +1,4 @@ +# About +This is a support library for implementing runtimes using LLVM + +[TODO: Provide more details...] \ No newline at end of file diff --git a/docfx/runtime-utils/toc.yml b/docfx/runtime-utils/toc.yml new file mode 100644 index 0000000000..21725efca2 --- /dev/null +++ b/docfx/runtime-utils/toc.yml @@ -0,0 +1,5 @@ +# TOC (Left nav) for LLVM folder +- name: Overview + href: index.md +- name: Namespaces + href: api/toc.yml diff --git a/docfx/templates/Ubiquity/partials/affix.tmpl.partial b/docfx/templates/Ubiquity/partials/affix.tmpl.partial deleted file mode 100644 index 43a33d0120..0000000000 --- a/docfx/templates/Ubiquity/partials/affix.tmpl.partial +++ /dev/null @@ -1,17 +0,0 @@ -{{^_disableContribution}} -
- {{#docurl}} - - {{/docurl}} - {{#sourceurl}} - - {{/sourceurl}} -
-{{/_disableContribution}} - - diff --git a/docfx/templates/Ubiquity/partials/class.header.tmpl.partial b/docfx/templates/Ubiquity/partials/class.header.tmpl.partial deleted file mode 100644 index 764fd449a3..0000000000 --- a/docfx/templates/Ubiquity/partials/class.header.tmpl.partial +++ /dev/null @@ -1,130 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - -

{{>partials/title}}

-
{{{summary}}}
-
{{{conceptual}}}
- -{{#inClass}} -
-
- -
-{{/inClass}} - -{{#derivedClasses.0}} -
-
- -
- -
-{{/derivedClasses.0}} - -{{#inheritedMembers.0}} -
-
- -
- -
-{{/inheritedMembers.0}} - -{{#implements.0}} -
-
- -
- -
-{{/implements.0}} - -
{{__global.namespace}}: {{{namespace.specName.0.value}}}
-
{{__global.assembly}}: {{assemblies.0}}.dll
- -

{{__global.syntax}}

-
-
{{syntax.content.0.value}}
-
- -{{#syntax.parameters.0}} -

{{__global.parameters}}

- -{{/syntax.parameters.0}} -{{#syntax.parameters}} - - - -{{/syntax.parameters}} -{{#syntax.parameters.0}} -
- {{{type.specName.0.value}}} - {{{id}}} -

{{{description}}}

-
-{{/syntax.parameters.0}} - -{{#syntax.return}} -

{{__global.returns}}

- - - - -
- {{{type.specName.0.value}}} -

{{{description}}}

-
-{{/syntax.return}} - -{{#syntax.typeParameters.0}} -

{{__global.typeParameters}}

- -{{/syntax.typeParameters.0}} -{{#syntax.typeParameters}} - - - -{{/syntax.typeParameters}} -{{#syntax.typeParameters.0}} -
- {{{id}}} -

{{{description}}}

-
-{{/syntax.typeParameters.0}} - -{{#remarks}} -

{{__global.remarks}}

-
{{{remarks}}}
-{{/remarks}} - -{{#example.0}} -

{{__global.examples}}

-{{/example.0}} -{{#example}} -{{{.}}} -{{/example}} diff --git a/docfx/templates/Ubiquity/partials/class.tmpl.partial b/docfx/templates/Ubiquity/partials/class.tmpl.partial deleted file mode 100644 index beb4915322..0000000000 --- a/docfx/templates/Ubiquity/partials/class.tmpl.partial +++ /dev/null @@ -1,51 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - -{{>partials/class.header}} -{{#children}} -{{#overload}} - -{{/overload}} -

{{>partials/classSubtitle}}

-{{#children.0}} - - {{/children.0}} - {{#children}} - - - - - {{/children}} - {{#children.0}} -
- - {{{summary}}}
-{{/children.0}} -{{/children}} -{{#extensionMethods.0}} -

{{__global.extensionMethods}}

-{{/extensionMethods.0}} -{{#extensionMethods}} -
- {{#definition}} - - {{/definition}} - {{^definition}} - - {{/definition}} -
-{{/extensionMethods}} -{{#seealso.0}} -

{{__global.seealso}}

-
-{{/seealso.0}} -{{#seealso}} - {{#isCref}} -
{{{type.specName.0.value}}}
- {{/isCref}} - {{^isCref}} -
{{{url}}}
- {{/isCref}} -{{/seealso}} -{{#seealso.0}} -
-{{/seealso.0}} diff --git a/docfx/templates/Ubiquity/partials/collection.tmpl.partial b/docfx/templates/Ubiquity/partials/collection.tmpl.partial deleted file mode 100644 index 97757cc24f..0000000000 --- a/docfx/templates/Ubiquity/partials/collection.tmpl.partial +++ /dev/null @@ -1,177 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - -

{{>partials/title}}

-
{{{summary}}}
-
{{{conceptual}}}
- -{{#children}} -{{#children}} -{{^_disableContribution}} -{{#docurl}} - -   - -{{/docurl}} -{{#sourceurl}} - - -{{/sourceurl}} -{{/_disableContribution}} -{{#overload}} - -{{/overload}} -

{{name.0.value}}

-
{{{summary}}}
-
{{{conceptual}}}
-

{{__global.declaration}}

-{{#syntax}} -
-
{{syntax.content.0.value}}
-
-{{#parameters.0}} -

{{__global.parameters}}

- -{{/parameters.0}} -{{#parameters}} - - - - - -{{/parameters}} -{{#parameters.0}} -
{{{type.specName.0.value}}}{{{id}}}{{{description}}}
-{{/parameters.0}} -{{#return}} -

{{__global.returns}}

- - - - - -
{{{type.specName.0.value}}}{{{description}}}
-{{/return}} -{{#typeParameters.0}} -

{{__global.typeParameters}}

- -{{/typeParameters.0}} -{{#typeParameters}} - - - - -{{/typeParameters}} -{{#typeParameters.0}} -
{{{id}}}{{{description}}}
-{{/typeParameters.0}} -{{#fieldValue}} -

{{__global.fieldValue}}

- - - - - -
{{{type.specName.0.value}}}{{{description}}}
-{{/fieldValue}} -{{#propertyValue}} -

{{__global.propertyValue}}

- - - - - -
{{{type.specName.0.value}}}{{{description}}}
-{{/propertyValue}} -{{#eventType}} -

{{__global.eventType}}

- - - - - -
{{{type.specName.0.value}}}{{{description}}}
-{{/eventType}} -{{/syntax}} -{{#overridden}} -

{{__global.overrides}}

-
-{{/overridden}} -{{#implements.0}} -

{{__global.implements}}

-{{/implements.0}} -{{#implements}} - {{#definition}} -
- {{/definition}} - {{^definition}} -
- {{/definition}} -{{/implements}} -{{#remarks}} -

{{__global.remarks}}

-
{{{remarks}}}
-{{/remarks}} -{{#example.0}} -

{{__global.examples}}

-{{/example.0}} -{{#example}} -{{{.}}} -{{/example}} -{{#exceptions.0}} -

{{__global.exceptions}}

- -{{/exceptions.0}} -{{#exceptions}} - - - - -{{/exceptions}} -{{#exceptions.0}} -
{{{type.specName.0.value}}}{{{description}}}
-{{/exceptions.0}} -{{#seealso.0}} -

{{__global.seealso}}

-
-{{/seealso.0}} -{{#seealso}} - {{#isCref}} -
{{{type.specName.0.value}}}
- {{/isCref}} - {{^isCref}} -
{{{url}}}
- {{/isCref}} -{{/seealso}} -{{#seealso.0}} -
-{{/seealso.0}} -{{/children}} -{{/children}} -{{#extensionMethods.0}} -

{{__global.extensionMethods}}

-{{/extensionMethods.0}} -{{#extensionMethods}} -
- {{#definition}} - - {{/definition}} - {{^definition}} - - {{/definition}} -
-{{/extensionMethods}} -{{#seealso.0}} -

{{__global.seealso}}

-
-{{/seealso.0}} -{{#seealso}} - {{#isCref}} -
{{{type.specName.0.value}}}
- {{/isCref}} - {{^isCref}} -
{{{url}}}
- {{/isCref}} -{{/seealso}} -{{#seealso.0}} -
-{{/seealso.0}} diff --git a/docfx/templates/Ubiquity/partials/enum.tmpl.partial b/docfx/templates/Ubiquity/partials/enum.tmpl.partial deleted file mode 100644 index c227d9e55b..0000000000 --- a/docfx/templates/Ubiquity/partials/enum.tmpl.partial +++ /dev/null @@ -1,26 +0,0 @@ -{{>partials/class.header}} -{{#children}} -

{{>partials/classSubtitle}}

- - {{#children}} - - - - - {{/children}} -
{{name.0.value}}{{{summary}}}
-{{/children}} - -{{#extensionMethods.0}} -

{{__global.extensionMethods}}

-{{/extensionMethods.0}} -{{#extensionMethods}} -
- {{#definition}} - - {{/definition}} - {{^definition}} - - {{/definition}} -
-{{/extensionMethods}} diff --git a/docfx/templates/Ubiquity/partials/footer.tmpl.partial b/docfx/templates/Ubiquity/partials/footer.tmpl.partial deleted file mode 100644 index 02930f4866..0000000000 --- a/docfx/templates/Ubiquity/partials/footer.tmpl.partial +++ /dev/null @@ -1,14 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - -
-
- -
diff --git a/docfx/templates/Ubiquity/partials/namespace.tmpl.partial b/docfx/templates/Ubiquity/partials/namespace.tmpl.partial deleted file mode 100644 index d18cb830b4..0000000000 --- a/docfx/templates/Ubiquity/partials/namespace.tmpl.partial +++ /dev/null @@ -1,17 +0,0 @@ -

{{>partials/title}}

-
{{{summary}}}
-
{{{conceptual}}}
-
{{{remarks}}}
-{{#children}} -

{{>partials/namespaceSubtitle}}

- - {{#children}} - - - - {{/children}} -
-

-

{{{summary}}}

-
-{{/children}} diff --git a/docfx/templates/Ubiquity/public/Kaleidoscope.js b/docfx/templates/Ubiquity/public/Kaleidoscope.js new file mode 100644 index 0000000000..75e8815c8a --- /dev/null +++ b/docfx/templates/Ubiquity/public/Kaleidoscope.js @@ -0,0 +1,25 @@ +/* +Simple Kaleidoscope syntax highlighting for Highlight.JS +*/ + +export function Kaleidoscope(hljs) +{ + return { + aliases: ['Kaleidoscope', 'kls'], + keywords: { + $pattern: '[A-Za-z$_][0-9A-Za-z$_]*', + keyword: 'def extern if then else for in var unary binary', + built_in: 'putchard printd' + }, + contains: [ + hljs.COMMENT('#', '\\n', { relevance: 0 }), + { + className: 'number', + variants: [ + { begin: '-?\\d+(?:[.]\\d+)?(?:[eE][-+]?\\d+(?:[.]\\d+)?)?' } + ], + relevance: 0 + } + ] + }; +} diff --git a/docfx/templates/Ubiquity/public/antlr.js b/docfx/templates/Ubiquity/public/antlr.js new file mode 100644 index 0000000000..d144309e2a --- /dev/null +++ b/docfx/templates/Ubiquity/public/antlr.js @@ -0,0 +1,61 @@ +/* +Simple antlr syntax highlighting for Highlight.JS +*/ + +export function antlr(hljs) +{ + return { + aliases: ['antlr'], + keywords: { + keyword: 'grammar fragment' + }, + contains: [ + hljs.COMMENT('//', '\\n', { relevance: 0 }), + { + scope: 'regexp', + variants: [ + { begin: '\\[', end: '\\]' } + ], + relevance: 0 + }, + { + scope: 'string', + variants: [ + { begin: '\'', end: '\'' }, + { begin: '"', end: '"' } + ], + relevance: 0 + }, + { + scope: 'keyword', + variants: [ + { begin: 'i\\d+' } + ], + relevance: 0 + }, + { + scope: 'meta', + variants: [ + { begin: '{', end: '}' }, + { begin: '{', end: '}\?' } + ], + relevance: 0 + }, + { + scope: 'number', + variants: [ + { begin: '0[xX][a-fA-F0-9]+' }, + { begin: '-?\\d+(?:[.]\\d+)?(?:[eE][-+]?\\d+(?:[.]\\d+)?)?' } + ], + relevance: 0 + }, + { + scope: 'symbol', + variants: [ + { begin: '([A-Z][\\w\\-$.]*)' } + ], + relevance: 0 + } + ] + }; +} diff --git a/docfx/templates/Ubiquity/public/main.js b/docfx/templates/Ubiquity/public/main.js new file mode 100644 index 0000000000..06990eedb5 --- /dev/null +++ b/docfx/templates/Ubiquity/public/main.js @@ -0,0 +1,11 @@ +import { Kaleidoscope } from './Kaleidoscope.js' +import { antlr } from './antlr.js' + +export default +{ + configureHljs (hljs) + { + hljs.registerLanguage('Kaleidoscope', Kaleidoscope); + hljs.registerLanguage('antlr', antlr); + }, +} diff --git a/docfx/templates/Ubiquity/readme.md b/docfx/templates/Ubiquity/readme.md index 28187cedf9..ffafb13a1a 100644 --- a/docfx/templates/Ubiquity/readme.md +++ b/docfx/templates/Ubiquity/readme.md @@ -1,21 +1,15 @@ -# Attribution - -This template is a modification of the template provided by Mathew Sachin -see: https://github.com/MathewSachin/docfx-tmpl -combined with modifications of the DocFX memberpage template - -Changes: -* Dark color theme -* Changed tags for many headers for items rendering really small due to use of h5/h6 -* Modified Enum template from member-pages to remove redundant table headers -* Modified Class template from member-pages to remove redundant headers -* Modified Collections template from default to remove redundant headers -* Fixed bug on class namespace name rendering (Was always saying System.Dynamic.ExpandoObject) -* Added Collapse region for Inherited Members list (Initially collapsed) -* Added Collapse region for Inheritance chain (Initially collapsed) -* Added Collapse region for Derived classes list (Initially collapsed) -* Added Collapse region for Implemented interfaces (Initially collapsed) -* Added llvm IR, and EBNF from highlightjs.org as those languages are not part of the docfx.vendor.js by default -* Added custom ANTLR grammar syntax highlighting as that is not part of standard grammars for highlight js -* Added custom Kaleidoscope grammar syntax highlighting as that is not part of standard grammars for highlight js +# Ubiquity DOCFX template +This template adds support to the syntx highlighting provided by [HightlightJS](https://highlightjs.readthedocs.io/en/latest/supported-languages.html). +The languages added are for ANTLR^1^ (Which seems bizarre it isn't already covered given the +esoteric nature of some of the supported languages...) and of course the `Kaleidoscope` +language, which was made up entirely for the purposes of LLVM tutorials. (No surprise that one +isn't supported) +^1^The support for ANTLR is not present. DOCFX officially documents that Highlight JS +extensibility is supported by this. It even has an example. However, that example uses +a language that is built-in to Highlight JS already. It doesn't seem to actually use +this extention point as the Kaleidoscope highlighting never happens ([Local MarkDig +based editor](https://github.com/MadsKristensen/MarkdownEditor2022) will render the +ANTLR with syntax highlighting [That seems to be using PRISM for highlighting though]. +But the final docs hosted by DOCFX do not. Thus, this functionality is currently +broken. diff --git a/docfx/templates/Ubiquity/styles/main.css b/docfx/templates/Ubiquity/styles/main.css deleted file mode 100644 index b75f20983a..0000000000 --- a/docfx/templates/Ubiquity/styles/main.css +++ /dev/null @@ -1,742 +0,0 @@ -@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css"); - -body, -html { - font-family: segoe-ui_normal,Segoe UI,Segoe,Segoe WP,Helvetica Neue,Helvetica,sans-serif; - font-size: 100%; -} - -/* Clickability fix for selector on sm devices */ -@media (min-width: 768px) and (max-width: 991px) { - article h1:first-of-type:before { - height: 0; - margin-top: 0; - } -} - -#search { - border: none; -} - -.fa-code { - font-size: 19px; -} - -.sidetoc, -body .toc, -.sidefilter, -.sidetoggle { -} - -.sidenav, -.toc-toggle { - padding: 0; -} - -.sidetoggle { - padding-bottom: 15px; -} - -/* Remove center align from Navbar and Collapsible section */ -.collapse.in, -.collapsing { - text-align: unset; -} - -article h4 { - border-bottom: none; -} - -@media (min-width: 768px) { - .sidetoc, .sidefilter { - margin-left: -15px; - } -} - -@media (max-width: 767px) { - .navbar-collapse { - text-align: center !important; - } - - .navbar-collapse li .active { - border-radius: 20px; - } -} - -/* Collapsible Sections - ------------------------------------------------------- */ -.expander:after { - font-family: 'Glyphicons Halflings'; - content: "\e260"; - margin-left: 5px; - color: grey; - font-size: small; -} - -.expander.collapsed:after { - content: "\e259"; -} - -/* Floating buttons - ------------------------------------------------------- */ -.fab { - width: 40px; - height: 40px; - text-align: center; - padding: 11px 0 0 0; - border: none; - outline: none; - color: #FFF; - border-radius: 100%; - box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); - transition:.3s; -} - -.fab-small { - width: 26px; - height: 26px; - text-align: center; - padding: 3px 0 0 0; - border: none; - outline: none; - color: #FFF; - border-radius: 100%; - box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); - transition: .3s; -} - -.fab:hover { - transform: scale(1.1); -} - -.fab + .fab { - margin-right: 15px; -} - -.contribution-panel { - z-index: 1000; - position: fixed; - right: 30px; - top: 70px; -} - -/* Bootstrap docs like sidebar - ------------------------------------------------------- */ -.affix h5 { - display: none; -} - -/* active & hover links */ -.affix ul > li > a:hover, -.affix ul > li.active > a, -.affix ul > li > a:focus { - color: #ad9017; - text-decoration: none; - background-color: transparent; - border-left-color: #ad9017; -} - -/* all active links */ -.affix ul > li.active > a, -.affix ul > li.active:hover > a, -.affix ul > li.active:focus >a { - font-weight: 700; -} - -/* nested active links */ -.affix ul ul > li.active > a, -.affix ul ul > li.active:hover > a, -.affix ul ul > li.active:focus > a { - font-weight: 500; -} - -/* all links */ -.affix ul > li > a { - border-left: 2px solid transparent; - padding: 4px 20px; - font-size: 13px; - font-weight: 400; -} - -/* nested links */ -.affix ul ul > li > a { - padding-top: 1px; - padding-bottom: 1px; - padding-left: 30px; - font-size: 12px; -} - -/* hide inactive nested list */ -.affix ul ul { - display: none; -} - -/* show active nested list */ -.affix ul > li.active > ul { - display: block; -} - -.affix > ul > li > a:before { - content: ''; -} - -.affix ul ul > li > a:before { - content: ''; -} - -/* Navbar Hamburger - ------------------------------------------------------- */ -.icon-bar { - transition: 0.4s; -} - -/* Rotate first bar */ -.change .icon-bar:nth-of-type(2) { - transform: rotate(-45deg) translate(-4px, 5px) ; -} - -/* Fade out the second bar */ -.change .icon-bar:nth-of-type(3) { - opacity: 0; -} - -/* Rotate last bar */ -.change .icon-bar:nth-of-type(4) { - transform: rotate(45deg) translate(-4px, -5px) ; -} - -/* Custom Navbar - ------------------------------------------------------- */ -.navbar-inverse { - opacity: 0.95; - border-color: #375c86; -} -.navbar-inverse .navbar-brand { - color: #E0E0E0; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #ecdbff; -} -.navbar-inverse .navbar-text { - color: #E0E0E0; -} -.navbar-inverse .navbar-nav > li > a { - color: #E0E0E0; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #58acff; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #000; - background-color: #337AB7; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #ecdbff; - background-color: #02735f; -} -.navbar-inverse .navbar-toggle { - border-color: #02735f; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #02735f; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #E0E0E0; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border: none; -} -.navbar-inverse .navbar-link { - color: #E0E0E0; -} -.navbar-inverse .navbar-link:hover { - color: #ecdbff; -} - -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #E0E0E0; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #ecdbff; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #ecdbff; - background-color: #02735f; - } -} - -/* Ubiquity layout overrides */ -.article { - margin-top: 35px; -} - -article { - margin: 1em; -} - -.sideaffix { - overflow: visible; -} - -.toc .level1 > li { - font-weight: bold; - font-size: 14px; -} - -toc .level2 { - font-weight: normal; - font-size: 12px; -} - -.sidetoggle div { - Width: 300px; -} - -/* Overrides for - Ubiquity DocFX Dark theme */ -body { - color:#ccc; - background-color: #0A0A0A; -} - -pre { - background-color:#0A0A0A; - border:1px solid #55595c; - font-size: .875rem; -} - -img { - vertical-align: middle; - background-color: #303030; -} - -.table .table { - background-color:#0A0A0A; -} - -.table-bordered, -.table-bordered>tbody>tr>td, -.table-bordered>tbody>tr>th, -.table-bordered>tfoot>tr>td, -.table-bordered>tfoot>tr>th, -.table-bordered>thead>tr>td, -.table-bordered>thead>tr>th { - border: none; - border-top: 1px solid #29393b; - border-bottom: 1px solid #29393b; -} - -.table-striped>tbody>tr:nth-of-type(odd) { - background-color:#0A0A0A; -} - -.sidenav, -.fixed_header, -.toc { - background-color: #0A0A0A; -} - -.sidetoc { - background-color: #0A0A0A; - border-left: 1px dotted #1e324d; - border-right: 1px dotted #1e324d; -} - -body .toc { - background-color: #0A0A0A; -} - -.toc .nav > li > a { - color: #ccc; -} - -.toc .nav > li > a:hover, -.toc .nav > li > a:focus { - color: #0078d7; - text-decoration: underline; -} - -.sidefilter { - background-color: #0A0A0A; - border-left: 1px dotted #1e324d; - border-right: 1px dotted #1e324d; -} - -.sideaffix > div.contribution > ul > li > a.contribution-link:hover { - background-color: #0A0A0A; -} - -.affix ul > li > a:before { - color: #FFFFFF; -} - -.affix ul > li > a:hover { - color: #eab22a;; -} - -.affix ul > li > a:hover { - color: #eab22a;; -} - -.affix ul > li > a { - color: #eab22a; -} - -.footer { - border-top: 1px solid #1e324d; - background-color: #0A0A0A; -} - -.toc li:after { - color: #eab22a; -} - -.alert-info { - color: #73d1ff; - background-color: #0c0e0f; - border-color: #bce8f1; -} - -.alert-warning { - color: #8a6d3b; - background-color: #000; - border-color: #f00; -} - -button, a { - color: #00a4f3; -} - -button:hover, -button:focus, -a:hover, -a:focus { - color: #0078d7; - text-decoration: underline; -} - -a.expander:hover { - text-decoration:none; -} - -code { - color: #4EC9B0; - background-color: #000; - border-radius: 1px; -} - -pre code { - color: #ccc -} - -svg:hover text { - fill: #58acff; -} - -svg:hover path { - fill: #58acff; -} - -/* Highlight.JS Ubiquity Dark theme */ -.hljs { - display: block; - overflow-x: auto; - background: #0A0A0A; - color: #C0C0C0; - padding: 0.5em; - -webkit-text-size-adjust:none -} - -.hljs-comment { - color:#e9d585; -} - -.hljs-quote { - color: #9c9491; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #f22c40; -} - -.hljs-meta { - color: #c0c682; -} - -.hljs-symbol -{ - color: #C0C0C0; -} - -.hljs-title, -.hljs-type, -.hljs-class { - color: #4EC9B0; -} - -.hljs-string, -.hljs-bullet { - color: #D69D85; -} - -.hljs-params -.hljs-section { - color: #C0C0C0; -} - -.hljs-built_in, -.hljs-builtin-name, -.hljs-keyword, -.hljs-selector-tag { - color: #569CD6; -} - -.hljs-number, -.hljs-literal { - color: #B5CEA8 -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-header { - color:paleturquoise; -} - -.hljs-addition { - color:forestgreen; -} - -.hljs-deletion { - color:crimson; -} - -code.lang-diff { - color:grey; -} - -/* Override docfx.vendor.css language specific hljs styles */ -.bash .hljs-shebang, -.java .hljs-javadoc, -.javascript .hljs-javadoc, -.rust .hljs-preprocessor { - color:#e9d585 -} - -.apache .hljs-sqbracket, -.c .hljs-preprocessor, -.coffeescript .hljs-regexp, -.coffeescript .hljs-subst, -.cpp .hljs-preprocessor, -.javascript .hljs-regexp, -.json .hljs-attribute, -.less .hljs-built_in, -.makefile .hljs-variable, -.markdown .hljs-blockquote, -.markdown .hljs-emphasis, -.markdown .hljs-link_label, -.markdown .hljs-strong, -.markdown .hljs-value, -.nginx .hljs-number, -.nginx .hljs-regexp, -.objectivec .hljs-preprocessor .hljs-title, -.perl .hljs-regexp, -.php .hljs-regexp,.scss .hljs-built_in, -.xml .hljs-value { - color:#bd63c5 -} - -.css .hljs-at_rule, -.css .hljs-important, -.go .hljs-typename,.haskell .hljs-type, -.http .hljs-request, -.ini .hljs-setting, -.java .hljs-javadoctag, -.javascript .hljs-javadoctag, -.javascript .hljs-tag, -.less .hljs-at_rule, -.less .hljs-tag, -.nginx .hljs-title, -.objectivec .hljs-preprocessor, -.php .hljs-phpdoc, -.scss .hljs-at_rule, -.scss .hljs-important, -.scss .hljs-tag, -.sql .hljs-built_in, -.stylus .hljs-at_rule, -.swift .hljs-preprocessor { - color:#a71d5d -} - -.apache .hljs-cbracket, -.apache .hljs-common, -.apache .hljs-keyword, -.bash .hljs-built_in, -.bash .hljs-literal, -.c .hljs-built_in, -.c .hljs-number, -.coffeescript .hljs-built_in, -.coffeescript .hljs-literal, -.coffeescript .hljs-number, -.cpp .hljs-built_in, -.cpp .hljs-number, -.cs .hljs-built_in, -.cs .hljs-number, -.css .hljs-attribute, -.css .hljs-function, -.css .hljs-hexcolor, -.css .hljs-number, -.go .hljs-built_in, -.go .hljs-constant, -.haskell .hljs-number, -.http .hljs-attribute, -.http .hljs-literal, -.java .hljs-number, -.javascript .hljs-built_in, -.javascript .hljs-literal, -.javascript .hljs-number, -.json .hljs-number, -.less .hljs-attribute, -.less .hljs-function, -.less .hljs-hexcolor, -.less .hljs-number, -.makefile .hljs-keyword, -.markdown .hljs-link_reference, -.nginx .hljs-built_in, -.objectivec .hljs-built_in, -.objectivec .hljs-literal, -.objectivec .hljs-number, -.php .hljs-literal, -.php .hljs-number, -.puppet .hljs-function, -.python .hljs-number, -.ruby .hljs-constant, -.ruby .hljs-number, -.ruby .hljs-prompt, -.ruby .hljs-subst .hljs-keyword, -.ruby .hljs-symbol, -.rust .hljs-number, -.scss .hljs-attribute, -.scss .hljs-function, -.scss .hljs-hexcolor, -.scss .hljs-number, -.scss .hljs-preprocessor, -.sql .hljs-number, -.stylus .hljs-attribute, -.stylus .hljs-hexcolor, -.stylus .hljs-number, -.stylus .hljs-params, -.swift .hljs-built_in, -.swift .hljs-number { - color:#4EC9B0 -} - -.apache .hljs-tag, -.cs .hljs-xmlDocTag, -.css .hljs-tag, -.stylus .hljs-tag, -.xml .hljs-title { - color:#63a35c -} - -.bash .hljs-variable, -.cs .hljs-preprocessor, -.cs .hljs-preprocessor .hljs-keyword, -.css .hljs-attr_selector, -.css .hljs-value, -.ini .hljs-keyword, -.ini .hljs-value, -.javascript .hljs-tag .hljs-title, -.makefile .hljs-constant, -.nginx .hljs-variable, -.scss .hljs-variable, -.xml .hljs-tag { - color:#333 -} - -.bash .hljs-title, -.c .hljs-title, -.coffeescript .hljs-title, -.cpp .hljs-title, -.cs .hljs-title, -.css .hljs-class, -.css .hljs-id, -.css .hljs-pseudo, -.diff .hljs-chunk, -.haskell .hljs-pragma, -.haskell .hljs-title, -.ini .hljs-title, -.java .hljs-title, -.javascript .hljs-title, -.less .hljs-class, -.less .hljs-id, -.less .hljs-pseudo, -.makefile .hljs-title, -.objectivec .hljs-title, -.perl .hljs-sub, -.php .hljs-title, -.puppet .hljs-title, -.python .hljs-decorator, -.python .hljs-title, -.ruby .hljs-parent, -.ruby .hljs-title, -.rust .hljs-title, -.scss .hljs-class, -.scss .hljs-id, -.scss .hljs-pseudo, -.stylus .hljs-class, -.stylus .hljs-id,.stylus .hljs-pseudo, -.stylus .hljs-title, -.swift .hljs-title, -.xml .hljs-attribute { - color:#4EC9B0 -} - -.coffeescript .hljs-attribute, -.coffeescript .hljs-reserved { - color:#1d3e81 -} - -.diff .hljs-chunk { - font-weight:700 -} - -.diff .hljs-addition { - color:#55a532; - background-color:#eaffea -} - -.diff .hljs-deletion { - color:#bd2c00; - background-color:#ffecec -} - -.markdown .hljs-link_url { - text-decoration:underline -} - diff --git a/docfx/templates/Ubiquity/styles/main.js b/docfx/templates/Ubiquity/styles/main.js deleted file mode 100644 index b4dfb2ee2d..0000000000 --- a/docfx/templates/Ubiquity/styles/main.js +++ /dev/null @@ -1,192 +0,0 @@ -// Use container fluid -var containers = $(".container"); -containers.removeClass("container"); -containers.addClass("container-fluid"); - -// Navbar Hamburger -$(function () { - $(".navbar-toggle").click(function () { - $(this).toggleClass("change"); - }) -}) - -// Select list to replace affix on small screens -$(function () { - var navItems = $(".sideaffix .level1 > li"); - - if (navItems.length == 0) { - return; - } - - var selector = $("