azure-sdk-for-cpp/eng/scripts/Generate-Snippets.ps1
Ahson Khan aa728eed6d
Use code snippets from managed identity credential samples in identity readme doc and fix minor generation issues (#6020)
* Use code snippets from managed identity credential samples in identity readme doc.

* Generate readme from snippets.

* Update snippet generation script to remove unnecessary indentation and extra new line at eof.

* Update Snippet Generation doc to show a concrete example on how to run it.

* Update other repo READMEs with the generation fixes.

* Fix KeyVault Secrets sample and use the snippets in its README

* Use the added sample snippet.
2024-09-25 16:56:57 -07:00

170 lines
6.4 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# Powershell script to generate snippets from C++ source files.
# Usage: generate_snippets.ps1 <source_dir> <output_dir>
param(
[Parameter(Mandatory = $true)]
[string]$source_dir,
[Parameter(Mandatory = $true)]
[string]$output_dir,
[Parameter(Mandatory = $false)][switch]
[bool]$verify = $false
)
function ParseSnippets {
param (
[Parameter(Mandatory = $true)]
[string[]]$input_files
)
# for each file in $input_files, read the contents of the file into a string, then search the string for patterns delimited by @begin_snippet and @end_snippet.
# for each pattern found, extract the snippet name and contents, and save the snippet in a map of snippet name to snippet contents.
# then, for each snippet in the map, write the snippet contents to a file in $output_dir with the snippet name as the file name.
#$snippet_pattern = '@begin_snippet:\s+(?<snippet_name>\w+)\s+(?<snippet_contents>.*?)@end_snippet'
$snippet_map = @{}
$snippet_pattern = '@begin_snippet:\s+(?<snippet_name>\w+)\s+\n(?<snippet_contents>.*?)\s+(//|`*/)\s+@end_snippet'
foreach ($cpp_file in $input_files) {
Write-Host "Scanning source: $cpp_file"
$cpp_file_contents = Get-Content $cpp_file -Raw
$snippet_matches = [regex]::Matches($cpp_file_contents, $snippet_pattern, 'Singleline')
foreach ($snippet_match in $snippet_matches) {
Write-Host "Found snippet: $($snippet_match.Groups['snippet_name'].Value)"
$snippet_name = $snippet_match.Groups['snippet_name'].Value
$snippet_contents = $snippet_match.Groups['snippet_contents'].Value
if ($snippet_map[$snippet_name]) {
Write-Host "ERROR: Duplicate snippet name: $snippet_name"
exit 1
}
else {
$snippet_map[$snippet_name] = $snippet_contents
}
}
}
return (, $snippet_map)
}
function ProcessSnippetsInFile {
param (
[Parameter(Mandatory = $true)]
[hashtable]$snippet_map,
[Parameter(Mandatory = $true)]
[object]$output_file
)
$output_file_contents = Get-Content $output_file -Raw
$snippet_matches = [regex]::Matches($output_file_contents, '@insert_snippet:\s+(?<snippet_name>\w+)', 'Singleline')
# if there is no match, we don't need to do anything else.
if ($snippet_matches.Count -eq 0) {
return $true;
}
$original_file_contents = $output_file_contents
foreach ($snippet_match in $snippet_matches) {
$snippet_name = $snippet_match.Groups['snippet_name'].Value
Write-Host "Replacing snippet $snippet_name in file $output_file."
if (!$snippet_map[$snippet_name]) {
Write-Host "ERROR: Unknown snippet name: $snippet_name in file $output_file"
return $false
}
if ($output_file.Extension -eq '.md') {
# Remove the existing snippet text, if any.
$output_file_contents = [Regex]::Replace($output_file_contents, "<!--\s+@insert_snippet:\s+$snippet_name\s*-->\s+``````cpp.+?``````\s+", "<!-- @insert_snippet: $snippet_name -->`r`n`r`n", 'Singleline')
# Insert the snippet text.
$snippet_text = $snippet_map[$snippet_name]
# Remove leading spaces from each line, by first splitting the text into lines.
$lines = $snippet_text -split [Environment]::NewLine
# Then, find the minimum leading whitespace across all lines.
# This is done to trim the minimum amount of leading whitespace from each line while preserving the relative indentation for lines that are already indented.
# When calculating the min whitespace, we only consider lines that are not empty.
$minWhitespace = ($lines | Where-Object { $_.Trim() -ne '' } | ForEach-Object { $_.IndexOf($_.TrimStart()) }) | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum
# Trim the minimum whitespace from each line
$trimmedLines = $lines | ForEach-Object {
if ($_.Length -gt $minWhitespace) {
$_.Substring($minWhitespace)
} else {
$_
}
}
# Join the lines back into a single string
$snippet_text_clean = $trimmedLines -join [Environment]::NewLine
$output_file_contents = $output_file_contents -replace "<!--\s+@insert_snippet:\s+$snippet_name\s*-->\s+", "<!-- @insert_snippet: $snippet_name -->`r`n``````cpp`r`n$snippet_text_clean`r`n```````r`n`r`n"
}
elseif ($output_file.Extension -eq '.hpp') {
$output_file_contents = $output_file_contents -replace '@insert_snippet:\s+(?<snippet_name>\w+)', '$snippet_map[$snippet_name]'
}
elseif ($output_file.Extension -eq '.cpp') {
$output_file_contents = $output_file_contents -replace '@insert_snippet:\s+(?<snippet_name>\w+)', '$snippet_map[$snippet_name]'
}
else {
Write-Host "ERROR: Unknown file extension: $output_file"
return $false
}
}
# The Regex::Replace above inserts an extra newline at the end of the file. Remove it.
$output_file_contents = $output_file_contents -replace "`r`n\s*\Z", ""
$original_contents = $original_file_contents -replace "`r`n\s*\Z", ""
if ($verify) {
if ($output_file_contents -ne $original_contents) {
Write-Host "ERROR: Snippet contents does not match for file: $output_file."
return $false
}
}
elseif (!$verify) {
Write-Host "Writing file: $output_file"
Set-Content -Path $output_file.FullName -Value $output_file_contents
}
return $true
}
$source_dir = Resolve-Path $source_dir
$output_dir = Resolve-Path $output_dir
$input_files = Get-ChildItem -Path $source_dir -Include *.cpp, *.hpp -Recurse
# The snippet generator only processes markdown files currently.
$output_files = Get-ChildItem -Path $output_dir -Include *.md -Recurse
$snippet_map = @{}
$snippet_map = ParseSnippets($input_files)
# for each file in $output_files, read the contents of the file, searching for a string @insert_snippet: <snippet_name>. Insert the corresponding snippet from the $snippet_map
# into the file at that location and write it out.
$failed = $false
foreach ($output_file in $output_files) {
$result = ProcessSnippetsInFile -snippet_map $snippet_map -output_file $output_file
if (!$result) {
$failed = $true
}
}
if ($failed) {
Write-Host "ERROR: Snippet generation failed."
Write-Host "`r`nTo fix this error, run the following command locally:"
Write-Host "`r`n`r`n`t powershell -ExecutionPolicy Bypass -File eng/scripts/Generate-Snippets.ps1 -source_dir $source_dir -output_dir $output_dir`r`n"
Write-Host "`r`nThen, run the following command to verify the changes."
Write-Host "`r`n`r`n`t powershell -ExecutionPolicy Bypass -File eng/scripts/Generate-Snippets.ps1 -source_dir $source_dir -output_dir $output_dir -verify`r`n"
Write-Host "`r`nFinally, commit the changes and push to the remote branch."
exit 1
}