OLD | NEW |
(Empty) | |
| 1 #!/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 # |
| 8 # 1. This tool is Windows only. |
| 9 # |
| 10 # 2. This tool must be executed with "elevated administrator" privileges. |
| 11 # |
| 12 # 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 |
| 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. |
| 19 # |
| 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. |
| 23 # |
| 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. |
| 26 # |
| 27 # 7. The VS140COMNTOOLS environment variable must refer to the Visual Studio |
| 28 # 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 # |
| 35 package require Tcl 8.4 |
| 36 |
| 37 proc 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 |
| 50 proc 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 |
| 58 proc 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 |
| 88 proc 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 |
| 97 proc 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 |
| 121 proc appendArgs { args } { |
| 122 # |
| 123 # NOTE: Returns all passed arguments joined together as a single string |
| 124 # with no intervening spaces between arguments. |
| 125 # |
| 126 eval append result $args |
| 127 } |
| 128 |
| 129 proc 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 |
| 141 proc 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 |
| 153 proc putsAndEval { command } { |
| 154 # |
| 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 |
| 162 return [uplevel 1 $command] |
| 163 } |
| 164 |
| 165 proc 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 |
| 178 proc 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 |
| 191 # |
| 192 # NOTE: This is the entry point for this script. |
| 193 # |
| 194 set script [file normalize [info script]] |
| 195 |
| 196 if {[string length $script] == 0} then { |
| 197 fail "script file currently being evaluated is unknown" true |
| 198 } |
| 199 |
| 200 if {![isWindows]} then { |
| 201 fail "this tool only works properly on Windows" |
| 202 } |
| 203 |
| 204 if {![isAdministrator]} then { |
| 205 fail "this tool must run with \"elevated administrator\" privileges" |
| 206 } |
| 207 |
| 208 set path [file normalize [file dirname $script]] |
| 209 set argc [llength $argv]; if {$argc > 1} then {fail "" true} |
| 210 |
| 211 if {$argc == 1} then { |
| 212 set vsixFileName [lindex $argv 0] |
| 213 } else { |
| 214 set vsixFileName [file join \ |
| 215 [file dirname $path] sqlite-UWP-output.vsix] |
| 216 } |
| 217 |
| 218 ############################################################################### |
| 219 |
| 220 if {[isBadFile $vsixFileName]} then { |
| 221 fail [appendArgs \ |
| 222 "VSIX file \"" $vsixFileName "\" does not exist"] |
| 223 } |
| 224 |
| 225 set versionFileName [file join [file dirname $path] VERSION] |
| 226 |
| 227 if {[isBadFile $versionFileName]} then { |
| 228 fail [appendArgs \ |
| 229 "Version file \"" $versionFileName "\" does not exist"] |
| 230 } |
| 231 |
| 232 set projectTemplateFileName [file join $path vsixtest.vcxproj.data] |
| 233 |
| 234 if {[isBadFile $projectTemplateFileName]} then { |
| 235 fail [appendArgs \ |
| 236 "Project template file \"" $projectTemplateFileName \ |
| 237 "\" does not exist"] |
| 238 } |
| 239 |
| 240 set envVarName VS140COMNTOOLS |
| 241 set vsDirectory [getEnvironmentVariable $envVarName] |
| 242 |
| 243 if {[isBadDirectory $vsDirectory]} then { |
| 244 fail [appendArgs \ |
| 245 "Visual Studio 2015 directory \"" $vsDirectory \ |
| 246 "\" from environment variable \"" $envVarName \ |
| 247 "\" does not exist"] |
| 248 } |
| 249 |
| 250 set vsixInstaller [file join \ |
| 251 [file dirname $vsDirectory] IDE VSIXInstaller.exe] |
| 252 |
| 253 if {[isBadFile $vsixInstaller]} then { |
| 254 fail [appendArgs \ |
| 255 "Visual Studio 2015 VSIX installer \"" $vsixInstaller \ |
| 256 "\" does not exist"] |
| 257 } |
| 258 |
| 259 set envVarName ProgramFiles |
| 260 set programFiles [getEnvironmentVariable $envVarName] |
| 261 |
| 262 if {[isBadDirectory $programFiles]} then { |
| 263 fail [appendArgs \ |
| 264 "Program Files directory \"" $programFiles \ |
| 265 "\" from environment variable \"" $envVarName \ |
| 266 "\" does not exist"] |
| 267 } |
| 268 |
| 269 set msBuild [file join $programFiles MSBuild 14.0 Bin MSBuild.exe] |
| 270 |
| 271 if {[isBadFile $msBuild]} then { |
| 272 fail [appendArgs \ |
| 273 "MSBuild v14.0 executable file \"" $msBuild \ |
| 274 "\" does not exist"] |
| 275 } |
| 276 |
| 277 set temporaryDirectory [getTemporaryPath] |
| 278 |
| 279 if {[isBadDirectory $temporaryDirectory]} then { |
| 280 fail [appendArgs \ |
| 281 "Temporary directory \"" $temporaryDirectory \ |
| 282 "\" does not exist"] |
| 283 } |
| 284 |
| 285 ############################################################################### |
| 286 |
| 287 set installLogFileName [appendArgs \ |
| 288 [file rootname [file tail $vsixFileName]] \ |
| 289 -install- [pid] .log] |
| 290 |
| 291 set commands(1) [list exec [file nativename $vsixInstaller]] |
| 292 |
| 293 lappend commands(1) /quiet /norepair |
| 294 lappend commands(1) [appendArgs /logFile: $installLogFileName] |
| 295 lappend commands(1) [file nativename $vsixFileName] |
| 296 |
| 297 ############################################################################### |
| 298 |
| 299 set buildLogFileName [appendArgs \ |
| 300 [file rootname [file tail $vsixFileName]] \ |
| 301 -build-%configuration%-%platform%- [pid] .log] |
| 302 |
| 303 set commands(2) [list exec [file nativename $msBuild]] |
| 304 |
| 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% |
| 309 |
| 310 lappend commands(2) [appendArgs \ |
| 311 /logger:FileLogger,Microsoft.Build.Engine\;Logfile= \ |
| 312 [file nativename [file join $temporaryDirectory \ |
| 313 $buildLogFileName]] \;Verbosity=diagnostic] |
| 314 |
| 315 ############################################################################### |
| 316 |
| 317 set uninstallLogFileName [appendArgs \ |
| 318 [file rootname [file tail $vsixFileName]] \ |
| 319 -uninstall- [pid] .log] |
| 320 |
| 321 set commands(3) [list exec [file nativename $vsixInstaller]] |
| 322 |
| 323 lappend commands(3) /quiet /norepair |
| 324 lappend commands(3) [appendArgs /logFile: $uninstallLogFileName] |
| 325 lappend commands(3) [appendArgs /uninstall:SQLite.UWP.2015] |
| 326 |
| 327 ############################################################################### |
| 328 |
| 329 if {1} then { |
| 330 catch { |
| 331 puts stdout [appendArgs \ |
| 332 "Install log: \"" [file nativename [file join \ |
| 333 $temporaryDirectory $installLogFileName]] \"\n] |
| 334 } |
| 335 |
| 336 catch { |
| 337 puts stdout [appendArgs \ |
| 338 "Build logs: \"" [file nativename [file join \ |
| 339 $temporaryDirectory $buildLogFileName]] \"\n] |
| 340 } |
| 341 |
| 342 catch { |
| 343 puts stdout [appendArgs \ |
| 344 "Uninstall log: \"" [file nativename [file join \ |
| 345 $temporaryDirectory $uninstallLogFileName]] \"\n] |
| 346 } |
| 347 } |
| 348 |
| 349 ############################################################################### |
| 350 |
| 351 if {1} then { |
| 352 putsAndEval $commands(1) |
| 353 |
| 354 set versionNumber [string trim [readFile $versionFileName]] |
| 355 set data [readFile $projectTemplateFileName] |
| 356 set data [string map [list %versionNumber% $versionNumber] $data] |
| 357 |
| 358 set projectFileName [file join $path vsixtest.vcxproj] |
| 359 writeFile $projectFileName $data |
| 360 |
| 361 set platforms [list x86 x64 ARM] |
| 362 set configurations [list Debug Release] |
| 363 |
| 364 foreach platform $platforms { |
| 365 foreach configuration $configurations { |
| 366 putsAndEval [string map [list \ |
| 367 %platform% $platform %configuration% $configuration] \ |
| 368 $commands(2)] |
| 369 } |
| 370 } |
| 371 |
| 372 putsAndEval $commands(3) |
| 373 } |
OLD | NEW |