blob: a7517786c51a8ba92f5bfb09974ea100e9c16890 [file] [log] [blame]
mistachkin48680192012-07-27 02:36:06 +00001#!/usr/bin/tclsh
2#
3# This script is used to generate a VSIX (Visual Studio Extension) file for
4# SQLite usable by Visual Studio.
5
6proc fail { {error ""} {usage false} } {
7 if {[string length $error] > 0} then {
8 puts stdout $error
9 if {!$usage} then {exit 1}
10 }
11
12 puts stdout "usage:\
13[file tail [info nameofexecutable]]\
14[file tail [info script]] <binaryDirectory> \[sourceDirectory\]"
15
16 exit 1
17}
18
19proc getEnvironmentVariable { name } {
20 #
21 # NOTE: Returns the value of the specified environment variable or an empty
22 # string for environment variables that do not exist in the current
23 # process environment.
24 #
25 return [expr {[info exists ::env($name)] ? $::env($name) : ""}]
26}
27
28proc getTemporaryPath {} {
29 #
30 # NOTE: Returns the normalized path to the first temporary directory found
31 # in the typical set of environment variables used for that purpose
32 # or an empty string to signal a failure to locate such a directory.
33 #
34 set names [list]
35
36 foreach name [list TEMP TMP] {
37 lappend names [string toupper $name] [string tolower $name] \
38 [string totitle $name]
39 }
40
41 foreach name $names {
42 set value [getEnvironmentVariable $name]
43
44 if {[string length $value] > 0} then {
45 return [file normalize $value]
46 }
47 }
48
49 return ""
50}
51
52proc appendArgs { args } {
53 #
54 # NOTE: Returns all passed arguments joined together as a single string with
55 # no intervening spaces between arguments.
56 #
57 eval append result $args
58}
59
60proc readFile { fileName } {
61 #
62 # NOTE: Reads and returns the entire contents of the specified file, which
63 # may contain binary data.
64 #
65 set file_id [open $fileName RDONLY]
66 fconfigure $file_id -encoding binary -translation binary
67 set result [read $file_id]
68 close $file_id
69 return $result
70}
71
72proc writeFile { fileName data } {
73 #
74 # NOTE: Writes the entire contents of the specified file, which may contain
75 # binary data.
76 #
77 set file_id [open $fileName {WRONLY CREAT TRUNC}]
78 fconfigure $file_id -encoding binary -translation binary
79 puts -nonewline $file_id $data
80 close $file_id
81 return ""
82}
83
84proc substFile { fileName } {
85 #
86 # NOTE: Performs all Tcl command, variable, and backslash substitutions in
87 # the specified file and then re-writes the contents of that same file
88 # with the substituted data.
89 #
90 return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]]
91}
92
93proc replacePlatform { fileName platformName } {
94 #
95 # NOTE: Returns the specified file name containing the platform name instead
96 # of platform placeholder tokens.
97 #
98 return [string map [list <platform> $platformName] $fileName]
99}
100
101set script [file normalize [info script]]
102
103if {[string length $script] == 0} then {
104 fail "script file currently being evaluated is unknown" true
105}
106
107set path [file dirname $script]
108set rootName [file rootname [file tail $script]]
109
110###############################################################################
111
112#
113# NOTE: Process and verify all the command line arguments.
114#
115set argc [llength $argv]
116if {$argc != 1 && $argc != 2} then {fail}
117
118set binaryDirectory [lindex $argv 0]
119
120if {[string length $binaryDirectory] == 0} then {
121 fail "invalid binary directory"
122}
123
124if {![file exists $binaryDirectory] || \
125 ![file isdirectory $binaryDirectory]} then {
126 fail "binary directory does not exist"
127}
128
129if {$argc == 2} then {
130 set sourceDirectory [lindex $argv 1]
131} else {
132 #
133 # NOTE: Assume that the source directory is the parent directory of the one
134 # that contains this script file.
135 #
136 set sourceDirectory [file dirname $path]
137}
138
139if {[string length $sourceDirectory] == 0} then {
140 fail "invalid source directory"
141}
142
143if {![file exists $sourceDirectory] || \
144 ![file isdirectory $sourceDirectory]} then {
145 fail "source directory does not exist"
146}
147
148###############################################################################
149
mistachkin391b3642012-07-31 00:43:31 +0000150#
151# NOTE: Evaluate the user-specific customizations file, if it exists.
152#
153set userFile [file join $path [appendArgs \
154 $rootName . $tcl_platform(user) .tcl]]
155
156if {[file exists $userFile] && \
157 [file isfile $userFile]} then {
158 source $userFile
159}
160
161###############################################################################
162
mistachkin48680192012-07-27 02:36:06 +0000163set templateFile [file join $path win sqlite.vsix]
164
165if {![file exists $templateFile] || \
166 ![file isfile $templateFile]} then {
167 fail [appendArgs "template file \"" $templateFile "\" does not exist"]
168}
169
170set currentDirectory [pwd]
171set outputFile [file join $currentDirectory sqlite-output.vsix]
172
173if {[file exists $outputFile]} then {
174 fail [appendArgs "output file \"" $outputFile "\" already exists"]
175}
176
177###############################################################################
178
179#
180# NOTE: Make sure that a valid temporary directory exists.
181#
182set temporaryDirectory [getTemporaryPath]
183
184if {[string length $temporaryDirectory] == 0 || \
185 ![file exists $temporaryDirectory] || \
186 ![file isdirectory $temporaryDirectory]} then {
187 fail "cannot locate a usable temporary directory"
188}
189
190#
191# NOTE: Setup the staging directory to have a unique name inside of the
192# configured temporary directory.
193#
194set stagingDirectory [file normalize [file join $temporaryDirectory \
195 [appendArgs $rootName . [pid]]]]
196
197###############################################################################
198
199#
200# NOTE: Configure the external zipping tool. First, see if it has already
201# been pre-configured. If not, try to query it from the environment.
202# Finally, fallback on the default of simply "zip", which will then
203# be assumed to exist somewhere along the PATH.
204#
205if {![info exists zip]} then {
206 if {[info exists env(ZipTool)]} then {
207 set zip $env(ZipTool)
208 }
209 if {![info exists zip] || ![file exists $zip]} then {
210 set zip zip
211 }
212}
213
214#
215# NOTE: Configure the external unzipping tool. First, see if it has already
216# been pre-configured. If not, try to query it from the environment.
217# Finally, fallback on the default of simply "unzip", which will then
218# be assumed to exist somewhere along the PATH.
219#
220if {![info exists unzip]} then {
221 if {[info exists env(UnZipTool)]} then {
222 set unzip $env(UnZipTool)
223 }
224 if {![info exists unzip] || ![file exists $unzip]} then {
225 set unzip unzip
226 }
227}
228
229###############################################################################
230
231#
232# NOTE: Attempt to extract the SQLite version from the "sqlite3.h" header file
233# in the source directory. This script assumes that the header file has
234# already been generated by the build process.
235#
mistachkin391b3642012-07-31 00:43:31 +0000236set pattern {^#define\s+SQLITE_VERSION\s+"(.*)"$}
mistachkin48680192012-07-27 02:36:06 +0000237set data [readFile [file join $sourceDirectory sqlite3.h]]
238
239if {![regexp -line -- $pattern $data dummy version]} then {
240 fail [appendArgs "cannot locate SQLITE_VERSION value in \"" \
241 [file join $sourceDirectory sqlite3.h] \"]
242}
243
244###############################################################################
245
246#
247# NOTE: Setup the master file list data, including the necessary flags.
248#
mistachkin391b3642012-07-31 00:43:31 +0000249if {![info exists fileNames(source)]} then {
250 set fileNames(source) [list "" "" "" \
251 [file join $sourceDirectory sqlite3.h] \
252 [file join $binaryDirectory <platform> sqlite3.lib] \
253 [file join $binaryDirectory <platform> sqlite3.dll]]
mistachkin48680192012-07-27 02:36:06 +0000254
mistachkin391b3642012-07-31 00:43:31 +0000255 if {![info exists no(symbols)]} then {
256 lappend fileNames(source) \
257 [file join $binaryDirectory <platform> sqlite3.pdb]
258 }
259}
mistachkin48680192012-07-27 02:36:06 +0000260
mistachkin391b3642012-07-31 00:43:31 +0000261if {![info exists fileNames(destination)]} then {
262 set fileNames(destination) [list \
263 [file join $stagingDirectory extension.vsixmanifest] \
264 [file join $stagingDirectory SDKManifest.xml] \
265 [file join $stagingDirectory DesignTime CommonConfiguration \
266 <platform> SQLite.WinRT.props] \
267 [file join $stagingDirectory DesignTime CommonConfiguration \
268 <platform> sqlite3.h] \
269 [file join $stagingDirectory DesignTime CommonConfiguration \
270 <platform> sqlite3.lib] \
271 [file join $stagingDirectory Redist CommonConfiguration \
272 <platform> sqlite3.dll]]
273
274 if {![info exists no(symbols)]} then {
275 lappend fileNames(destination) \
mistachkinab8c4cf2012-08-06 22:29:26 +0000276 [file join $stagingDirectory Redist Debug \
mistachkin391b3642012-07-31 00:43:31 +0000277 <platform> sqlite3.pdb]
278 }
279}
280
281if {![info exists fileNames(neutral)]} then {
282 set fileNames(neutral) [list 1 1 1 1 0 0]
283
284 if {![info exists no(symbols)]} then {
285 lappend fileNames(neutral) 0
286 }
287}
288
289if {![info exists fileNames(subst)]} then {
290 set fileNames(subst) [list 1 1 1 0 0 0]
291
292 if {![info exists no(symbols)]} then {
293 lappend fileNames(subst) 0
294 }
295}
mistachkin48680192012-07-27 02:36:06 +0000296
297###############################################################################
298
299#
300# NOTE: Setup the list of platforms supported by this script.
301#
mistachkin391b3642012-07-31 00:43:31 +0000302if {![info exists platformNames]} then {
mistachkin50afa2a2012-07-31 08:15:56 +0000303 set platformNames [list x86 x64 ARM]
mistachkin391b3642012-07-31 00:43:31 +0000304}
mistachkin48680192012-07-27 02:36:06 +0000305
306###############################################################################
307
308#
309# NOTE: Make sure the staging directory exists, creating it if necessary.
310#
311file mkdir $stagingDirectory
312
313#
314# NOTE: Build the Tcl command used to extract the template package to the
315# staging directory.
316#
317set extractCommand [list exec -- $unzip $templateFile -d $stagingDirectory]
318
319#
320# NOTE: Extract the template package to the staging directory.
321#
322eval $extractCommand
323
324###############################################################################
325
326#
327# NOTE: Process each file in the master file list. There are actually four
328# parallel lists that contain the source file names, destination file
329# names, the platform-neutral flags, and the use-subst flags. When the
330# platform-neutral flag is non-zero, the file is not platform-specific.
331# When the use-subst flag is non-zero, the file is considered to be a
332# text file that may contain Tcl variable and/or command replacements,
333# to be dynamically replaced during processing. If the source file name
334# is an empty string, then the destination file name will be assumed to
335# already exist in the staging directory and will not be copied; however,
336# dynamic replacements may still be performed on the destination file
337# prior to the package being re-zipped.
338#
339foreach sourceFileName $fileNames(source) \
340 destinationFileName $fileNames(destination) \
341 isNeutral $fileNames(neutral) useSubst $fileNames(subst) {
342 #
343 # NOTE: If the current file is platform-neutral, then only one platform will
344 # be processed for it, namely "neutral"; otherwise, each supported
345 # platform will be processed for it individually.
346 #
347 foreach platformName [expr {$isNeutral ? [list neutral] : $platformNames}] {
348 #
mistachkin391b3642012-07-31 00:43:31 +0000349 # NOTE: Use the actual platform name in the destination file name.
350 #
351 set newDestinationFileName [replacePlatform $destinationFileName \
352 $platformName]
353
354 #
mistachkin48680192012-07-27 02:36:06 +0000355 # NOTE: Does the source file need to be copied to the destination file?
356 #
357 if {[string length $sourceFileName] > 0} then {
358 #
mistachkin391b3642012-07-31 00:43:31 +0000359 # NOTE: First, make sure the destination directory exists.
360 #
361 file mkdir [file dirname $newDestinationFileName]
362
363 #
364 # NOTE: Then, copy the source file to the destination file verbatim.
mistachkin48680192012-07-27 02:36:06 +0000365 #
366 file copy [replacePlatform $sourceFileName $platformName] \
mistachkin391b3642012-07-31 00:43:31 +0000367 $newDestinationFileName
mistachkin48680192012-07-27 02:36:06 +0000368 }
369
370 #
371 # NOTE: Does the destination file contain dynamic replacements that must
372 # be processed now?
373 #
374 if {$useSubst} then {
375 #
376 # NOTE: Perform any dynamic replacements contained in the destination
377 # file and then re-write it in-place.
378 #
mistachkin391b3642012-07-31 00:43:31 +0000379 substFile $newDestinationFileName
mistachkin48680192012-07-27 02:36:06 +0000380 }
381 }
382}
383
384###############################################################################
385
386#
387# NOTE: Change the current directory to the staging directory so that the
388# external archive building tool can pickup the necessary files using
389# relative paths.
390#
391cd $stagingDirectory
392
393#
394# NOTE: Build the Tcl command used to archive the final package in the
395# output directory.
396#
397set archiveCommand [list exec -- $zip -r $outputFile *]
398
399#
400# NOTE: Build the final package archive in the output directory.
401#
402eval $archiveCommand
403
404#
405# NOTE: Change back to the previously saved current directory.
406#
407cd $currentDirectory
408
409#
410# NOTE: Cleanup the temporary staging directory.
411#
412file delete -force $stagingDirectory
413
414###############################################################################
415
416#
417# NOTE: Success, emit the fully qualified path of the generated VSIX file.
418#
419puts stdout $outputFile