-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathGenerate-StdLikeHeaders.ps1
226 lines (194 loc) · 8.25 KB
/
Generate-StdLikeHeaders.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<#
.SYNOPSIS
Generate std-like headers which you can use just like standard c++'s ones.
For example include <thread>.
.PARAMETER GccPath
Path to GCC. Will try to use the default one from $env:Path if not
specified.
.PARAMETER MinGWStdThreadsPath
Path to mingw-std-threads folder. Will try to use $PSScriptRoot/.. if not
specified.
.PARAMETER DestinationFolder
Destination folder where generated headers will be saved to
.PARAMETER GenerateCompilerWrapperWithFileName
If specified, will be generated a wrapper batch script for g++ which automatically
adds $DestinationFolder as an include path
.PARAMETER Interactive
Use this switch if you want to pass parameters interactively
#>
[CmdletBinding(PositionalBinding = $false)]
param (
# Path of GCC
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "NonInteractive",
HelpMessage = "Pathtof GCC. Will try to use the default one from `$env:Path if not specified.")]
[string]
$GccPath,
# Path of mingw-std-threads
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "NonInteractive",
HelpMessage = "Path to mingw-std-threads folder. Will try to use `$PSScriptRoot/.. if not specified.")]
[string]
$MinGWStdThreadsPath,
# Destination folder path
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "NonInteractive",
HelpMessage = "Destination folder where generated headers will be saved to")]
[ValidateNotNullOrEmpty()]
[string]
$DestinationFolder,
# Compiler wrapper path
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = "NonInteractive",
HelpMessage = "If specified, will generate a wrapper batch script for g++ which automatically adds `$DestinationFolder as an include path")]
[string]
$GenerateCompilerWrapperWithFileName,
# Interactive Switch
[Parameter(ParameterSetName = "Interactive")]
[switch]
$Interactive = $false
)
# Stop execution when encountering any error (includeing Write-Error command)
$ErrorActionPreference = "Stop";
# headers to be generated
$headers = @("condition_variable", "future", "latch", "mutex", "shared_mutex", "thread")
# ask for user input in interactive mode
if ($Interactive) {
Write-Host "Generate std-like headers which you can use just like standard c++'s ones."
Write-Host "Something like `"include <thread>`"."
$DestinationFolder = Read-Host -Prompt "Destination folder into which headers will be generated"
$GccPath = Read-Host -Prompt "Path to GCC, optional. Press Enter to let it be retrieved from PATH"
$MinGWStdThreadsPath = Read-Host -Prompt "Path to mingw-std-threads folder, optional. Press Enter to use default value"
$GenerateCompilerWrapperWithFileName = Read-Host "Optional path to which a wrapper batch script for g++ will be created. It will automatically use $DestinationFolder as an include path. Press Enter to skip"
}
if (-not $GccPath) {
$GccPath = "gcc"
}
# set default value of $MinGWStdThreadsPath
if (-not $MinGWStdThreadsPath) {
$scriptFilePath = $null
if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript") {
$scriptFilePath = $MyInvocation.MyCommand.Definition
}
else {
$scriptFilePath = [Environment]::GetCommandLineArgs()[0]
}
$MinGWStdThreadsPath = (Get-Item -LiteralPath $scriptFilePath).Directory.Parent.FullName
}
# Normalize paths
$GccPath = (Get-Command -Name $GccPath).Source
$MinGWStdThreadsPath = Resolve-Path -LiteralPath $MinGWStdThreadsPath
$DestinationFolder = New-Item -Path $DestinationFolder -ItemType "Directory" -Force
Write-Output "GccPath: $GccPath"
Write-Output "MinGWStdThreadsPath: $MinGWStdThreadsPath"
Write-Output "DestinationFolder: $DestinationFolder"
if ($GenerateCompilerWrapperWithFileName) {
Write-Output "GenerateCompilerWrapperWithFileName: $GenerateCompilerWrapperWithFileName"
}
# Find path of real headers
Write-Output "Retrieving system header search paths..."
$readingIncludePath = $false
# Empty array which will later store include paths
$includePaths = @()
# Launch GCC
$processStartInfo = New-Object -TypeName "System.Diagnostics.ProcessStartInfo"
$processStartInfo.FileName = $GccPath
$processStartInfo.Arguments = "-xc++ -E -v -"
$processStartInfo.RedirectStandardInput = $true
$processStartInfo.RedirectStandardOutput = $true
$processStartInfo.RedirectStandardError = $true
$processStartInfo.UseShellExecute = $false
$outputLines = @()
$gcc = New-Object -TypeName "System.Diagnostics.Process"
try {
$gcc.StartInfo = $processStartInfo
$gcc.Start() | Out-Null
$gcc.StandardInput.Close()
$gcc.WaitForExit()
$output = $gcc.StandardError.ReadToEnd()
$outputLines = $output -split "[\r\n]" |
ForEach-Object { return $_.Trim() } |
Where-Object { return $_.Length -gt 0 }
}
finally {
$gcc.StandardInput.Dispose()
$gcc.StandardOutput.Dispose()
$gcc.StandardError.Dispose()
$gcc.Dispose()
}
# Parse Output
foreach ($line in $outputLines) {
if (-not $readingIncludePath) {
if ($line -match "#include <...> search starts here:") {
$readingIncludePath = $true
}
continue
}
if ($line -match "End of search list.") {
break
}
Write-Output "Retrieved search path: $line"
$includePaths += $line
}
if ($includePaths.Count -eq 0) {
Write-Error "Error: didn't find any #inlcude <...> search paths"
}
# look for std header paths
Write-Output "Searching for standard headers..."
$stdHeaders = @()
# set a label called "nextHeader" to allow continue with outer loop
:nextHeader foreach ($header in $headers) {
# check if mingw-std-threads headers exist
$myHeader = "mingw.$header.h"
$myHeader = Join-Path -Path $MinGWStdThreadsPath -ChildPath $myHeader
if (-not (Test-Path -LiteralPath $myHeader -PathType "Leaf")) {
Write-Error "Error: mingw-std-threads header not found: $myHeader"
}
foreach ($inludePath in $includePaths) {
$fullPath = Join-Path -Path $inludePath -ChildPath $header
if (Test-Path -LiteralPath $fullPath -PathType "Leaf") {
$fullPath = (Get-Item -LiteralPath $fullPath).FullName
$stdHeaders += $fullPath
Write-Output "Found std header: $fullPath"
# if found matching header, continue with outer loop
continue nextHeader
}
}
Write-Error "Error: didn't find $header in any search paths"
}
# generate headers
Write-Output "Generating headers..."
foreach ($stdHeader in $stdHeaders) {
$headerFileName = (Get-Item -LiteralPath $stdHeader).Name
$myHeader = "mingw.$headerFileName.h"
$myHeader = Join-Path -Path $MinGWStdThreadsPath -ChildPath $myHeader
Write-Output "Generating <$headerFileName> from $myHeader and $stdHeader..."
# both two headers should already have include guards
# but we still add a #pragma once just to be safe
$content = "#pragma once`r`n"
$content += "#include `"$stdHeader`"`r`n"
$content += "#include `"$myHeader`"`r`n";
$outputFileName = Join-Path -Path $DestinationFolder -ChildPath $headerFileName
Write-Output "Writing file: $outputFileName"
# use .NET's method to output lines to avoid UTF-8 BOM
$noBomEncoding = New-Object -TypeName "System.Text.UTF8Encoding" -ArgumentList $false
[IO.File]::WriteAllText($outputFileName, $content, $noBomEncoding)
}
$message = "Successfully generated std-like headers. Use them by adding "
$message += "`"-I$DestinationFolder`" to your compiler command line parameters"
Write-Output $message
if ($GenerateCompilerWrapperWithFileName) {
$compilerFolder = Split-Path -LiteralPath $GccPath
$compiler = Join-Path -Path $compilerFolder -ChildPath "g++"
$command = "@echo off`r`n"
$command += "$compiler %* `"-I$DestinationFolder`""
$wrapper = New-Item -Path $GenerateCompilerWrapperWithFileName -ItemType "File" -Force
# use .NET's method to output lines to avoid UTF-8 BOM
$noBomEncoding = New-Object -TypeName "System.Text.UTF8Encoding" -ArgumentList $false
[IO.File]::WriteAllText($wrapper, $command, $noBomEncoding)
Write-Output "Wrapper batch script successfully generated to $wrapper"
}