Windows: fix handling of Unicode paths in Lua
[metze/wireshark/wip.git] / tools / win-setup.ps1
1 #
2 # win-setup - Prepare a Windows development environment for building Wireshark.
3 #
4 # Copyright 2015 Gerald Combs <gerald@wireshark.org>
5 #
6 # Wireshark - Network traffic analyzer
7 # By Gerald Combs <gerald@wireshark.org>
8 # Copyright 1998 Gerald Combs
9 #
10 # SPDX-License-Identifier: GPL-2.0-or-later
11
12 #requires -version 2
13
14 # Makefile.nmake + win-setup.sh does:
15 # - verify_tools: Checks required executables. CMake does this.
16 # - clean_setup: Removes current and past lib dirs.
17 # - process_libs: calls libverify or download for each lib.
18
19 # To do:
20 # - Make this the source of truth. Keep the list of libs here.
21 # - Download everything unconditionally, at least initially.
22
23 # Bugs:
24 # - Unzipping from the shell seems to be slower than Cygwin's unzip or 7zip.
25
26 <#
27 .SYNOPSIS
28 Prepare a Windows development environment for building Wireshark.
29
30 .DESCRIPTION
31 This script downloads and extracts third-party libraries required to compile
32 Wireshark.
33
34 .PARAMETER Destination
35 Specifies the destination directory for the text files. The path must
36 contain the pattern "wireshark-*-libs".
37
38 .PARAMETER Platform
39 Target platform. One of "win64" or "win32".
40
41 .INPUTS
42 -Destination Destination directory.
43 -Platform Target platform.
44
45 .OUTPUTS
46 A set of libraries required to compile Wireshark on Windows, along with
47 their compressed archives.
48 A date stamp (current-tag.txt)
49
50 .EXAMPLE
51 C:\PS> .\tools\win-setup.ps1 -Destination C:\wireshark-master-64-libs -Platform win64
52 #>
53
54 Param(
55     [Parameter(Mandatory=$true, Position=0)]
56     [ValidateScript({$_ -like "*\wireshark-*-libs"})]
57     [String]
58     $Destination,
59
60     [Parameter(Mandatory=$true, Position=1)]
61     [ValidateSet("win32", "win64")]
62     [String]
63     $Platform
64 )
65
66 # Variables
67
68 # We create and delete files and directories. Bail out at the first sign of
69 # trouble instead of trying to catch exceptions everywhere.
70 $ErrorActionPreference = "Stop"
71
72 $Win64CurrentTag = "2018-12-28"
73 $Win32CurrentTag = "2018-12-28"
74
75 # Archive file / SHA256
76 $Win64Archives = @{
77     "AirPcap_Devpack_4_1_0_1622.zip" = "09d637f28a79b1d2ecb09f35436271a90c0f69bd0a1ee82b803abaaf63c18a69";
78     "bcg729-1.0.4-win64ws.zip" = "9a095fda4c39860d96f0c568830faa6651cd17635f68e27aa6de46c689aa0ee2";
79     "c-ares-1.14.0-win64ws.zip" = "91b1e1460bda513375910977a3410afd024575eebc528adecf3abea7814c0ef1";
80     "gnutls-3.4.11-1.35-win64ws.zip" = "defc04f430f88e0c1217b98157e38b0e9fc8b4e7ad744c6dd0c24dd17648f9f4";
81     "glib2-2.52.2-1.31-win64ws.zip" = "e19a7812db6715c632a5bbf96452ab474a4eaf0c6aaee999323ac7beb7ebe6db";
82     "kfw-3-2-2-x64-ws.zip" = "91654ffe0b6d418b369c95bc060414a90f91627e55c19a3e753803c9deb2fe9a";
83     "libgcrypt-1.8.3-win64ws.zip" = "53b1c636cb89de308ca4ea01b4990cf1deca7f6c2446189c7ff6e971137ffd76";
84     "libsmi-svn-40773-win64ws.zip" = "571fcee71d741bf847c3247d4c2e1c42388ca6a9feebe08fc0d4ce053571d15d";
85     "libssh-0.7.3-1-win64ws.zip" = "3a81b9f4a914a46f15243bbb13b6919ef1c20d4bf502c47646caeccff2cbd75c";
86     "libxml2-2.9.4-win64ws.zip" = "bb1892f5506f281d8f2b6c8be4fa8e83a9a3fb94c9160466fa59afdc5110d52e";
87     "lua-5.2.4-unicode-win64-vc14.zip" = "fdf24928931a55d1f0bdb909820d5389cbc0ba510528a16e499e33c034ca508d";
88     "lz4-1.7.5-win64ws.zip" = "dc946b68238c25cbc216901332d608d7f4b084be2d401210f74ce68b9b93207f";
89     "MaxMindDB-1.3.2-win64ws.zip" = "9025c43e9b21ff0bfbaf206b8ed96e2920ef1434107f789e4c7c0c1d8b508952";
90     "nghttp2-1.14.0-1-win64ws.zip" = "a4f15854f30b4fbb65cbf150011612e4580683dc9bbb339c632c37e414c938cb";
91     "sbc-1.3-1-win64ws.zip" = "08cef6898c421277a6582ef3225d8820f74a037cbd5b6e673a4d8f4593ce80a1";
92     "snappy-1.1.3-1-win64ws.zip" = "692a15e70f2cdeca621988a46e936d3651e7feb5176981f2656a5e913c394bcc";
93     "spandsp-0.0.6-1-win64ws.zip" = "0e46c61a5a8dca562c36e88a8962a50c1ec1a9fcf89dd05996dac5a79e454527";
94     "WinSparkle-0.5.7.zip" = "56d396ef0c4e8b0589ea74134e484376ca6459d972cd1ab1da6b9624d82e6d04";
95     "WpdPack_4_1_2.zip" = "ea799cf2f26e4afb1892938070fd2b1ca37ce5cf75fec4349247df12b784edbd";
96     "zlib-1.2.11-2-ws.zip" = "82764f71649cdc1e5467686289936ca7f632966186e2fce35df94037e4ecb596";
97 }
98
99 $Win32Archives = @{
100     "AirPcap_Devpack_4_1_0_1622.zip" = "09d637f28a79b1d2ecb09f35436271a90c0f69bd0a1ee82b803abaaf63c18a69";
101     "bcg729-1.0.4-win32ws.zip" = "b785ec78dec6bca8252130eb884bfa28c1140001dd7369a535579176de9e4271";
102     "c-ares-1.14.0-win32ws.zip" = "7355f3ad6d6ec05541c59e5b398b8dbd9a41bf6776b26d9656d8d54ecd58178e";
103     "gnutls-3.4.11-1.36-win32ws.zip" = "10cd21d25b22cfba2566c8d6f5afbbd23d0f8faceb5bc167ccbb8fbb97d6873f";
104     "glib2-2.52.2-1.34-win32ws.zip" = "28c426a7b64c1cd5b058c2f25685ddfaebca29083bd8f94fec2a8910ece6faf0";
105     "kfw-3-2-2-i386-ws-vc6.zip" = "527deb2cf1c3ba0cf743f2b9b8011a22096b54f7ce62fc7ba31b520bbac0e802";
106     "libgcrypt-1.8.3-win32ws.zip" = "409b72f2809019050cca91b9e670047c50a0752ff52999089178da54ef926393";
107     "libsmi-svn-40773-win32ws.zip" = "44bc81edfeb8948322ca365fc632e419383907c305cc922e6b74fdbb13827958";
108     "libssh-0.7.3-1-win32ws.zip" = "b02f0d318175194ac538a24c9c9fc280a0ecad69fb3afd4945c106b4b7c4fa6f";
109     "libxml2-2.9.4-win32ws.zip" = "147e521abacdd96913f7f94d1da44d59ee138e510922f3c1e03e485c8c9d8d1c";
110     "lua-5.2.4-unicode-win32-vc14.zip" = "609337fb9db817f94c7813ebceb44226a8d71a41896656ff5e8308ecd52968b5";
111     "lz4-1.7.5-win32ws.zip" = "1b2e4b509163bc5039c0694369b9e40ba27cdbf4c4c88fcd454ba6a34c79b41b";
112     "MaxMindDB-1.3.2-win32ws.zip" = "5c8b4bf3092da8fad6edb005a5283c6a74b7e115a50da010953eed77d33c11b7";
113     "nghttp2-1.14.0-1-win32ws.zip" = "939ec18c81fed2e44270dc924fad8beffe90a74300cc98360442300fb0a5c292";
114     "sbc-1.3-1-win32ws.zip" = "ad37825e9ace4b849a5442c08f1ed7e30634e6b774bba4307fb86f35f82e71ba";
115     "snappy-1.1.3-1-win32ws.zip" = "2508ef7c5d27655c356d7b86a00ac887fc178eab5df63595b8793953dae5c379";
116     "spandsp-0.0.6-1-win32ws.zip" = "3c25f2f4d641d4257ec9922f6db77346a8eed2e360e7d0e27b828ade19c4705b";
117     "WinSparkle-0.5.7.zip" = "56d396ef0c4e8b0589ea74134e484376ca6459d972cd1ab1da6b9624d82e6d04";
118     "WpdPack_4_1_2.zip" = "ea799cf2f26e4afb1892938070fd2b1ca37ce5cf75fec4349247df12b784edbd";
119     "zlib-1.2.11-2-ws.zip" = "82764f71649cdc1e5467686289936ca7f632966186e2fce35df94037e4ecb596";
120 }
121
122 # Subdirectory to extract an archive to
123 $ArchivesSubDirectory = @{
124     "AirPcap_Devpack_4_1_0_1622.zip" = "AirPcap_Devpack_4_1_0_1622";
125 }
126
127 # Plain file downloads
128
129 $Win32Files = @{
130     "npcap-0.99-r8.exe" = "c8f996c430d7d6395edf94c0a85c849d034c324bd234b74a0f6ac62e4f0f04e0";
131     "USBPcapSetup-1.2.0.4.exe" = "0a5ac30b0264e058f262e9c28e5865af7b836620ca5d68bb4bb42c9a808f7a43";
132 }
133
134 $Win64Files = @{
135     "npcap-0.99-r8.exe" = "c8f996c430d7d6395edf94c0a85c849d034c324bd234b74a0f6ac62e4f0f04e0";
136     "USBPcapSetup-1.2.0.4.exe" = "0a5ac30b0264e058f262e9c28e5865af7b836620ca5d68bb4bb42c9a808f7a43";
137 }
138
139 $Archives = $Win64Archives;
140 $Files = $Win64Files;
141 $CurrentTag = $Win64CurrentTag;
142
143 if ($Platform -eq "win32") {
144     $Archives = $Win32Archives;
145     $Files = $Win32Files;
146     $CurrentTag = $Win32CurrentTag;
147 }
148
149 $CleanupItems = @(
150     "bcg729-1.0.4-win??ws"
151     "c-ares-1.9.1-1-win??ws"
152     "c-ares-1.1*-win??ws"
153     "gnutls-3.1.22-*-win??ws"
154     "gnutls-3.2.15-*-win??ws"
155     "gnutls-3.4.11-*-win??ws"
156     "glib2-2.*-win??ws"
157     "gtk2"
158     "gtk3"
159     "json-glib-1.0.2-*-win??ws"
160     "kfw-3-2-2-final"
161     "kfw-3-2-2-i386-ws-vc6"
162     "kfw-3-2-2-x64-ws"
163     "libgcrypt-*-win??ws"
164     "libsmi-0.4.8"
165     "libsmi-svn-40773-win??ws"
166     "libssh-0.7.?-win??ws"
167     "libxml2-*-win??ws"
168     "lua5.1.4"
169     "lua5.2.?"
170     "lua5.2.?-win??"
171     "lua-5.?.?-win??-vc??"
172     "lz4-*-win??ws"
173     "MaxMindDB-1.3.2-win??ws"
174     "nghttp2-*-win??ws"
175     "portaudio_v19"
176     "portaudio_v19_2"
177     "sbc-1.3-win??ws"
178     "snappy-1.1.3-win??ws"
179     "spandsp-0.0.6-win??ws"
180     "upx301w"
181     "upx303w"
182     "user-guide"
183     "zlib-1.2.5"
184     "zlib-1.2.8"
185     "zlib-1.2.*-ws"
186     "AirPcap_Devpack_4_1_0_1622"
187     "GeoIP-1.*-win??ws"
188     "WinSparkle-0.3-44-g2c8d9d3-win??ws"
189     "WinSparkle-0.5.?"
190     "WpdPack"
191     "current-tag.txt"
192 )
193
194 [Uri] $DownloadPrefix = "https://anonsvn.wireshark.org/wireshark-$($Platform)-libs/tags/$($CurrentTag)/packages"
195 $Global:SevenZip = "7-zip-not-found"
196 $proxy = $null
197
198 # Functions
199
200 # Verifies the contents of a file against a SHA256 checksum.
201 # Returns success (0) if the file exists and verifies.
202 # Returns error (1) if the file does not exist.
203 # Returns error (2) if the integrity check fails (an error is also printed).
204 function VerifyIntegrity($filename, $hash) {
205     # Use absolute path because PS and .NET may have different working directories.
206     $filepath = Convert-Path -Path $filename -ErrorAction SilentlyContinue
207     if (-not ($filepath)) {
208         return 1
209     }
210     # may throw due to permission error, I/O error, etc.
211     try { $stream = [IO.File]::OpenRead($filepath) } catch { throw }
212
213     try {
214         $sha256 = New-Object Security.Cryptography.SHA256Managed
215         $binaryHash = $sha256.ComputeHash([IO.Stream]$stream)
216         $hexHash = ([System.BitConverter]::ToString($binaryHash) -Replace "-").ToLower()
217         $hash = $hash.ToLower()
218         if ($hexHash -ne $hash) {
219             Write-Warning "$($filename): computed checksum $hexHash did NOT match $hash"
220             return 2
221         }
222         return 0
223     } finally {
224         $stream.Close()
225     }
226 }
227
228 # Downloads a file and checks its integrity. If a corrupt file already exists,
229 # it is removed and re-downloaded. Succeeds only if the SHA256 checksum matches.
230 function DownloadFile($fileName, $checksum, [Uri] $fileUrl = $null) {
231     if ([string]::IsNullOrEmpty($fileUrl)) {
232         $fileUrl = "$DownloadPrefix/$fileName"
233     }
234     $destinationFile = "$Destination\$fileName"
235     if (Test-Path $destinationFile -PathType 'Leaf') {
236         if ((VerifyIntegrity $destinationFile $checksum) -ne 0) {
237             Write-Output "$fileName is corrupt, removing and retrying download."
238             Remove-Item $destinationFile
239         } else {
240             Write-Output "$fileName already there; not retrieving."
241             return
242         }
243     }
244
245     if (-not ($Script:proxy)) {
246         $Script:proxy = [System.Net.WebRequest]::GetSystemWebProxy()
247         $Script:proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
248     }
249
250     Write-Output "Downloading $fileUrl into $Destination"
251     $webClient = New-Object System.Net.WebClient
252     $webClient.proxy = $Script:proxy
253     $webClient.DownloadFile($fileUrl, "$destinationFile")
254     Write-Output "Verifying $destinationFile"
255     if ((VerifyIntegrity $destinationFile $checksum) -ne 0) {
256         Write-Output "Download is corrupted, aborting!"
257         exit 1
258     }
259 }
260
261 # Find 7-Zip, downloading it if necessary.
262 # If we ever add NuGet support we might be able to use
263 # https://github.com/thoemmi/7Zip4Powershell
264 function Bootstrap7Zip() {
265     $searchExes = @("7z.exe", "7za.exe")
266     $binDir = "$Destination\bin"
267
268     # First, check $env:Path.
269     foreach ($exe in $searchExes) {
270         if (Get-Command $exe -ErrorAction SilentlyContinue)  {
271             $Global:SevenZip = "$exe"
272             Write-Output "Found 7-zip on the path"
273             return
274         }
275     }
276
277     # Next, look in a few likely places.
278     $searchDirs = @(
279         "${env:ProgramFiles}\7-Zip"
280         "${env:ProgramFiles(x86)}\7-Zip"
281         "${env:ProgramW6432}\7-Zip"
282         "${env:ChocolateyInstall}\bin"
283         "${env:ChocolateyInstall}\tools"
284         "$binDir"
285     )
286
287     foreach ($dir in $searchDirs) {
288         if ($dir -ne $null -and (Test-Path $dir -PathType 'Container')) {
289             foreach ($exe in $searchExes) {
290                 if (Test-Path "$dir\$exe" -PathType 'Leaf') {
291                     $Global:SevenZip = "$dir\$exe"
292                     Write-Output "Found 7-zip at $dir\$exe"
293                     return
294                 }
295             }
296         }
297     }
298
299     # Finally, download a copy from anonsvn.
300     if ( -not (Test-Path $binDir -PathType 'Container') ) {
301         New-Item -ItemType 'Container' "$binDir" > $null
302     }
303
304     Write-Output "Unable to find 7-zip, retrieving from anonsvn into $binDir\7za.exe"
305     [Uri] $bbUrl = "https://anonsvn.wireshark.org/wireshark-win32-libs/trunk/bin/7za.exe"
306     $checksum = "77613cca716edf68b9d5bab951463ed7fade5bc0ec465b36190a76299c50f117"
307     DownloadFile "bin\7za.exe" "$checksum" "$bbUrl"
308
309     $Global:SevenZip = "$binDir\7za.exe"
310 }
311
312 function DownloadArchive($fileName, $checksum, $subDir) {
313     DownloadFile $fileName $checksum
314     # $shell = New-Object -com shell.application
315     $archiveFile = "$Destination\$fileName"
316     $archiveDir = "$Destination\$subDir"
317     if ($subDir -and -not (Test-Path $archiveDir -PathType 'Container')) {
318         New-Item -ItemType Directory -Path $archiveDir > $null
319     }
320     $activity = "Extracting into $($archiveDir)"
321     Write-Progress -Activity "$activity" -Status "Running 7z x $archiveFile ..."
322     & "$SevenZip" x "-o$archiveDir" -y "$archiveFile" 2>&1 |
323         Set-Variable -Name SevenZOut
324     $bbStatus = $LASTEXITCODE
325     Write-Progress -Activity "$activity" -Status "Done" -Completed
326     if ($bbStatus -gt 0) {
327         Write-Output $SevenZOut
328         exit 1
329     }
330 }
331
332 # On with the show
333
334 # Make sure $Destination exists and do our work there.
335 if ( -not (Test-Path $Destination -PathType 'Container') ) {
336     New-Item -ItemType 'Container' "$Destination" > $null
337 }
338
339 # CMake's file TO_NATIVE_PATH passive-aggressively omits the drive letter.
340 Set-Location "$Destination"
341 $Destination = $(Get-Item -Path ".\")
342 Write-Output "Working in $Destination"
343
344 # Check our last known state
345 $destinationTag = "INVALID"
346 $tagFile = "current_tag.txt"
347 if ((Test-Path $tagFile -PathType 'Leaf') -and -not ($Force)) {
348     $destinationTag = Get-Content $tagFile
349 }
350
351 if ($destinationTag -ne $CurrentTag) {
352     Write-Output "Tag $CurrentTag not found. Refreshing."
353     Bootstrap7Zip
354     $activity = "Removing directories"
355     foreach ($oldItem in $CleanupItems) {
356         if (Test-Path $oldItem) {
357             Write-Progress -Activity "$activity" -Status "Removing $oldItem"
358             Remove-Item -force -recurse $oldItem
359         }
360     }
361     Write-Progress -Activity "$activity" -Status "Done" -Completed
362 } else {
363     Write-Output "Tag $CurrentTag found. Skipping."
364     exit 0
365 }
366
367 # Download files
368 foreach ($item in $Files.GetEnumerator() | Sort-Object -property key) {
369     DownloadFile $item.Name $item.Value
370 }
371
372 # Download and extract archives
373 foreach ($item in $Archives.GetEnumerator() | Sort-Object -property key) {
374     $subDir = $ArchivesSubDirectory[$item.Name]
375     DownloadArchive $item.Name $item.Value $subDir
376 }
377
378 # Save our last known state
379 Set-Content -Path $tagFile -Value "$CurrentTag"