2021-02-09 05:55:11 +08:00
#! /usr/bin/env pwsh
2020-12-02 05:37:26 +08:00
[ CmdletBinding ( ) ]
Param (
[ String ] $VcpkgRoot = ''
)
2017-05-25 14:33:16 +08:00
2020-12-02 05:37:26 +08:00
if ( [ String ] :: IsNullOrEmpty ( $VcpkgRoot ) ) {
$VcpkgRoot = " ${PSScriptRoot} /.. "
2017-05-25 14:33:16 +08:00
}
$VcpkgRoot = Resolve-Path $VcpkgRoot
2020-12-02 05:37:26 +08:00
if ( -not ( Test-Path " $VcpkgRoot /.vcpkg-root " ) ) {
2017-05-25 14:33:16 +08:00
throw " Invalid vcpkg instance, did you forget -VcpkgRoot? "
}
2021-03-01 05:17:19 +08:00
class CMakeDocumentation {
[ String ] $Filename
[ String[] ] $ActualDocumentation
[ Bool ] $IsDeprecated
[ String ] $DeprecationMessage
2021-05-10 01:47:21 +08:00
[ String ] $DeprecatedByName
[ String ] $DeprecatedByPath
2021-03-01 05:17:19 +08:00
[ Bool ] $HasError
}
[ String[] ] $cmakeScriptsPorts = @ (
'vcpkg-cmake'
'vcpkg-cmake-config'
2021-04-07 01:53:41 +08:00
'vcpkg-pkgconfig-get-modules'
2021-03-01 05:17:19 +08:00
)
[ 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 ]
}
2021-02-09 05:55:11 +08:00
2021-03-01 05:17:19 +08:00
$Result -join '/'
}
2021-02-09 05:55:11 +08:00
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 (
2021-03-01 05:17:19 +08:00
[ CMakeDocumentation ] $Docs ,
[ String ] $PathToFile # something like docs/maintainers/blah.md
2021-02-09 05:55:11 +08:00
)
2021-03-01 05:17:19 +08:00
[ String[] ] $documentation = @ ( )
if ( $Docs . ActualDocumentation . Length -eq 0 )
{
throw " Invalid documentation: empty docs "
}
$documentation + = $Docs . ActualDocumentation [ 0 ] # name line
if ( $Docs . IsDeprecated )
{
2021-05-10 01:47:21 +08:00
if ( $null -eq $Docs . DeprecationMessage -or $Docs . DeprecationMessage -match '^ *$' )
2021-03-01 05:17:19 +08:00
{
2021-05-10 01:47:21 +08:00
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 ** " )
2021-03-01 05:17:19 +08:00
}
else
{
$documentation + = @ ( " " , " **This function has been deprecated $( $Docs . DeprecationMessage ) ** " )
}
}
2021-03-11 01:56:07 +08:00
$documentation + = @ ( " " , " The latest version of this document lives in the [vcpkg repo](https://github.com/Microsoft/vcpkg/blob/master/docs/ $PathToFile ). " )
2021-03-01 05:17:19 +08:00
$documentation + = $Docs . ActualDocumentation [ 1 . . $Docs . ActualDocumentation . Length ]
$relativePath = RelativeUnixPathTo $Docs . Filename $VcpkgRoot
$documentation + = @ (
2021-02-09 05:55:11 +08:00
" " ,
" ## Source " ,
2021-03-01 05:17:19 +08:00
" [ $( $relativePath -replace '_' , '\_' ) ](https://github.com/Microsoft/vcpkg/blob/master/ $relativePath ) " ,
2021-02-09 05:55:11 +08:00
" "
)
2021-03-01 05:17:19 +08:00
$documentation
2021-02-09 05:55:11 +08:00
}
2020-12-02 05:37:26 +08:00
2021-03-01 05:17:19 +08:00
function ParseCmakeDocComment
{
Param (
[ Parameter ( Mandatory ) ]
[ System.IO.FileSystemInfo ] $Filename
)
2020-12-02 05:37:26 +08:00
2021-03-01 05:17:19 +08:00
$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
}
2021-05-10 01:47:21 +08:00
elseif ( $contents [ 0 ] -match '^# DEPRECATED( BY (([^/]+/)+)(.+))?((: *)(.*))?$' )
2021-03-01 05:17:19 +08:00
{
$Docs . IsDeprecated = $True
2021-05-10 01:47:21 +08:00
$Docs . DeprecatedByPath = $matches [ 2 ]
$Docs . DeprecatedByName = $matches [ 4 ]
$Docs . DeprecationMessage = $matches [ 7 ]
2020-12-02 05:37:26 +08:00
}
[ 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' ) {
2021-03-01 05:17:19 +08:00
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. "
2020-12-02 05:37:26 +08:00
}
$inComment = $True
$endCommentRegex = " \] $( $matches [ 1 ] ) \] "
} elseif ( $_ -match $startCommentRegex ) {
2021-03-01 05:17:19 +08:00
$Docs . HasError = $True
2020-12-02 05:37:26 +08:00
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 ) {
2021-03-01 05:17:19 +08:00
$Docs . HasError = $True
2020-12-02 05:37:26 +08:00
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 ) {
2021-03-01 05:17:19 +08:00
Write-Warning " File $( $Filename . FullName ) has an unclosed comment. "
$Docs . HasError = $True
2020-12-02 05:37:26 +08:00
}
2021-07-15 03:45:18 +08:00
if ( -not [ String ] :: IsNullOrEmpty ( $contents ) )
2021-03-01 05:17:19 +08:00
{
$Docs . ActualDocumentation = $contents
2020-12-02 05:37:26 +08:00
}
2017-05-25 14:33:16 +08:00
2021-03-01 05:17:19 +08:00
$Docs
}
Get-ChildItem " $VcpkgRoot /scripts/cmake " -Filter '*.cmake' | ForEach-Object {
$docs = ParseCmakeDocComment $_
2021-07-30 00:47:35 +08:00
[ Bool ] $isInternalFunction = $_ . Name . StartsWith ( " z_vcpkg " )
2021-03-01 05:17:19 +08:00
if ( $docs . IsDeprecated -and $null -eq $docs . ActualDocumentation )
{
return
}
if ( $docs . HasError )
{
return
}
2017-05-25 14:33:16 +08:00
2021-03-01 05:17:19 +08:00
if ( $null -ne $docs . ActualDocumentation )
{
if ( $isInternalFunction )
{
$pathToFile = " maintainers/internal/ $( $_ . BaseName ) .md "
2021-02-09 05:55:11 +08:00
WriteFile `
2021-03-01 05:17:19 +08:00
-Path " $PSScriptRoot / $pathToFile " `
-Value ( FinalDocFile $docs )
2020-12-03 03:21:22 +08:00
2021-03-01 05:17:19 +08:00
$internalTableOfContents + = $docs
}
else
{
$pathToFile = " maintainers/ $( $_ . BaseName ) .md "
2021-02-09 05:55:11 +08:00
WriteFile `
2021-03-01 05:17:19 +08:00
-Path " $PSScriptRoot / $pathToFile " `
-Value ( FinalDocFile $docs $pathToFile )
2021-02-09 05:55:11 +08:00
2021-03-01 05:17:19 +08:00
$tableOfContents + = $docs
2021-02-09 05:55:11 +08:00
}
2021-03-01 05:17:19 +08:00
}
elseif ( -not $isInternalFunction )
{
2020-12-02 05:37:26 +08:00
# don't worry about undocumented internal functions
2021-03-01 05:17:19 +08:00
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 = $_
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. "
}
2017-05-25 14:33:16 +08:00
}
}
2020-12-03 03:21:22 +08:00
$portfileFunctionsContent = @ (
2021-03-01 05:17:19 +08:00
'<!-- Run regenerate.ps1 to extract scripts documentation -->' ,
2020-12-03 03:21:22 +08:00
'' ,
'# Portfile helper functions' )
2021-05-10 01:47:21 +08:00
function GetDeprecationMessage
{
Param (
[ CMakeDocumentation ] $Doc
)
2021-07-15 03:45:18 +08:00
$message = ''
2021-05-10 01:47:21 +08:00
if ( $Doc . IsDeprecated )
{
$message = " (deprecated "
if ( ! [ string ] :: IsNullOrEmpty ( $Doc . DeprecatedByName ) )
{
$message + = " , use [ $( $ ( $Doc . DeprecatedByName ) -replace '_' , '\_' ) ]( $( $Doc . DeprecatedByPath ) $( $Doc . DeprecatedByName ) .md) "
}
$message + = " ) "
}
$message
}
2021-03-01 05:17:19 +08:00
$DocsName = @ { expression = { Split-Path -LeafBase $_ . Filename } }
$tableOfContents | Sort-Object -Property $DocsName -Culture '' | ForEach-Object {
$name = Split-Path -LeafBase $_ . Filename
2021-05-10 01:47:21 +08:00
$portfileFunctionsContent + = " - [ $( $name -replace '_' , '\_' ) ]( $name .md) " + $ ( GetDeprecationMessage $_ )
2020-12-03 03:21:22 +08:00
}
2021-02-09 05:55:11 +08:00
$portfileFunctionsContent + = @ ( " " , " ## Internal Functions " , " " )
2021-03-01 05:17:19 +08:00
$internalTableOfContents | Sort-Object -Property $DocsName -Culture '' | ForEach-Object {
$name = Split-Path -LeafBase $_ . Filename
2021-05-10 01:47:21 +08:00
$portfileFunctionsContent + = " - [ $( $name -replace '_' , '\_' ) ](internal/ $name .md) " + $ ( GetDeprecationMessage $_ )
2021-02-09 05:55:11 +08:00
}
2021-03-01 05:17:19 +08:00
$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
2021-05-10 01:47:21 +08:00
$portfileFunctionsContent + = " - [ $( $name -replace '_' , '\_' ) ](ports/ $portName / $name .md) " + $ ( GetDeprecationMessage $_ )
2021-03-01 05:17:19 +08:00
}
}
2021-02-09 05:55:11 +08:00
$portfileFunctionsContent + = " " # final newline
2020-12-03 03:21:22 +08:00
2021-02-09 05:55:11 +08:00
WriteFile `
2020-12-03 03:21:22 +08:00
-Path " $PSScriptRoot /maintainers/portfile-functions.md " `
2021-02-09 05:55:11 +08:00
-Value $portfileFunctionsContent