blob: 7baed3af6a7e275c154ed41ef545b9e98feefe83 [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#
mistachkin6ae4d842016-02-25 02:56:53 +00008# 1. This tool must be executed with "elevated administrator" privileges.
mistachkin7aa3ebe2016-02-24 21:42:03 +00009#
mistachkin6ae4d842016-02-25 02:56:53 +000010# 2. Tcl 8.4 and later are supported, earlier versions have not been tested.
11#
12# 3. The "sqlite-UWP-output.vsix" file is assumed to exist in the parent
mistachkin7aa3ebe2016-02-24 21:42:03 +000013# directory of the directory containing this script. The [optional] first
14# command line argument to this script may be used to specify an alternate
15# file. However, currently, the file must be compatible with both Visual
16# Studio 2015 and the Universal Windows Platform.
17#
mistachkin6ae4d842016-02-25 02:56:53 +000018# 4. The "VERSION" file is assumed to exist in the parent directory of the
mistachkinc32db462016-02-25 02:49:58 +000019# directory containing this script. It must contain a version number that
20# matches the VSIX file being tested.
21#
mistachkin6ae4d842016-02-25 02:56:53 +000022# 5. The temporary directory specified in the TEMP or TMP environment variables
mistachkin7aa3ebe2016-02-24 21:42:03 +000023# must refer to an existing directory writable by the current user.
24#
mistachkin6ae4d842016-02-25 02:56:53 +000025# 6. The VS140COMNTOOLS environment variable must refer to the Visual Studio
mistachkin7aa3ebe2016-02-24 21:42:03 +000026# 2015 common tools directory.
27#
28# USAGE
29#
30# The first argument to this script is optional. If specified, it must be the
31# name of the VSIX file to test.
32#
33package require Tcl 8.4
34
35proc fail { {error ""} {usage false} } {
36 if {[string length $error] > 0} then {
37 puts stdout $error
38 if {!$usage} then {exit 1}
39 }
40
41 puts stdout "usage:\
42[file tail [info nameofexecutable]]\
43[file tail [info script]] \[vsixFile\]"
44
45 exit 1
46}
47
mistachkin7856c1c2016-02-25 23:22:26 +000048proc isWindows {} {
49 #
50 # NOTE: Returns non-zero only when running on Windows.
51 #
52 return [expr {[info exists ::tcl_platform(platform)] && \
53 $::tcl_platform(platform) eq "windows"}]
54}
55
56proc isAdministrator {} {
57 #
58 # NOTE: Returns non-zero only when running as "elevated administrator".
59 #
60 if {[isWindows]} then {
61 if {[catch {exec -- whoami /groups} groups] == 0} then {
62 set groups [string map [list \r\n \n] $groups]
63
64 foreach group [split $groups \n] {
65 #
66 # NOTE: Match this group line against the "well-known" SID for
67 # the "Administrators" group on Windows.
68 #
69 if {[regexp -- {\sS-1-5-32-544\s} $group]} then {
70 #
71 # NOTE: Match this group line against the attributes column
72 # sub-value that should be present when running with
73 # elevated administrator credentials.
74 #
75 if {[regexp -- {\sEnabled group(?:,|\s)} $group]} then {
76 return true
77 }
78 }
79 }
80 }
81 }
82
83 return false
84}
85
mistachkin7aa3ebe2016-02-24 21:42:03 +000086proc getEnvironmentVariable { name } {
87 #
88 # NOTE: Returns the value of the specified environment variable or an empty
89 # string for environment variables that do not exist in the current
90 # process environment.
91 #
92 return [expr {[info exists ::env($name)] ? $::env($name) : ""}]
93}
94
95proc getTemporaryPath {} {
96 #
97 # NOTE: Returns the normalized path to the first temporary directory found
98 # in the typical set of environment variables used for that purpose
99 # or an empty string to signal a failure to locate such a directory.
100 #
101 set names [list]
102
103 foreach name [list TEMP TMP] {
104 lappend names [string toupper $name] [string tolower $name] \
105 [string totitle $name]
106 }
107
108 foreach name $names {
109 set value [getEnvironmentVariable $name]
110
111 if {[string length $value] > 0} then {
112 return [file normalize $value]
113 }
114 }
115
116 return ""
117}
118
119proc appendArgs { args } {
120 #
mistachkin77b7e2a2016-02-25 08:02:16 +0000121 # NOTE: Returns all passed arguments joined together as a single string
122 # with no intervening spaces between arguments.
mistachkin7aa3ebe2016-02-24 21:42:03 +0000123 #
124 eval append result $args
125}
126
mistachkinc32db462016-02-25 02:49:58 +0000127proc readFile { fileName } {
128 #
129 # NOTE: Reads and returns the entire contents of the specified file, which
130 # may contain binary data.
131 #
132 set file_id [open $fileName RDONLY]
133 fconfigure $file_id -encoding binary -translation binary
134 set result [read $file_id]
135 close $file_id
136 return $result
137}
138
139proc writeFile { fileName data } {
140 #
141 # NOTE: Writes the entire contents of the specified file, which may contain
142 # binary data.
143 #
144 set file_id [open $fileName {WRONLY CREAT TRUNC}]
145 fconfigure $file_id -encoding binary -translation binary
146 puts -nonewline $file_id $data
147 close $file_id
148 return ""
149}
150
151proc putsAndEval { command } {
mistachkin77b7e2a2016-02-25 08:02:16 +0000152 #
153 # NOTE: Outputs a command to the standard output channel and then evaluates
154 # it in the callers context.
155 #
156 catch {
157 puts stdout [appendArgs "Running: " [lrange $command 1 end] ...\n]
158 }
159
mistachkinc32db462016-02-25 02:49:58 +0000160 return [uplevel 1 $command]
161}
162
mistachkin77b7e2a2016-02-25 08:02:16 +0000163proc isBadDirectory { directory } {
164 #
165 # NOTE: Returns non-zero if the directory is empty, does not exist, -OR- is
166 # not a directory.
167 #
168 catch {
169 puts stdout [appendArgs "Checking directory \"" $directory \"...\n]
170 }
171
172 return [expr {[string length $directory] == 0 || \
173 ![file exists $directory] || ![file isdirectory $directory]}]
174}
175
176proc isBadFile { fileName } {
177 #
178 # NOTE: Returns non-zero if the file name is empty, does not exist, -OR- is
179 # not a regular file.
180 #
181 catch {
182 puts stdout [appendArgs "Checking file \"" $fileName \"...\n]
183 }
184
185 return [expr {[string length $fileName] == 0 || \
186 ![file exists $fileName] || ![file isfile $fileName]}]
187}
188
mistachkin7aa3ebe2016-02-24 21:42:03 +0000189#
190# NOTE: This is the entry point for this script.
191#
192set script [file normalize [info script]]
193
194if {[string length $script] == 0} then {
195 fail "script file currently being evaluated is unknown" true
196}
197
mistachkin7856c1c2016-02-25 23:22:26 +0000198if {![isWindows]} then {
199 fail "this tool only works properly on Windows"
200}
201
202if {![isAdministrator]} then {
203 fail "this tool must run with \"elevated administrator\" privileges"
204}
205
mistachkin77b7e2a2016-02-25 08:02:16 +0000206set path [file normalize [file dirname $script]]
207set argc [llength $argv]; if {$argc > 1} then {fail "" true}
mistachkin7aa3ebe2016-02-24 21:42:03 +0000208
209if {$argc == 1} then {
mistachkinc32db462016-02-25 02:49:58 +0000210 set vsixFileName [lindex $argv 0]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000211} else {
mistachkin77b7e2a2016-02-25 08:02:16 +0000212 set vsixFileName [file join \
213 [file dirname $path] sqlite-UWP-output.vsix]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000214}
215
mistachkin77b7e2a2016-02-25 08:02:16 +0000216###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000217
mistachkin77b7e2a2016-02-25 08:02:16 +0000218if {[isBadFile $vsixFileName]} then {
219 fail [appendArgs \
220 "VSIX file \"" $vsixFileName "\" does not exist"]
mistachkinc32db462016-02-25 02:49:58 +0000221}
222
223set versionFileName [file join [file dirname $path] VERSION]
224
mistachkin77b7e2a2016-02-25 08:02:16 +0000225if {[isBadFile $versionFileName]} then {
226 fail [appendArgs \
227 "Version file \"" $versionFileName "\" does not exist"]
mistachkinc32db462016-02-25 02:49:58 +0000228}
229
230set projectTemplateFileName [file join $path vsixtest.vcxproj.data]
mistachkinc32db462016-02-25 02:49:58 +0000231
mistachkin77b7e2a2016-02-25 08:02:16 +0000232if {[isBadFile $projectTemplateFileName]} then {
mistachkinc32db462016-02-25 02:49:58 +0000233 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000234 "Project template file \"" $projectTemplateFileName \
235 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000236}
237
238set envVarName VS140COMNTOOLS
239set vsDirectory [getEnvironmentVariable $envVarName]
240
mistachkin77b7e2a2016-02-25 08:02:16 +0000241if {[isBadDirectory $vsDirectory]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000242 fail [appendArgs \
243 "Visual Studio 2015 directory \"" $vsDirectory \
mistachkin77b7e2a2016-02-25 08:02:16 +0000244 "\" from environment variable \"" $envVarName \
mistachkin7aa3ebe2016-02-24 21:42:03 +0000245 "\" does not exist"]
246}
247
mistachkin77b7e2a2016-02-25 08:02:16 +0000248set vsixInstaller [file join \
249 [file dirname $vsDirectory] IDE VSIXInstaller.exe]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000250
mistachkin77b7e2a2016-02-25 08:02:16 +0000251if {[isBadFile $vsixInstaller]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000252 fail [appendArgs \
253 "Visual Studio 2015 VSIX installer \"" $vsixInstaller \
254 "\" does not exist"]
255}
256
257set envVarName ProgramFiles
258set programFiles [getEnvironmentVariable $envVarName]
259
mistachkin77b7e2a2016-02-25 08:02:16 +0000260if {[isBadDirectory $programFiles]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000261 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000262 "Program Files directory \"" $programFiles \
263 "\" from environment variable \"" $envVarName \
264 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000265}
266
267set msBuild [file join $programFiles MSBuild 14.0 Bin MSBuild.exe]
268
mistachkin77b7e2a2016-02-25 08:02:16 +0000269if {[isBadFile $msBuild]} then {
mistachkin7aa3ebe2016-02-24 21:42:03 +0000270 fail [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000271 "MSBuild v14.0 executable file \"" $msBuild \
272 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000273}
274
275set temporaryDirectory [getTemporaryPath]
276
mistachkin77b7e2a2016-02-25 08:02:16 +0000277if {[isBadDirectory $temporaryDirectory]} then {
278 fail [appendArgs \
279 "Temporary directory \"" $temporaryDirectory \
280 "\" does not exist"]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000281}
282
mistachkin77b7e2a2016-02-25 08:02:16 +0000283###############################################################################
284
mistachkin7aa3ebe2016-02-24 21:42:03 +0000285set installLogFileName [appendArgs \
mistachkin77b7e2a2016-02-25 08:02:16 +0000286 [file rootname [file tail $vsixFileName]] \
287 -install- [pid] .log]
288
289set commands(1) [list exec [file nativename $vsixInstaller]]
290
291lappend commands(1) /quiet /norepair
292lappend commands(1) [appendArgs /logFile: $installLogFileName]
293lappend commands(1) [file nativename $vsixFileName]
294
295###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000296
297set buildLogFileName [appendArgs \
mistachkinc32db462016-02-25 02:49:58 +0000298 [file rootname [file tail $vsixFileName]] \
mistachkin78007b22016-02-24 23:25:23 +0000299 -build-%configuration%-%platform%- [pid] .log]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000300
mistachkin78007b22016-02-24 23:25:23 +0000301set commands(2) [list exec [file nativename $msBuild]]
mistachkin77b7e2a2016-02-25 08:02:16 +0000302
mistachkin78007b22016-02-24 23:25:23 +0000303lappend commands(2) [file nativename [file join $path vsixtest.sln]]
304lappend commands(2) /target:Rebuild
305lappend commands(2) /property:Configuration=%configuration%
mistachkin5dad68d2016-02-24 23:31:14 +0000306lappend commands(2) /property:Platform=%platform%
mistachkin7aa3ebe2016-02-24 21:42:03 +0000307
mistachkin78007b22016-02-24 23:25:23 +0000308lappend commands(2) [appendArgs \
mistachkin7aa3ebe2016-02-24 21:42:03 +0000309 /logger:FileLogger,Microsoft.Build.Engine\;Logfile= \
mistachkin77b7e2a2016-02-25 08:02:16 +0000310 [file nativename [file join $temporaryDirectory \
311 $buildLogFileName]] \;Verbosity=diagnostic]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000312
mistachkin77b7e2a2016-02-25 08:02:16 +0000313###############################################################################
314
315set uninstallLogFileName [appendArgs \
316 [file rootname [file tail $vsixFileName]] \
317 -uninstall- [pid] .log]
318
319set commands(3) [list exec [file nativename $vsixInstaller]]
320
321lappend commands(3) /quiet /norepair
mistachkin78007b22016-02-24 23:25:23 +0000322lappend commands(3) [appendArgs /logFile: $uninstallLogFileName]
323lappend commands(3) [appendArgs /uninstall:SQLite.UWP.2015]
mistachkin7aa3ebe2016-02-24 21:42:03 +0000324
mistachkinc32db462016-02-25 02:49:58 +0000325###############################################################################
mistachkin7aa3ebe2016-02-24 21:42:03 +0000326
mistachkin5dad68d2016-02-24 23:31:14 +0000327if {1} then {
mistachkin77b7e2a2016-02-25 08:02:16 +0000328 catch {
329 puts stdout [appendArgs \
330 "Install log: \"" [file nativename [file join \
331 $temporaryDirectory $installLogFileName]] \"\n]
332 }
mistachkin78007b22016-02-24 23:25:23 +0000333
mistachkin77b7e2a2016-02-25 08:02:16 +0000334 catch {
335 puts stdout [appendArgs \
336 "Build logs: \"" [file nativename [file join \
337 $temporaryDirectory $buildLogFileName]] \"\n]
338 }
mistachkinc32db462016-02-25 02:49:58 +0000339
mistachkin77b7e2a2016-02-25 08:02:16 +0000340 catch {
341 puts stdout [appendArgs \
342 "Uninstall log: \"" [file nativename [file join \
343 $temporaryDirectory $uninstallLogFileName]] \"\n]
344 }
mistachkinc32db462016-02-25 02:49:58 +0000345}
346
347###############################################################################
348
349if {1} then {
mistachkin6ae4d842016-02-25 02:56:53 +0000350 putsAndEval $commands(1)
mistachkinc32db462016-02-25 02:49:58 +0000351
352 set versionNumber [string trim [readFile $versionFileName]]
353 set data [readFile $projectTemplateFileName]
354 set data [string map [list %versionNumber% $versionNumber] $data]
mistachkin77b7e2a2016-02-25 08:02:16 +0000355
356 set projectFileName [file join $path vsixtest.vcxproj]
mistachkinc32db462016-02-25 02:49:58 +0000357 writeFile $projectFileName $data
358
359 set platforms [list x86 x64 ARM]
mistachkin78007b22016-02-24 23:25:23 +0000360 set configurations [list Debug Release]
361
362 foreach platform $platforms {
363 foreach configuration $configurations {
mistachkinc32db462016-02-25 02:49:58 +0000364 putsAndEval [string map [list \
mistachkin77b7e2a2016-02-25 08:02:16 +0000365 %platform% $platform %configuration% $configuration] \
366 $commands(2)]
mistachkin78007b22016-02-24 23:25:23 +0000367 }
368 }
369
mistachkin6ae4d842016-02-25 02:56:53 +0000370 putsAndEval $commands(3)
mistachkin78007b22016-02-24 23:25:23 +0000371}