1+ # CMake FetchContent helper functions for update-dependency.ps1
2+
3+ function Parse-CMakeFetchContent ($filePath , $depName ) {
4+ $content = Get-Content $filePath - Raw
5+
6+ if ($depName ) {
7+ $pattern = " FetchContent_Declare\s*\(\s*$depName \s+([^)]+)\)"
8+ } else {
9+ # Find all FetchContent_Declare blocks
10+ $allMatches = [regex ]::Matches($content , " FetchContent_Declare\s*\(\s*([a-zA-Z0-9_-]+)" , ' Singleline' )
11+ if ($allMatches.Count -eq 1 ) {
12+ $depName = $allMatches [0 ].Groups[1 ].Value
13+ $pattern = " FetchContent_Declare\s*\(\s*$depName \s+([^)]+)\)"
14+ } else {
15+ throw " Multiple FetchContent declarations found. Use #DepName syntax."
16+ }
17+ }
18+
19+ $match = [regex ]::Match($content , $pattern , ' Singleline,IgnoreCase' )
20+ if (-not $match.Success ) {
21+ throw " FetchContent_Declare for '$depName ' not found in $filePath "
22+ }
23+ $block = $match.Groups [1 ].Value
24+
25+ # Look for GIT_REPOSITORY and GIT_TAG patterns specifically
26+ # Exclude matches that are in comments (lines starting with #)
27+ $repoMatch = [regex ]::Match($block , ' (?m)^\s*GIT_REPOSITORY\s+(\S+)' )
28+ $tagMatch = [regex ]::Match($block , ' (?m)^\s*GIT_TAG\s+(\S+)' )
29+
30+ $repo = if ($repoMatch.Success ) { $repoMatch.Groups [1 ].Value } else { " " }
31+ $tag = if ($tagMatch.Success ) { $tagMatch.Groups [1 ].Value } else { " " }
32+
33+ if ([string ]::IsNullOrEmpty($repo ) -or [string ]::IsNullOrEmpty($tag )) {
34+ throw " Could not parse GIT_REPOSITORY or GIT_TAG from FetchContent_Declare block"
35+ }
36+
37+ return @ { GitRepository = $repo ; GitTag = $tag ; DepName = $depName }
38+ }
39+
40+ function Find-TagForHash ($repo , $hash ) {
41+ try {
42+ $refs = git ls- remote -- tags $repo
43+ foreach ($ref in $refs ) {
44+ $commit , $tagRef = $ref -split ' \s+' , 2
45+ if ($commit -eq $hash ) {
46+ return $tagRef -replace ' ^refs/tags/' , ' '
47+ }
48+ }
49+ return $null
50+ }
51+ catch {
52+ Write-Host " Warning: Could not resolve hash $hash to tag name: $_ "
53+ return $null
54+ }
55+ }
56+
57+ function Update-CMakeFile ($filePath , $depName , $newValue ) {
58+ $content = Get-Content $filePath - Raw
59+ $fetchContent = Parse- CMakeFetchContent $filePath $depName
60+ $originalValue = $fetchContent.GitTag
61+ $repo = $fetchContent.GitRepository
62+ $wasHash = $originalValue -match ' ^[a-f0-9]{40}$'
63+
64+ if ($wasHash ) {
65+ # Convert tag to hash and add comment
66+ $newHashRefs = git ls- remote $repo " refs/tags/$newValue "
67+ if (-not $newHashRefs ) {
68+ throw " Tag $newValue not found in repository $repo "
69+ }
70+ $newHash = ($newHashRefs -split ' \s+' )[0 ]
71+ $replacement = " $newHash # $newValue "
72+
73+ # Validate ancestry: ensure old hash is reachable from new tag
74+ # Note: Skipping ancestry check for now as it requires local repository
75+ # TODO: Implement proper ancestry validation for remote repositories
76+ Write-Host " Warning: Skipping ancestry validation for hash update from $originalValue to $newValue "
77+ } else {
78+ $replacement = $newValue
79+ }
80+
81+ # Update GIT_TAG value, preserving formatting
82+ $pattern = " (FetchContent_Declare\s*\(\s*$depName \s+[^)]*GIT_TAG\s+)\S+([^#\r\n]*).*?(\s*[^)]*\))"
83+ $newContent = [regex ]::Replace($content , $pattern , " `$ {1}$replacement `$ {3}" , ' Singleline' )
84+
85+ if ($newContent -eq $content ) {
86+ throw " Failed to update GIT_TAG in $filePath - pattern may not have matched"
87+ }
88+
89+ $newContent | Out-File $filePath - NoNewline
90+
91+ # Verify the update worked
92+ $verifyContent = Parse- CMakeFetchContent $filePath $depName
93+ $expectedValue = $wasHash ? $newHash : $newValue
94+ if ($verifyContent.GitTag -notmatch [regex ]::Escape($expectedValue )) {
95+ throw " Update verification failed - read-after-write did not match expected value"
96+ }
97+ }
0 commit comments