3 # This script is used to quickly test a VSIX (Visual Studio Extension) file
4 # with Visual Studio 2015 on Windows.
8 # 1. This tool is Windows only.
10 # 2. This tool must be executed with "elevated administrator" privileges.
12 # 3. Tcl 8.4 and later are supported, earlier versions have not been tested.
14 # 4. The "sqlite-UWP-output.vsix" file is assumed to exist in the parent
15 # 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.
20 # 5. The "VERSION" file is assumed to exist in the parent directory of the
21 # directory containing this script. It must contain a version number that
22 # matches the VSIX file being tested.
24 # 6. The temporary directory specified in the TEMP or TMP environment variables
25 # must refer to an existing directory writable by the current user.
27 # 7. The VS140COMNTOOLS environment variable must refer to the Visual Studio
28 # 2015 common tools directory.
32 # The first argument to this script is optional. If specified, it must be the
33 # name of the VSIX file to test.
35 package require
Tcl 8.4
37 proc fail
{ {error ""} {usage false
} } {
38 if {[string length
$error] > 0} then
{
40 if {!$usage} then
{exit 1}
44 [file tail [info nameofexecutable]]\
45 [file tail [info script]] \[vsixFile\]"
52 # NOTE: Returns non-zero only when running on Windows.
54 return [expr {[info exists
::tcl_platform(platform
)] && \
55 $::tcl_platform(platform
) eq
"windows"}]
58 proc isAdministrator
{} {
60 # NOTE: Returns non-zero only when running as "elevated administrator".
62 if {[isWindows
]} then
{
63 if {[catch {exec -- whoami
/groups
} groups
] == 0} then
{
64 set groups
[string map
[list \r\n \n] $groups]
66 foreach group
[split $groups \n] {
68 # NOTE: Match this group line against the "well-known" SID for
69 # the "Administrators" group on Windows.
71 if {[regexp -- {\sS-1-5-32-544
\s
} $group]} then
{
73 # NOTE: Match this group line against the attributes column
74 # sub-value that should be present when running with
75 # elevated administrator credentials.
77 if {[regexp -- {\sEnabled group
(?
:,|
\s
)} $group]} then
{
88 proc getEnvironmentVariable
{ name
} {
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.
94 return [expr {[info exists
::env($name)] ?
$::env($name) : ""}]
97 proc getTemporaryPath
{} {
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.
105 foreach name
[list TEMP TMP
] {
106 lappend names
[string toupper
$name] [string tolower
$name] \
107 [string totitle
$name]
110 foreach name
$names {
111 set value
[getEnvironmentVariable
$name]
113 if {[string length
$value] > 0} then
{
114 return [file normalize
$value]
121 proc appendArgs
{ args
} {
123 # NOTE: Returns all passed arguments joined together as a single string
124 # with no intervening spaces between arguments.
126 eval append result
$args
129 proc readFile
{ fileName
} {
131 # NOTE: Reads and returns the entire contents of the specified file, which
132 # may contain binary data.
134 set file_id
[open $fileName RDONLY
]
135 fconfigure $file_id -encoding binary -translation binary
136 set result
[read $file_id]
141 proc writeFile
{ fileName data
} {
143 # NOTE: Writes the entire contents of the specified file, which may contain
146 set file_id
[open $fileName {WRONLY CREAT TRUNC
}]
147 fconfigure $file_id -encoding binary -translation binary
148 puts -nonewline $file_id $data
153 proc putsAndEval
{ command
} {
155 # NOTE: Outputs a command to the standard output channel and then evaluates
156 # it in the callers context.
159 puts stdout
[appendArgs
"Running: " [lrange $command 1 end
] ...
\n]
162 return [uplevel 1 $command]
165 proc isBadDirectory
{ directory
} {
167 # NOTE: Returns non-zero if the directory is empty, does not exist, -OR- is
171 puts stdout
[appendArgs
"Checking directory \"" $directory \"...
\n]
174 return [expr {[string length
$directory] == 0 ||
\
175 ![file exists
$directory] ||
![file isdirectory
$directory]}]
178 proc isBadFile
{ fileName
} {
180 # NOTE: Returns non-zero if the file name is empty, does not exist, -OR- is
181 # not a regular file.
184 puts stdout
[appendArgs
"Checking file \"" $fileName \"...
\n]
187 return [expr {[string length
$fileName] == 0 ||
\
188 ![file exists
$fileName] ||
![file isfile
$fileName]}]
192 # NOTE: This is the entry point for this script.
194 set script
[file normalize
[info script
]]
196 if {[string length
$script] == 0} then
{
197 fail
"script file currently being evaluated is unknown" true
200 if {![isWindows
]} then
{
201 fail
"this tool only works properly on Windows"
204 if {![isAdministrator
]} then
{
205 fail
"this tool must run with \"elevated administrator\" privileges"
208 set path
[file normalize
[file dirname
$script]]
209 set argc
[llength $argv]; if {$argc > 1} then
{fail
"" true
}
211 if {$argc == 1} then
{
212 set vsixFileName
[lindex $argv 0]
214 set vsixFileName
[file join \
215 [file dirname
$path] sqlite-UWP-output.vsix
]
218 ###############################################################################
220 if {[isBadFile
$vsixFileName]} then
{
222 "VSIX file \"" $vsixFileName "\" does not exist"]
225 set versionFileName
[file join [file dirname
$path] VERSION
]
227 if {[isBadFile
$versionFileName]} then
{
229 "Version file \"" $versionFileName "\" does not exist"]
232 set projectTemplateFileName
[file join $path vsixtest.vcxproj.data
]
234 if {[isBadFile
$projectTemplateFileName]} then
{
236 "Project template file \"" $projectTemplateFileName \
240 set envVarName VS140COMNTOOLS
241 set vsDirectory
[getEnvironmentVariable
$envVarName]
243 if {[isBadDirectory
$vsDirectory]} then
{
245 "Visual Studio 2015 directory \"" $vsDirectory \
246 "\" from environment variable \"" $envVarName \
250 set vsixInstaller
[file join \
251 [file dirname
$vsDirectory] IDE VSIXInstaller.exe
]
253 if {[isBadFile
$vsixInstaller]} then
{
255 "Visual Studio 2015 VSIX installer \"" $vsixInstaller \
259 set envVarName ProgramFiles
260 set programFiles
[getEnvironmentVariable
$envVarName]
262 if {[isBadDirectory
$programFiles]} then
{
264 "Program Files directory \"" $programFiles \
265 "\" from environment variable \"" $envVarName \
269 set msBuild
[file join $programFiles MSBuild
14.0 Bin MSBuild.exe
]
271 if {[isBadFile
$msBuild]} then
{
273 "MSBuild v14.0 executable file \"" $msBuild \
277 set temporaryDirectory
[getTemporaryPath
]
279 if {[isBadDirectory
$temporaryDirectory]} then
{
281 "Temporary directory \"" $temporaryDirectory \
285 ###############################################################################
287 set installLogFileName
[appendArgs
\
288 [file rootname
[file tail
$vsixFileName]] \
289 -install- [pid] .log
]
291 set commands
(1) [list exec [file nativename
$vsixInstaller]]
293 lappend commands
(1) /quiet
/norepair
294 lappend commands
(1) [appendArgs
/logFile
: $installLogFileName]
295 lappend commands
(1) [file nativename
$vsixFileName]
297 ###############################################################################
299 set buildLogFileName
[appendArgs
\
300 [file rootname
[file tail
$vsixFileName]] \
301 -build-%configuration
%-%platform
%- [pid] .log
]
303 set commands
(2) [list exec [file nativename
$msBuild]]
305 lappend commands
(2) [file nativename
[file join $path vsixtest.sln
]]
306 lappend commands
(2) /target
:Rebuild
307 lappend commands
(2) /property
:Configuration
=%configuration
%
308 lappend commands
(2) /property
:Platform
=%platform
%
310 lappend commands
(2) [appendArgs
\
311 /logger
:FileLogger
,Microsoft.Build.Engine
\;Logfile
= \
312 [file nativename
[file join $temporaryDirectory \
313 $buildLogFileName]] \;Verbosity
=diagnostic
]
315 ###############################################################################
317 set uninstallLogFileName
[appendArgs
\
318 [file rootname
[file tail
$vsixFileName]] \
319 -uninstall- [pid] .log
]
321 set commands
(3) [list exec [file nativename
$vsixInstaller]]
323 lappend commands
(3) /quiet
/norepair
324 lappend commands
(3) [appendArgs
/logFile
: $uninstallLogFileName]
325 lappend commands
(3) [appendArgs
/uninstall
:SQLite.UWP
.2015]
327 ###############################################################################
331 puts stdout
[appendArgs
\
332 "Install log: \"" [file nativename
[file join \
333 $temporaryDirectory $installLogFileName]] \"\n]
337 puts stdout
[appendArgs
\
338 "Build logs: \"" [file nativename
[file join \
339 $temporaryDirectory $buildLogFileName]] \"\n]
343 puts stdout
[appendArgs
\
344 "Uninstall log: \"" [file nativename
[file join \
345 $temporaryDirectory $uninstallLogFileName]] \"\n]
349 ###############################################################################
352 putsAndEval
$commands(1)
354 set versionNumber
[string trim
[readFile
$versionFileName]]
355 set data
[readFile
$projectTemplateFileName]
356 set data
[string map
[list %versionNumber
% $versionNumber] $data]
358 set projectFileName
[file join $path vsixtest.vcxproj
]
359 writeFile
$projectFileName $data
361 set platforms
[list x86 x64 ARM
]
362 set configurations
[list Debug Release
]
364 foreach platform
$platforms {
365 foreach configuration
$configurations {
366 putsAndEval
[string map
[list \
367 %platform
% $platform %configuration
% $configuration] \
372 putsAndEval
$commands(3)