-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbootstrap.tcl
358 lines (308 loc) · 10.3 KB
/
bootstrap.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# This should be the preamble to every application
# It makes it possible to run as starpack or as a sourced script
if {![catch {package require starkit}]} {
#this is to initialize starkit variables
starkit::startup
}
set builddate [clock format [clock seconds] -gmt 1]
proc ex {args} {
return [exec -- {*}$args >&@ stdout]
}
# only for text files, assumes utf-8 encoding
proc slurp {path} {
set fd [open $path r]
fconfigure $fd -encoding utf-8
set data [read $fd]
close $fd
return $data
}
proc spit {path content} {
set fd [open $path w]
puts -nonewline $fd $content
close $fd
}
proc generalize-arch {arch} {
switch -glob $arch {
i?86 {return ix86}
x86_64 {return x86_64}
default {error "Unrecognized CPU architecture"}
}
}
proc this-arch {} {
return [generalize-arch $::tcl_platform(machine)]
}
proc this-os {} {
switch -glob $::tcl_platform(os) {
Linux {return linux}
Windows* {return win32}
default {error "Unrecognized OS"}
}
}
package require http
package require vfs::zip
proc platforminfo {} {
puts "Script name: $::argv0"
puts "Arguments:\n[join $::argv \n]"
puts "Current directory: [pwd]"
puts "This is Tcl version $::tcl_version , patchlevel $::tcl_patchLevel"
puts "[info nameofexecutable] is [info tclversion] patch [info patchlevel]"
puts "Directory(s) where package require will search:"
puts "$::auto_path"
puts "tcl_libPath = $::tcl_libPath" ;# May want to skip this one
puts "tcl_library = $::tcl_library"
puts "info library = [info library]"
puts "Shared libraries are expected to use the extension [info sharedlibextension]"
puts "platform information:"
parray ::tcl_platform
}
proc install-fpm {} {
if {[catch {exec fpm --version}] == 1} {
puts "Installing fpm"
ex sudo apt-get update --fix-missing
catch {ex sudo apt-get remove ruby1.8}
ex sudo apt-get -fy install git ruby1.9.1-full ruby-dev gcc rpm
catch {ex sudo apt-get -fy install rubygems}
ex sudo apt-get -fy install rubygems-integration
ex sudo gem install fpm
} else {
puts "fpm already present"
}
}
#TODO test for rpms
proc fpm-arch {arch} {
if {$arch eq "x86_64"} {
return x86_64
} elseif {$arch eq "ix86"} {
return i386
} else {
error "fpm-arch unrecognized arch: $arch"
}
}
# also in sklib
proc unzip {zipfile {destdir .}} {
set mntfile [vfs::zip::Mount $zipfile $zipfile]
foreach f [glob [file join $zipfile *]] {
file copy -force $f $destdir
}
vfs::zip::Unmount $mntfile $zipfile
}
# convert pkg-name-1.2.3 into "pkg-name 1.2.3" or
# convert linux-ix86 into "linux ix86"
proc split-last-dash {s} {
set dashpos [string last - $s]
if {$dashpos > 0} {
return [string replace $s $dashpos $dashpos " "]
} else {
error "Wrong name to split: $s. It should contain at least one dash"
}
}
# Package presence is checked in the following order:
# 1. is pkg-ver in lib? => copy to build dir
# 2. is pkg-ver in teapot-cache? => prepare, unpack to lib dir, delete other versions in lib dir
# 3. is pkg-ver in tepot repo? => fetch to teapot-cache dir
# first prepare-pkg and copy from lib to build
proc copy-pkg {os arch pkgname ver proj} {
prepare-pkg $os $arch $pkgname $ver
set libdir [file join build $proj $os-$arch $proj.vfs lib]
#puts "Copying package $pkgname-$ver to $libdir"
if {\
[catch {file copy -force [file join lib $os-$arch $pkgname-$ver] $libdir}] &&\
[catch {file copy -force [file join lib generic $pkgname-$ver] $libdir}]} {
#if both copy attempts failed raise error
error "Could not find $pkgname-$ver neither in lib/$os-$arch nor lib/generic"
}
}
proc suffix_exec {os} {
array set os_suffix {
linux .bin
win32 .exe
}
return $os_suffix($os)
}
# recursively copy contents of the $from dir to the $to dir
# while overwriting items in $to if necessary
# ignore files matching glob pattern $ignore
proc copy-merge {from to {ignore ""}} {
file mkdir $to
foreach f [glob [file join $from *]] {
set tail [file tail $f]
if {![string match $ignore $tail]} {
if {[file isdirectory $f]} {
set new_to [file join $to $tail]
file mkdir $new_to
copy-merge $f $new_to
} else {
file copy -force $f $to
}
}
}
}
proc build {os arch_exact proj base {packages {}}} {
set arch [generalize-arch $arch_exact]
puts "\nStarting build ($os $arch $proj $base $packages)"
if {![file isdirectory $proj]} {
puts "Could not find project dir $proj"
return
}
set bld [file join build $proj $os-$arch]
puts "Cleaning build dir $bld"
file delete -force $bld
file mkdir [file join $bld $proj.vfs lib]
# we don't copy base-tcl/tk to build folder. Having it in lib is enough - hence prepare-pkg
prepare-pkg $os $arch {*}[split-last-dash $base]
foreach pkgver $packages {
copy-pkg $os $arch {*}[split-last-dash $pkgver] $proj
}
set vfs [file join $bld $proj.vfs]
puts "Copying project source files to VFS dir: $vfs"
copy-merge $proj $vfs exclude
set cmd [list [info nameofexecutable] sdx.kit wrap [file join $bld $proj[suffix_exec $os]] -vfs [file join $bld $proj.vfs] -runtime [file join lib $os-$arch $base]]
puts "Building starpack $proj"
puts $cmd
ex {*}$cmd
}
proc run {proj} {
ex [info nameofexecutable] [file join build $proj [this-os]-[this-arch] $proj.vfs main.tcl]
}
proc prepare-lib {pkgname ver} {
set dest [file join lib generic $pkgname-$ver]
file delete -force $dest
file mkdir $dest
copy-merge $pkgname $dest
pkg_mkIndex $dest
}
proc doc {path} {
package require doctools
::doctools::new mydtp -format html
set path [file normalize $path]
set dest [file join [file dir $path] [file root [file tail $path]].html]
spit $dest [mydtp format [slurp $path]]
}
# tarch - teapot specific architecture like linux-glibc2.3-x86_64
proc tarch {os arch} {
#TODO macosx
switch -exact $os {
linux {return $os-glibc2.3-$arch}
win32 {return $os-$arch}
default {error "Unrecognized os: $os"}
}
}
proc ctype2ext {ctype} {
switch -glob $ctype {
*application/x-zip* {return .zip}
*application/octet-stream* {return ""}
*text/plain* {return .tcl}
default {error "Unrecognized Content-Type: $ctype"}
}
}
# return random 0 <= x < $n
proc rand-int {n} {
return [expr {round(floor(rand()*$n))}]
}
# return 9-digit random integer
proc rand-big {} {
return [expr {100000000 + [rand-int 900000000]}]
}
# return the path to the cached package
proc teacup-fetch {os arch pkgname ver} {
set nv $pkgname-$ver
if {[string match base-* $pkgname]} {
set type application
set tarch_list [list [tarch $os $arch]]
} else {
set type package
set tarch_list [list tcl [tarch $os $arch]]
}
set tcdir [teapot-cache]
foreach tarch $tarch_list {
set tcpath /$type/name/$pkgname/ver/$ver/arch/$tarch
set tcpath_local [file normalize ../teapot-cache$tcpath]
foreach ext {.tcl .zip ""} {
set f $tcpath_local/$nv$ext
if {[file isfile $f]} {
puts stderr "Found cached $f"
return $f
}
}
}
foreach tarch $tarch_list {
set tcpath /$type/name/$pkgname/ver/$ver/arch/$tarch
set tcpath_local [file normalize ../teapot-cache$tcpath]
puts stderr "Fetching $nv-$tarch"
try {
set tmpfile /tmp/teacup_fetch_[rand-big]
set url http://teapot.activestate.com$tcpath/file
puts stderr "Fetching url: $url"
set tok [http::geturl $url -channel [open $tmpfile w] -timeout 200000]
upvar #0 $tok state
if {[http::ncode $tok] == 200} {
array set meta [http::meta $tok]
puts stderr "Content-Type: $meta(Content-Type)"
set ext [ctype2ext $meta(Content-Type)]
puts stderr "ext=$ext"
file mkdir $tcpath_local
file rename -force $tmpfile $tcpath_local/$nv$ext
return $tcpath_local/$nv$ext
} else {
puts stderr "Fetching ERROR: Received http response: [http::code $tok]"
}
} on error {e1 e2} {
puts stderr "Could not fetch $nv ERROR: $e1 $e2"
} finally {
catch {set fd $state(-channel); close $fd;}
catch {file delete $tmpfile}
http::cleanup $tok
}
}
error "Could not fetch $nv"
}
proc teapot-cache {} {
set tcdir [file normalize ../teapot-cache]
if {![file isdir $tcdir]} {
ex git clone https://github.com/fruho/teapot-cache $tcdir
}
return $tcdir
}
proc prepare-pkg {os arch pkgname ver} {
file mkdir [file join lib $os-$arch]
set target_path_depend [file join lib $os-$arch $pkgname-$ver]
set target_path_indep [file join lib generic $pkgname-$ver]
# nothing to do if pkg exists in lib dir, it may be file or dir
if {[file exists $target_path_depend]} {
#puts "Already prepared: $target_path_depend"
return
}
if {[file exists $target_path_indep]} {
#puts "Already prepared: $target_path_indep"
return
}
set localpkg [teacup-fetch $os $arch $pkgname $ver]
puts "Preparing package $pkgname-$ver to place in lib folder"
switch -glob $localpkg {
*/application/*/arch/* {
file copy -force $localpkg $target_path_depend
return
}
*/package/*/arch/tcl/*.tcl {
file mkdir $target_path_indep
file copy $localpkg $target_path_indep
pkg_mkIndex $target_path_indep
return
}
*/package/*/arch/tcl/*.zip {
file mkdir $target_path_indep
#puts stderr "Unzipping to $target_path_indep"
unzip $localpkg $target_path_indep
return
}
*/package/*.zip {
file mkdir $target_path_depend
#puts stderr "Unzipping to $target_path_depend"
unzip $localpkg $target_path_depend
return
}
default {error "Could not determine what to do with $localpkg"}
}
}
#platforminfo
source build.tcl