blob: 5dce821dc4e70075f43aecf9a739ee3ec9eb5466 [file] [log] [blame]
mistachkin7aa3ebe2016-02-24 21:42:03 +00001#!/usr/bin/tclsh
2#
3# This script is used to quickly test a VSIX (Visual Studio Extension) file
4# with Visual Studio 2015 on Windows.
5#
6# PREREQUISITES
7#
mistachkin74c2f062016-02-25 23:27:02 +00008# 1. This tool is Windows only.
mistachkin7aa3ebe2016-02-24 21:42:03 +00009#
mistachkin74c2f062016-02-25 23:27:02 +000010# 2. This tool must be executed with "elevated administrator" privileges.
mistachkin6ae4d842016-02-25 02:56:53 +000011#
mistachkin74c2f062016-02-25 23:27:02 +000012# 3. Tcl 8.4 and later are supported, earlier versions have not been tested.
13#
14# 4. The "sqlite-UWP-output.vsix" file is assumed to exist in the parent
mistachkin7aa3ebe2016-02-24 21:42:03 +000015# directory of the directory containing this script. The [optional] first
16# command line argument to this script may be used to specify an alternate
17# file. However, currently, the file must be compatible with both Visual
18# Studio 2015 and the Universal Windows Platform.
19#
mistachkin74c2f062016-02-25 23:27:02 +000020# 5. The "VERSION" file is assumed to exist in the parent directory of the
mistachkinc32db462016-02-25 02:49:58 +000021# directory containing this script. It must contain a version number that
22# matches the VSIX file being tested.
23#
mistachkin74c2f062016-02-25 23:27:02 +000024# 6. The temporary directory specified in the TEMP or TMP environment variables
mistachkin7aa3ebe2016-02-24 21:42:03 +000025# must refer to an existing directory writable by the current user.
26#
mistachkin74c2f062016-02-25 23:27:02 +000027# 7. The VS140COMNTOOLS environment variable must refer to the Visual Studio
mistachkin7aa3ebe2016-02-24 21:42:03 +000028# 2015 common tools directory.
29#
30# USAGE
31#
32# The first argument to this script is optional. If specified, it must be the
33# name of the VSIX file to test.
34#
35package require Tcl 8.4
36
37proc fail { {error ""} {usage false} } {
38 if {[string length $error] > 0} then {
39 puts stdout $error
40 if {!$usage} then {exit 1}
41 }
42
43 puts stdout "usage:\
44[file tail [info nameofexecutable]]\
45[file tail [info script]] \[vsixFile\]"
46
47 exit 1
48}
49
mistachkin7856c1c2016-02-25 23:22:26 +000050proc isWindows {} {
51 #
52 # NOTE: Returns non-zero only when running on Windows.
53 #
54 return [expr {[info exists ::tcl_platform(platform)] && \
55 $::tcl_platform(platform) eq "windows"}]
56}
57
58proc isAdministrator {} {
59 #
60 # NOTE: Returns non-zero only when running as "elevated administrator".
61 #
62 if {[isWindows]} then {
63 if {[catch {exec -- whoami /groups} groups] == 0} then {
64 set groups [string map [list \r\n \n] $groups]
65
66 foreach group [split $groups \n] {
67 #
68 # NOTE: Match this group line against the "well-known" SID for
69 # the "Administrators" group on Windows.
70 #
71 if {[regexp -- {\sS-1-5-32-544\s} $group]} then {
72 #
73 # NOTE: Match this group line against the attributes column
74 # sub-value that should be present when running with
75 # elevated administrator credentials.
76 #
77 if {[regexp -- {\sEnabled group(?:,|\s)} $group]} then {
78 return true
79 }
80 }
81 }
82 }
83 }
84
85 return false
86}
87
mistachkin7aa3ebe2016-02-24 21:42:03 +000088proc getEnvironmentVariable { name } {
89 #
90 # NOTE: Returns the value of the specified environment variable or an empty
91 # string for environment variables that do not exist in the current
92 # process environment.
93 #
94 return [expr {[info exists ::env($name)] ? $::env($name) : ""}]
95}
96
97proc getTemporaryPath {} {
98 #
99 # NOTE: Returns the normalized path to the first temporary directory found
100 # in the typical set of environment variables used for that purpose
101 # or an empty string to signal a failure to locate such a directory.
102 #
103 set names [list]
104
105 foreach name [list TEMP TMP] {
106 lappend names [string toupper $name] [string tolower $name] \
107 [string totitle $name]
108 }
109
110 foreach name $names {
111 set value [getEnvironmentVariable $name]
112
113 if {[string length $value] > 0} then {
114 return [file normalize $value]
115 }
116 }
117
118 return ""
119}
120
121proc appendArgs { args } {
122 #
mistachkin77b7e2a2016-02-25 08:02:16 +0000123 # NOTE: Returns all passed arguments joined together as a single string
124 # with no intervening spaces between arguments.
mistachkin7aa3ebe2016-02-24 21:42:03 +0000125 #
126 eval append result $args
127}
128
mistachkinc32db462016-02-25 02:49:58 +0000129proc readFile { fileName } {
130 #
131 # NOTE: Reads and returns the entire contents of the specified file, which
132 # may contain binary data.
133 #
134 set file_id [open $fileName RDONLY]
135 fconfigure $file_id -encoding binary -translation binary
136 set result [read $file_id]
137 close $file_id
138 return $result
139}
140
141proc writeFile { fileName data } {
142 #
143 # NOTE: Writes the entire contents of the specified file, which may contain
144 # binary data.
145 #
146 set file_id [open $fileName {WRONLY CREAT TRUNC}]
147 fconfigure $file_id -encoding binary -translation binary
148 puts -nonewline $file_id $data
149 close $file_id
150 return ""
151}
152
153proc putsAndEval { command } {
mistachkin77b7e2a2016-02-25 08:02:16 +0000154 #
155 # NOTE: Outputs a command to the standard output channel and then evaluates
156 # it in the callers context.
157 #
158 catch {
159 puts stdout [appendArgs "Running: " [lrange $command 1 end] ...\n]
160 }
161
mistachkinc32db462016-02-25 02:49:58 +0000162 return [uplevel 1 $command]
163}
164
mistachkin77b7e2a2016-02-25 08:02:16 +0000165proc isBadDirectory { directory } {
166 #
167 # NOTE: Returns non-zero if the directory is empty, does not exist, -OR- is
168 # not a directory.
169 #
170 catch {
171 puts stdout [appendArgs "Checking directory \"" $directory \"...\n]
172 }
173
174 return [expr {[string length $directory] == 0 || \
175 ![file exists $directory] || ![file isdirectory $directory]}]
176}
177
178proc isBadFile { fileName } {
179 #
180 # NOTE: Returns non-zero if the file name is empty, does not exist, -OR- is
181 # not a regular file.
182 #
183 catch {
184 puts stdout [appendArgs "Checking file \"" $fileName \"...\n]
185 }
186
187 return [expr {[string length $fileName] == 0 || \
188 ![file exists $fileName] || ![file isfile $fileName]}]
189}
190
mistachkin7aa3ebe2016-02-24 21:42:03 +0000191#
192# NOTE: This is the entry point for this script.
193#
194set script [file normalize [info script]]
195
196if {[string length $script] == 0} then {
197 fail "script file currently being evaluated is unknown" true
198}
199
mistachkin7856c1c2016-02-25 23:22:26 +0000200if {![isWindows]} then {
201 fail "this tool only works properly on Windows"
202}
203
204if {![isAdministrator]} then {
205 fail "this tool must run with \"elevated administrator\" privileges"
206}
207
mistachkin77b7e2a2016-02-25 08:02:16 +0000208set path [file normalize [file dirname $script]]
209set argc [llength $argv]; if {$argc > 1} then {fail "" true}
mistachkin7aa3ebe2016-02-24 21:42:03 +0000210
211if {$argc == 1} then {
mistachkinc32db462016-02-25 02:49:58 +0000212 set vsixFileName [lindex $argv 0]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000213} else {
mistachkin77b7e2a2016-02-25 08:02:16 +0000214 set vsixFileName [file join \
215 [file dirname $path] sqlite-UWP-output.vsix]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000216}
217
mistachkin77b7e2a2016-02-25 08:02:16 +0000218###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000219
mistachkin77b7e2a2016-02-25 08:02:16 +0000220if {[isBadFile $vsixFileName]} then {
221 fail [appendArgs \
222 "VSIX file \"" $vsixFileName "\" does not exist"]
mistachkinc32db462016-02-25 02:49:58 +0000223}
224
225set versionFileName [file join [file dirname $path] VERSION]
226
mistachkin77b7e2a2016-02-25 08:02:16 +0000227if {[isBadFile $versionFileName]} then {
228 fail [appendArgs \
229 "Version file \"" $versionFileName "\" does not exist"]
mistachkinc32db462016-02-25 02:49:58 +0000230}
231
232set projectTemplateFileName [file join $path vsixtest.vcxproj.data]
mistachkinc32db462016-02-25 02:49:58 +0000233
mistachkin77b7e2a2016-02-25 08:02:16 +0000234if {[isBadFile $projectTemplateFileName]} then {
mistachkinc32db462016-02-25 02:49:58 +0000235 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000236 "Project template file \"" $projectTemplateFileName \
237 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000238}
239
240set envVarName VS140COMNTOOLS
241set vsDirectory [getEnvironmentVariable $envVarName]
242
mistachkin77b7e2a2016-02-25 08:02:16 +0000243if {[isBadDirectory $vsDirectory]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000244 fail [appendArgs \
245 "Visual Studio 2015 directory \"" $vsDirectory \
mistachkin77b7e2a2016-02-25 08:02:16 +0000246 "\" from environment variable \"" $envVarName \
mistachkin7aa3ebe2016-02-24 21:42:03 +0000247 "\" does not exist"]
248}
249
mistachkin77b7e2a2016-02-25 08:02:16 +0000250set vsixInstaller [file join \
251 [file dirname $vsDirectory] IDE VSIXInstaller.exe]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000252
mistachkin77b7e2a2016-02-25 08:02:16 +0000253if {[isBadFile $vsixInstaller]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000254 fail [appendArgs \
255 "Visual Studio 2015 VSIX installer \"" $vsixInstaller \
256 "\" does not exist"]
257}
258
259set envVarName ProgramFiles
260set programFiles [getEnvironmentVariable $envVarName]
261
mistachkin77b7e2a2016-02-25 08:02:16 +0000262if {[isBadDirectory $programFiles]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000263 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000264 "Program Files directory \"" $programFiles \
265 "\" from environment variable \"" $envVarName \
266 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000267}
268
269set msBuild [file join $programFiles MSBuild 14.0 Bin MSBuild.exe]
270
mistachkin77b7e2a2016-02-25 08:02:16 +0000271if {[isBadFile $msBuild]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000272 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000273 "MSBuild v14.0 executable file \"" $msBuild \
274 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000275}
276
277set temporaryDirectory [getTemporaryPath]
278
mistachkin77b7e2a2016-02-25 08:02:16 +0000279if {[isBadDirectory $temporaryDirectory]} then {
280 fail [appendArgs \
281 "Temporary directory \"" $temporaryDirectory \
282 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000283}
284
mistachkin77b7e2a2016-02-25 08:02:16 +0000285###############################################################################
286
mistachkin7aa3ebe2016-02-24 21:42:03 +0000287set installLogFileName [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000288 [file rootname [file tail $vsixFileName]] \
289 -install- [pid] .log]
290
291set commands(1) [list exec [file nativename $vsixInstaller]]
292
293lappend commands(1) /quiet /norepair
294lappend commands(1) [appendArgs /logFile: $installLogFileName]
295lappend commands(1) [file nativename $vsixFileName]
296
297###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000298
299set buildLogFileName [appendArgs \
mistachkinc32db462016-02-25 02:49:58 +0000300 [file rootname [file tail $vsixFileName]] \
mistachkin78007b22016-02-24 23:25:23 +0000301 -build-%configuration%-%platform%- [pid] .log]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000302
mistachkin78007b22016-02-24 23:25:23 +0000303set commands(2) [list exec [file nativename $msBuild]]
mistachkin77b7e2a2016-02-25 08:02:16 +0000304
mistachkin78007b22016-02-24 23:25:23 +0000305lappend commands(2) [file nativename [file join $path vsixtest.sln]]
306lappend commands(2) /target:Rebuild
307lappend commands(2) /property:Configuration=%configuration%
mistachkin5dad68d2016-02-24 23:31:14 +0000308lappend commands(2) /property:Platform=%platform%
mistachkin7aa3ebe2016-02-24 21:42:03 +0000309
mistachkin78007b22016-02-24 23:25:23 +0000310lappend commands(2) [appendArgs \
mistachkin7aa3ebe2016-02-24 21:42:03 +0000311 /logger:FileLogger,Microsoft.Build.Engine\;Logfile= \
mistachkin77b7e2a2016-02-25 08:02:16 +0000312 [file nativename [file join $temporaryDirectory \
313 $buildLogFileName]] \;Verbosity=diagnostic]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000314
mistachkin77b7e2a2016-02-25 08:02:16 +0000315###############################################################################
316
317set uninstallLogFileName [appendArgs \
318 [file rootname [file tail $vsixFileName]] \
319 -uninstall- [pid] .log]
320
321set commands(3) [list exec [file nativename $vsixInstaller]]
322
323lappend commands(3) /quiet /norepair
mistachkin78007b22016-02-24 23:25:23 +0000324lappend commands(3) [appendArgs /logFile: $uninstallLogFileName]
325lappend commands(3) [appendArgs /uninstall:SQLite.UWP.2015]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000326
mistachkinc32db462016-02-25 02:49:58 +0000327###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000328
mistachkin5dad68d2016-02-24 23:31:14 +0000329if {1} then {
mistachkin77b7e2a2016-02-25 08:02:16 +0000330 catch {
331 puts stdout [appendArgs \
332 "Install log: \"" [file nativename [file join \
333 $temporaryDirectory $installLogFileName]] \"\n]
334 }
mistachkin78007b22016-02-24 23:25:23 +0000335
mistachkin77b7e2a2016-02-25 08:02:16 +0000336 catch {
337 puts stdout [appendArgs \
338 "Build logs: \"" [file nativename [file join \
339 $temporaryDirectory $buildLogFileName]] \"\n]
340 }
mistachkinc32db462016-02-25 02:49:58 +0000341
mistachkin77b7e2a2016-02-25 08:02:16 +0000342 catch {
343 puts stdout [appendArgs \
344 "Uninstall log: \"" [file nativename [file join \
345 $temporaryDirectory $uninstallLogFileName]] \"\n]
346 }
mistachkinc32db462016-02-25 02:49:58 +0000347}
348
349###############################################################################
350
351if {1} then {
mistachkin6ae4d842016-02-25 02:56:53 +0000352 putsAndEval $commands(1)
mistachkinc32db462016-02-25 02:49:58 +0000353
354 set versionNumber [string trim [readFile $versionFileName]]
355 set data [readFile $projectTemplateFileName]
356 set data [string map [list %versionNumber% $versionNumber] $data]
mistachkin77b7e2a2016-02-25 08:02:16 +0000357
358 set projectFileName [file join $path vsixtest.vcxproj]
mistachkinc32db462016-02-25 02:49:58 +0000359 writeFile $projectFileName $data
360
361 set platforms [list x86 x64 ARM]
mistachkin78007b22016-02-24 23:25:23 +0000362 set configurations [list Debug Release]
363
364 foreach platform $platforms {
365 foreach configuration $configurations {
mistachkinc32db462016-02-25 02:49:58 +0000366 putsAndEval [string map [list \
mistachkin77b7e2a2016-02-25 08:02:16 +0000367 %platform% $platform %configuration% $configuration] \
368 $commands(2)]
mistachkin78007b22016-02-24 23:25:23 +0000369 }
370 }
371
mistachkin6ae4d842016-02-25 02:56:53 +0000372 putsAndEval $commands(3)
mistachkin78007b22016-02-24 23:25:23 +0000373}