platform-1.0.14.tm 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. # -*- tcl -*-
  2. # ### ### ### ######### ######### #########
  3. ## Overview
  4. # Heuristics to assemble a platform identifier from publicly available
  5. # information. The identifier describes the platform of the currently
  6. # running tcl shell. This is a mixture of the runtime environment and
  7. # of build-time properties of the executable itself.
  8. #
  9. # Examples:
  10. # <1> A tcl shell executing on a x86_64 processor, but having a
  11. # wordsize of 4 was compiled for the x86 environment, i.e. 32
  12. # bit, and loaded packages have to match that, and not the
  13. # actual cpu.
  14. #
  15. # <2> The hp/solaris 32/64 bit builds of the core cannot be
  16. # distinguished by looking at tcl_platform. As packages have to
  17. # match the 32/64 information we have to look in more places. In
  18. # this case we inspect the executable itself (magic numbers,
  19. # i.e. fileutil::magic::filetype).
  20. #
  21. # The basic information used comes out of the 'os' and 'machine'
  22. # entries of the 'tcl_platform' array. A number of general and
  23. # os/machine specific transformation are applied to get a canonical
  24. # result.
  25. #
  26. # General
  27. # Only the first element of 'os' is used - we don't care whether we
  28. # are on "Windows NT" or "Windows XP" or whatever.
  29. #
  30. # Machine specific
  31. # % arm* -> arm
  32. # % sun4* -> sparc
  33. # % intel -> ix86
  34. # % i*86* -> ix86
  35. # % Power* -> powerpc
  36. # % x86_64 + wordSize 4 => x86 code
  37. #
  38. # OS specific
  39. # % AIX are always powerpc machines
  40. # % HP-UX 9000/800 etc means parisc
  41. # % linux has to take glibc version into account
  42. # % sunos -> solaris, and keep version number
  43. #
  44. # NOTE: A platform like linux glibc 2.3, which can use glibc 2.2 stuff
  45. # has to provide all possible allowed platform identifiers when
  46. # searching search. Ditto a solaris 2.8 platform can use solaris 2.6
  47. # packages. Etc. This is handled by the other procedure, see below.
  48. # ### ### ### ######### ######### #########
  49. ## Requirements
  50. namespace eval ::platform {}
  51. # ### ### ### ######### ######### #########
  52. ## Implementation
  53. # -- platform::generic
  54. #
  55. # Assembles an identifier for the generic platform. It leaves out
  56. # details like kernel version, libc version, etc.
  57. proc ::platform::generic {} {
  58. global tcl_platform
  59. set plat [string tolower [lindex $tcl_platform(os) 0]]
  60. set cpu $tcl_platform(machine)
  61. switch -glob -- $cpu {
  62. sun4* {
  63. set cpu sparc
  64. }
  65. intel -
  66. i*86* {
  67. set cpu ix86
  68. }
  69. x86_64 {
  70. if {$tcl_platform(wordSize) == 4} {
  71. # See Example <1> at the top of this file.
  72. set cpu ix86
  73. }
  74. }
  75. "Power*" {
  76. set cpu powerpc
  77. }
  78. "arm*" {
  79. set cpu arm
  80. }
  81. ia64 {
  82. if {$tcl_platform(wordSize) == 4} {
  83. append cpu _32
  84. }
  85. }
  86. }
  87. switch -glob -- $plat {
  88. cygwin* {
  89. set plat cygwin
  90. }
  91. windows {
  92. if {$tcl_platform(platform) == "unix"} {
  93. set plat cygwin
  94. } else {
  95. set plat win32
  96. }
  97. if {$cpu eq "amd64"} {
  98. # Do not check wordSize, win32-x64 is an IL32P64 platform.
  99. set cpu x86_64
  100. }
  101. }
  102. sunos {
  103. set plat solaris
  104. if {[string match "ix86" $cpu]} {
  105. if {$tcl_platform(wordSize) == 8} {
  106. set cpu x86_64
  107. }
  108. } elseif {![string match "ia64*" $cpu]} {
  109. # sparc
  110. if {$tcl_platform(wordSize) == 8} {
  111. append cpu 64
  112. }
  113. }
  114. }
  115. darwin {
  116. set plat macosx
  117. # Correctly identify the cpu when running as a 64bit
  118. # process on a machine with a 32bit kernel
  119. if {$cpu eq "ix86"} {
  120. if {$tcl_platform(wordSize) == 8} {
  121. set cpu x86_64
  122. }
  123. }
  124. }
  125. aix {
  126. set cpu powerpc
  127. if {$tcl_platform(wordSize) == 8} {
  128. append cpu 64
  129. }
  130. }
  131. hp-ux {
  132. set plat hpux
  133. if {![string match "ia64*" $cpu]} {
  134. set cpu parisc
  135. if {$tcl_platform(wordSize) == 8} {
  136. append cpu 64
  137. }
  138. }
  139. }
  140. osf1 {
  141. set plat tru64
  142. }
  143. }
  144. return "${plat}-${cpu}"
  145. }
  146. # -- platform::identify
  147. #
  148. # Assembles an identifier for the exact platform, by extending the
  149. # generic identifier. I.e. it adds in details like kernel version,
  150. # libc version, etc., if they are relevant for the loading of
  151. # packages on the platform.
  152. proc ::platform::identify {} {
  153. global tcl_platform
  154. set id [generic]
  155. regexp {^([^-]+)-([^-]+)$} $id -> plat cpu
  156. switch -- $plat {
  157. solaris {
  158. regsub {^5} $tcl_platform(osVersion) 2 text
  159. append plat $text
  160. return "${plat}-${cpu}"
  161. }
  162. macosx {
  163. set major [lindex [split $tcl_platform(osVersion) .] 0]
  164. if {$major > 8} {
  165. incr major -4
  166. append plat 10.$major
  167. return "${plat}-${cpu}"
  168. }
  169. }
  170. linux {
  171. # Look for the libc*.so and determine its version
  172. # (libc5/6, libc6 further glibc 2.X)
  173. set v unknown
  174. # Determine in which directory to look. /lib, or /lib64.
  175. # For that we use the tcl_platform(wordSize).
  176. #
  177. # We could use the 'cpu' info, per the equivalence below,
  178. # that however would be restricted to intel. And this may
  179. # be a arm, mips, etc. system. The wordsize is more
  180. # fundamental.
  181. #
  182. # ix86 <=> (wordSize == 4) <=> 32 bit ==> /lib
  183. # x86_64 <=> (wordSize == 8) <=> 64 bit ==> /lib64
  184. #
  185. # Do not look into /lib64 even if present, if the cpu
  186. # doesn't fit.
  187. # TODO: Determine the prefixes (i386, x86_64, ...) for
  188. # other cpus. The path after the generic one is utterly
  189. # specific to intel right now. Ok, on Ubuntu, possibly
  190. # other Debian systems we may apparently be able to query
  191. # the necessary CPU code. If we can't we simply use the
  192. # hardwired fallback.
  193. switch -exact -- $tcl_platform(wordSize) {
  194. 4 {
  195. lappend bases /lib
  196. if {[catch {
  197. exec dpkg-architecture -qDEB_HOST_MULTIARCH
  198. } res]} {
  199. lappend bases /lib/i386-linux-gnu
  200. } else {
  201. # dpkg-arch returns the full tripled, not just cpu.
  202. lappend bases /lib/$res
  203. }
  204. }
  205. 8 {
  206. lappend bases /lib64
  207. if {[catch {
  208. exec dpkg-architecture -qDEB_HOST_MULTIARCH
  209. } res]} {
  210. lappend bases /lib/x86_64-linux-gnu
  211. } else {
  212. # dpkg-arch returns the full tripled, not just cpu.
  213. lappend bases /lib/$res
  214. }
  215. }
  216. default {
  217. return -code error "Bad wordSize $tcl_platform(wordSize), expected 4 or 8"
  218. }
  219. }
  220. foreach base $bases {
  221. if {[LibcVersion $base -> v]} break
  222. }
  223. append plat -$v
  224. return "${plat}-${cpu}"
  225. }
  226. }
  227. return $id
  228. }
  229. proc ::platform::LibcVersion {base _->_ vv} {
  230. upvar 1 $vv v
  231. set libclist [lsort [glob -nocomplain -directory $base libc*]]
  232. if {![llength $libclist]} { return 0 }
  233. set libc [lindex $libclist 0]
  234. # Try executing the library first. This should suceed
  235. # for a glibc library, and return the version
  236. # information.
  237. if {![catch {
  238. set vdata [lindex [split [exec $libc] \n] 0]
  239. }]} {
  240. regexp {version ([0-9]+(\.[0-9]+)*)} $vdata -> v
  241. foreach {major minor} [split $v .] break
  242. set v glibc${major}.${minor}
  243. return 1
  244. } else {
  245. # We had trouble executing the library. We are now
  246. # inspecting its name to determine the version
  247. # number. This code by Larry McVoy.
  248. if {[regexp -- {libc-([0-9]+)\.([0-9]+)} $libc -> major minor]} {
  249. set v glibc${major}.${minor}
  250. return 1
  251. }
  252. }
  253. return 0
  254. }
  255. # -- platform::patterns
  256. #
  257. # Given an exact platform identifier, i.e. _not_ the generic
  258. # identifier it assembles a list of exact platform identifier
  259. # describing platform which should be compatible with the
  260. # input.
  261. #
  262. # I.e. packages for all platforms in the result list should be
  263. # loadable on the specified platform.
  264. # << Should we add the generic identifier to the list as well ? In
  265. # general it is not compatible I believe. So better not. In many
  266. # cases the exact identifier is identical to the generic one
  267. # anyway.
  268. # >>
  269. proc ::platform::patterns {id} {
  270. set res [list $id]
  271. if {$id eq "tcl"} {return $res}
  272. switch -glob -- $id {
  273. solaris*-* {
  274. if {[regexp {solaris([^-]*)-(.*)} $id -> v cpu]} {
  275. if {$v eq ""} {return $id}
  276. foreach {major minor} [split $v .] break
  277. incr minor -1
  278. for {set j $minor} {$j >= 6} {incr j -1} {
  279. lappend res solaris${major}.${j}-${cpu}
  280. }
  281. }
  282. }
  283. linux*-* {
  284. if {[regexp {linux-glibc([^-]*)-(.*)} $id -> v cpu]} {
  285. foreach {major minor} [split $v .] break
  286. incr minor -1
  287. for {set j $minor} {$j >= 0} {incr j -1} {
  288. lappend res linux-glibc${major}.${j}-${cpu}
  289. }
  290. }
  291. }
  292. macosx-powerpc {
  293. lappend res macosx-universal
  294. }
  295. macosx-x86_64 {
  296. lappend res macosx-i386-x86_64
  297. }
  298. macosx-ix86 {
  299. lappend res macosx-universal macosx-i386-x86_64
  300. }
  301. macosx*-* {
  302. # 10.5+
  303. if {[regexp {macosx([^-]*)-(.*)} $id -> v cpu]} {
  304. switch -exact -- $cpu {
  305. ix86 {
  306. lappend alt i386-x86_64
  307. lappend alt universal
  308. }
  309. x86_64 { lappend alt i386-x86_64 }
  310. default { set alt {} }
  311. }
  312. if {$v ne ""} {
  313. foreach {major minor} [split $v .] break
  314. # Add 10.5 to 10.minor to patterns.
  315. set res {}
  316. for {set j $minor} {$j >= 5} {incr j -1} {
  317. lappend res macosx${major}.${j}-${cpu}
  318. foreach a $alt {
  319. lappend res macosx${major}.${j}-$a
  320. }
  321. }
  322. # Add unversioned patterns for 10.3/10.4 builds.
  323. lappend res macosx-${cpu}
  324. foreach a $alt {
  325. lappend res macosx-$a
  326. }
  327. } else {
  328. # No version, just do unversioned patterns.
  329. foreach a $alt {
  330. lappend res macosx-$a
  331. }
  332. }
  333. } else {
  334. # no v, no cpu ... nothing
  335. }
  336. }
  337. }
  338. lappend res tcl ; # Pure tcl packages are always compatible.
  339. return $res
  340. }
  341. # ### ### ### ######### ######### #########
  342. ## Ready
  343. package provide platform 1.0.14
  344. # ### ### ### ######### ######### #########
  345. ## Demo application
  346. if {[info exists argv0] && ($argv0 eq [info script])} {
  347. puts ====================================
  348. parray tcl_platform
  349. puts ====================================
  350. puts Generic\ identification:\ [::platform::generic]
  351. puts Exact\ identification:\ \ \ [::platform::identify]
  352. puts ====================================
  353. puts Search\ patterns:
  354. puts *\ [join [::platform::patterns [::platform::identify]] \n*\ ]
  355. puts ====================================
  356. exit 0
  357. }