mirror of
https://github.com/microsoft/vcpkg.git
synced 2024-12-28 11:21:12 +08:00
292067a61d
* [docs] Fix broken links. Add docs validation pipeline. * [docs][regenerate.ps1] Do not require a README.md in helper ports * [docs] Update validateDocs.yml to point to current main
358 lines
11 KiB
PowerShell
Executable File
358 lines
11 KiB
PowerShell
Executable File
#! /usr/bin/env pwsh
|
|
|
|
[CmdletBinding()]
|
|
Param(
|
|
[String]$VcpkgRoot = ''
|
|
)
|
|
|
|
if ([String]::IsNullOrEmpty($VcpkgRoot)) {
|
|
$VcpkgRoot = "${PSScriptRoot}/.."
|
|
}
|
|
|
|
$VcpkgRoot = Resolve-Path $VcpkgRoot
|
|
|
|
if (-not (Test-Path "$VcpkgRoot/.vcpkg-root")) {
|
|
throw "Invalid vcpkg instance, did you forget -VcpkgRoot?"
|
|
}
|
|
|
|
class CMakeDocumentation {
|
|
[String]$Filename
|
|
[String[]]$ActualDocumentation
|
|
[Bool]$IsDeprecated
|
|
[String]$DeprecationMessage
|
|
[String]$DeprecatedByName
|
|
[String]$DeprecatedByPath
|
|
[Bool]$HasError
|
|
}
|
|
|
|
[String[]]$cmakeScriptsPorts = @(
|
|
'vcpkg-cmake'
|
|
'vcpkg-gn'
|
|
'vcpkg-cmake-config'
|
|
'vcpkg-cmake-get-vars'
|
|
'vcpkg-pkgconfig-get-modules'
|
|
)
|
|
|
|
[CMakeDocumentation[]]$tableOfContents = @()
|
|
[CMakeDocumentation[]]$internalTableOfContents = @()
|
|
$portTableOfContents = [ordered]@{}
|
|
|
|
function RelativeUnixPathTo
|
|
{
|
|
Param(
|
|
[Parameter(Mandatory)]
|
|
[String]$Path,
|
|
[Parameter(Mandatory)]
|
|
[String]$Base
|
|
)
|
|
|
|
$Path = Resolve-Path -LiteralPath $Path
|
|
$Base = Resolve-Path -LiteralPath $Base
|
|
|
|
if ($IsWindows)
|
|
{
|
|
if ((Split-Path -Qualifier $Path) -ne (Split-Path -Qualifier $Base))
|
|
{
|
|
throw "It is not possible to get the relative unix path from $Base to $Path"
|
|
}
|
|
}
|
|
|
|
$Path = $Path -replace '\\','/'
|
|
$Base = $Base -replace '\\','/'
|
|
|
|
[String[]]$PathArray = $Path -split '/'
|
|
[String[]]$BaseArray = $Base -split '/'
|
|
|
|
[String[]]$Result = @()
|
|
|
|
$Idx = 0
|
|
|
|
while ($Idx -lt $PathArray.Length -and $Idx -lt $BaseArray.Length)
|
|
{
|
|
if ($PathArray[$Idx] -ne $BaseArray[$Idx])
|
|
{
|
|
break
|
|
}
|
|
++$Idx
|
|
}
|
|
|
|
for ($BaseIdx = $Idx; $BaseIdx -lt $BaseArray.Length; ++$BaseIdx)
|
|
{
|
|
$Result += '..'
|
|
}
|
|
for ($PathIdx = $Idx; $PathIdx -lt $PathArray.Length; ++$PathIdx)
|
|
{
|
|
$Result += $PathArray[$PathIdx]
|
|
}
|
|
|
|
$Result -join '/'
|
|
}
|
|
function WriteFile
|
|
{
|
|
Param(
|
|
[String[]]$Value,
|
|
[String]$Path
|
|
)
|
|
# note that we use this method of getting the utf-8 bytes in order to:
|
|
# - have no final `r`n, which happens when Set-Content does the thing automatically on Windows
|
|
# - have no BOM, which happens when one uses [System.Text.Encoding]::UTF8
|
|
[byte[]]$ValueAsBytes = (New-Object -TypeName 'System.Text.UTF8Encoding').GetBytes($Value -join "`n")
|
|
Set-Content -Path $Path -Value $ValueAsBytes -AsByteStream
|
|
}
|
|
function FinalDocFile
|
|
{
|
|
Param(
|
|
[CMakeDocumentation]$Docs,
|
|
[String]$PathToFile # something like docs/maintainers/blah.md
|
|
)
|
|
[String[]]$documentation = @()
|
|
|
|
if ($Docs.ActualDocumentation.Length -eq 0)
|
|
{
|
|
throw "Invalid documentation: empty docs"
|
|
}
|
|
|
|
$documentation += $Docs.ActualDocumentation[0] # name line
|
|
if ($Docs.IsDeprecated)
|
|
{
|
|
if ($null -eq $Docs.DeprecationMessage -or $Docs.DeprecationMessage -match '^ *$')
|
|
{
|
|
if(![string]::IsNullOrEmpty($Docs.DeprecatedByName))
|
|
{
|
|
$message = " in favor of [``$($Docs.DeprecatedByName)``]($($Docs.DeprecatedByPath)$($Docs.DeprecatedByName).md)"
|
|
$Docs.DeprecatedByPath -match '^ports/([a-z\-]+)/$' | Out-Null
|
|
$port = $matches[1]
|
|
if(![string]::IsNullOrEmpty($port))
|
|
{
|
|
$message += " from the $port port."
|
|
}
|
|
}
|
|
$documentation += @("", "**This function has been deprecated$message**")
|
|
}
|
|
else
|
|
{
|
|
$documentation += @("", "**This function has been deprecated $($Docs.DeprecationMessage)**")
|
|
}
|
|
}
|
|
$documentation += @("", "The latest version of this document lives in the [vcpkg repo](https://github.com/Microsoft/vcpkg/blob/master/docs/$PathToFile).")
|
|
|
|
$documentation += $Docs.ActualDocumentation[1..$Docs.ActualDocumentation.Length]
|
|
|
|
$relativePath = RelativeUnixPathTo $Docs.Filename $VcpkgRoot
|
|
$documentation += @(
|
|
"",
|
|
"## Source",
|
|
"[$($relativePath -replace '_','\_')](https://github.com/Microsoft/vcpkg/blob/master/$relativePath)",
|
|
""
|
|
)
|
|
|
|
$documentation
|
|
}
|
|
|
|
function ParseCmakeDocComment
|
|
{
|
|
Param(
|
|
[Parameter(Mandatory)]
|
|
[System.IO.FileSystemInfo]$Filename
|
|
)
|
|
|
|
$Docs = New-Object 'CMakeDocumentation'
|
|
$Docs.HasError = $False
|
|
$Docs.IsDeprecated = $False
|
|
$Docs.Filename = $Filename.FullName
|
|
|
|
[String[]]$contents = Get-Content $Filename
|
|
|
|
if ($contents[0] -eq '# DEPRECATED')
|
|
{
|
|
$Docs.IsDeprecated = $True
|
|
}
|
|
elseif($contents[0] -match '^# DEPRECATED( BY (([^/]+/)+)(.+))?((: *)(.*))?$')
|
|
{
|
|
$Docs.IsDeprecated = $True
|
|
$Docs.DeprecatedByPath = $matches[2]
|
|
$Docs.DeprecatedByName = $matches[4]
|
|
$Docs.DeprecationMessage = $matches[7]
|
|
}
|
|
|
|
[String]$startCommentRegex = '#\[(=*)\['
|
|
[String]$endCommentRegex = ''
|
|
[Bool]$inComment = $False
|
|
|
|
$contents = $contents | ForEach-Object {
|
|
if (-not $inComment) {
|
|
if ($_ -match "^\s*${startCommentRegex}(\.[a-z]*)?:?\s*$") {
|
|
if (-not [String]::IsNullOrEmpty($matches[2]) -and $matches[2] -ne '.md') {
|
|
Write-Warning "The documentation in $($Filename.FullName) doesn't seem to be markdown (extension: $($matches[2])). Only markdown is supported; please rewrite the documentation in markdown."
|
|
}
|
|
$inComment = $True
|
|
$endCommentRegex = "\]$($matches[1])\]"
|
|
} elseif ($_ -match $startCommentRegex) {
|
|
$Docs.HasError = $True
|
|
Write-Warning "Invalid start of comment -- the comment start must be at the beginning of the line.
|
|
(on line: `"$_`")"
|
|
} else {
|
|
# do nothing -- we're outside a comment, so cmake code
|
|
}
|
|
} else {
|
|
if ($_ -match "^\s*#?${endCommentRegex}\s*$") {
|
|
$inComment = $False
|
|
$endCommentRegex = ''
|
|
} elseif ($_ -match $endCommentRegex) {
|
|
$Docs.HasError = $True
|
|
Write-Warning "Invalid end of comment -- the comment end must be on it's own on a line.
|
|
(on line: `"$_`")"
|
|
} else {
|
|
# regular documentation line
|
|
$_
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($inComment) {
|
|
Write-Warning "File $($Filename.FullName) has an unclosed comment."
|
|
$Docs.HasError = $True
|
|
}
|
|
|
|
if (-not [String]::IsNullOrEmpty($contents))
|
|
{
|
|
$Docs.ActualDocumentation = $contents
|
|
}
|
|
|
|
$Docs
|
|
}
|
|
|
|
Get-ChildItem "$VcpkgRoot/scripts/cmake" -Filter '*.cmake' | ForEach-Object {
|
|
$docs = ParseCmakeDocComment $_
|
|
[Bool]$isInternalFunction = $_.Name.StartsWith("z_vcpkg")
|
|
|
|
if ($docs.IsDeprecated -and $null -eq $docs.ActualDocumentation)
|
|
{
|
|
return
|
|
}
|
|
if ($docs.HasError)
|
|
{
|
|
return
|
|
}
|
|
|
|
if ($null -ne $docs.ActualDocumentation)
|
|
{
|
|
if ($isInternalFunction)
|
|
{
|
|
$pathToFile = "maintainers/internal/$($_.BaseName).md"
|
|
WriteFile `
|
|
-Path "$PSScriptRoot/$pathToFile" `
|
|
-Value (FinalDocFile $docs)
|
|
|
|
$internalTableOfContents += $docs
|
|
}
|
|
else
|
|
{
|
|
$pathToFile = "maintainers/$($_.BaseName).md"
|
|
WriteFile `
|
|
-Path "$PSScriptRoot/$pathToFile" `
|
|
-Value (FinalDocFile $docs $pathToFile)
|
|
|
|
$tableOfContents += $docs
|
|
}
|
|
}
|
|
elseif (-not $isInternalFunction)
|
|
{
|
|
# don't worry about undocumented internal functions
|
|
Write-Warning "The cmake function in file $($_.FullName) doesn't seem to have any documentation. Make sure the documentation comments are correctly written."
|
|
}
|
|
}
|
|
|
|
$cmakeScriptsPorts | ForEach-Object {
|
|
$portName = $_
|
|
|
|
if (Test-Path "$VcpkgRoot/ports/$portName/README.md") {
|
|
Copy-Item "$VcpkgRoot/ports/$portName/README.md" "$PSScriptRoot/maintainers/ports/$portName.md"
|
|
}
|
|
New-Item -Path "$PSScriptRoot/maintainers/ports/$portName" -Force -ItemType 'Directory' | Out-Null
|
|
|
|
$portTableOfContents[$portName] = @()
|
|
|
|
Get-ChildItem "$VcpkgRoot/ports/$portName" -Filter '*.cmake' | ForEach-Object {
|
|
if ($_.Name -eq 'vcpkg-port-config.cmake' -or $_.Name -eq 'portfile.cmake')
|
|
{
|
|
return
|
|
}
|
|
|
|
$docs = ParseCmakeDocComment $_
|
|
|
|
if ($docs.IsDeprecated -and $null -eq $docs.ActualDocumentation)
|
|
{
|
|
return
|
|
}
|
|
if ($docs.HasError)
|
|
{
|
|
return
|
|
}
|
|
|
|
if ($null -ne $docs.ActualDocumentation)
|
|
{
|
|
$pathToFile = "maintainers/ports/$portName/$($_.BaseName).md"
|
|
WriteFile `
|
|
-Path "$PSScriptRoot/$pathToFile" `
|
|
-Value (FinalDocFile $docs $pathToFile)
|
|
$portTableOfContents[$portName] += $docs
|
|
}
|
|
else
|
|
{
|
|
Write-Warning "The cmake function in file $($_.FullName) doesn't seem to have any documentation. Make sure the documentation comments are correctly written."
|
|
}
|
|
}
|
|
}
|
|
|
|
$portfileFunctionsContent = @(
|
|
'<!-- Run regenerate.ps1 to extract scripts documentation -->',
|
|
'',
|
|
'# Portfile helper functions')
|
|
|
|
function GetDeprecationMessage
|
|
{
|
|
Param(
|
|
[CMakeDocumentation]$Doc
|
|
)
|
|
$message = ''
|
|
if ($Doc.IsDeprecated)
|
|
{
|
|
$message = " (deprecated"
|
|
if(![string]::IsNullOrEmpty($Doc.DeprecatedByName))
|
|
{
|
|
$message += ", use [$($($Doc.DeprecatedByName) -replace '_','\_')]($($Doc.DeprecatedByPath)$($Doc.DeprecatedByName).md)"
|
|
}
|
|
$message += ")"
|
|
}
|
|
$message
|
|
}
|
|
|
|
$DocsName = @{ expression = { Split-Path -LeafBase $_.Filename } }
|
|
$tableOfContents | Sort-Object -Property $DocsName -Culture '' | ForEach-Object {
|
|
$name = Split-Path -LeafBase $_.Filename
|
|
$portfileFunctionsContent += "- [$($name -replace '_','\_')]($name.md)" + $(GetDeprecationMessage $_)
|
|
}
|
|
$portfileFunctionsContent += @("", "## Internal Functions", "")
|
|
$internalTableOfContents | Sort-Object -Property $DocsName -Culture '' | ForEach-Object {
|
|
$name = Split-Path -LeafBase $_.Filename
|
|
$portfileFunctionsContent += "- [$($name -replace '_','\_')](internal/$name.md)" + $(GetDeprecationMessage $_)
|
|
}
|
|
|
|
$portfileFunctionsContent += @("", "## Scripts from Ports")
|
|
$portTableOfContents.GetEnumerator() | ForEach-Object {
|
|
$portName = $_.Name
|
|
$cmakeDocs = $_.Value
|
|
$portfileFunctionsContent += @("", "### [$portName](ports/$portName.md)", "")
|
|
$cmakeDocs | ForEach-Object {
|
|
$name = Split-Path -LeafBase $_.Filename
|
|
$portfileFunctionsContent += "- [$($name -replace '_','\_')](ports/$portName/$name.md)" + $(GetDeprecationMessage $_)
|
|
}
|
|
}
|
|
|
|
$portfileFunctionsContent += "" # final newline
|
|
|
|
WriteFile `
|
|
-Path "$PSScriptRoot/maintainers/portfile-functions.md" `
|
|
-Value $portfileFunctionsContent
|