platform-1.0.15.tm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. windows {
  89. if {$tcl_platform(platform) == "unix"} {
  90. set plat cygwin
  91. } else {
  92. set plat win32
  93. }
  94. if {$cpu eq "amd64"} {
  95. # Do not check wordSize, win32-x64 is an IL32P64 platform.
  96. set cpu x86_64
  97. }
  98. }
  99. sunos {
  100. set plat solaris
  101. if {[string match "ix86" $cpu]} {
  102. if {$tcl_platform(wordSize) == 8} {
  103. set cpu x86_64
  104. }
  105. } elseif {![string match "ia64*" $cpu]} {
  106. # sparc
  107. if {$tcl_platform(wordSize) == 8} {
  108. append cpu 64
  109. }
  110. }
  111. }
  112. darwin {
  113. set plat macosx
  114. # Correctly identify the cpu when running as a 64bit
  115. # process on a machine with a 32bit kernel
  116. if {$cpu eq "ix86"} {
  117. if {$tcl_platform(wordSize) == 8} {
  118. set cpu x86_64
  119. }
  120. }
  121. }
  122. aix {
  123. set cpu powerpc
  124. if {$tcl_platform(wordSize) == 8} {
  125. append cpu 64
  126. }
  127. }
  128. hp-ux {
  129. set plat hpux
  130. if {![string match "ia64*" $cpu]} {
  131. set cpu parisc
  132. if {$tcl_platform(wordSize) == 8} {
  133. append cpu 64
  134. }
  135. }
  136. }
  137. osf1 {
  138. set plat tru64
  139. }
  140. default {
  141. set plat [lindex [split $plat _-] 0]
  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 > 19} {
  165. incr major -20
  166. append plat 11.$major
  167. } else {
  168. incr major -4
  169. append plat 10.$major
  170. return "${plat}-${cpu}"
  171. }
  172. return "${plat}-${cpu}"
  173. }
  174. linux {
  175. # Look for the libc*.so and determine its version
  176. # (libc5/6, libc6 further glibc 2.X)
  177. set v unknown
  178. # Determine in which directory to look. /lib, or /lib64.
  179. # For that we use the tcl_platform(wordSize).
  180. #
  181. # We could use the 'cpu' info, per the equivalence below,
  182. # that however would be restricted to intel. And this may
  183. # be a arm, mips, etc. system. The wordsize is more
  184. # fundamental.
  185. #
  186. # ix86 <=> (wordSize == 4) <=> 32 bit ==> /lib
  187. # x86_64 <=> (wordSize == 8) <=> 64 bit ==> /lib64
  188. #
  189. # Do not look into /lib64 even if present, if the cpu
  190. # doesn't fit.
  191. # TODO: Determine the prefixes (i386, x86_64, ...) for
  192. # other cpus. The path after the generic one is utterly
  193. # specific to intel right now. Ok, on Ubuntu, possibly
  194. # other Debian systems we may apparently be able to query
  195. # the necessary CPU code. If we can't we simply use the
  196. # hardwired fallback.
  197. switch -exact -- $tcl_platform(wordSize) {
  198. 4 {
  199. lappend bases /lib
  200. if {[catch {
  201. exec dpkg-architecture -qDEB_HOST_MULTIARCH
  202. } res]} {
  203. lappend bases /lib/i386-linux-gnu
  204. } else {
  205. # dpkg-arch returns the full tripled, not just cpu.
  206. lappend bases /lib/$res
  207. }
  208. }
  209. 8 {
  210. lappend bases /lib64
  211. if {[catch {
  212. exec dpkg-architecture -qDEB_HOST_MULTIARCH
  213. } res]} {
  214. lappend bases /lib/x86_64-linux-gnu
  215. } else {
  216. # dpkg-arch returns the full tripled, not just cpu.
  217. lappend bases /lib/$res
  218. }
  219. }
  220. default {
  221. return -code error "Bad wordSize $tcl_platform(wordSize), expected 4 or 8"
  222. }
  223. }
  224. foreach base $bases {
  225. if {[LibcVersion $base -> v]} break
  226. }
  227. append plat -$v
  228. return "${plat}-${cpu}"
  229. }
  230. }
  231. return $id
  232. }
  233. proc ::platform::LibcVersion {base _->_ vv} {
  234. upvar 1 $vv v
  235. set libclist [lsort [glob -nocomplain -directory $base libc*]]
  236. if {![llength $libclist]} { return 0 }
  237. set libc [lindex $libclist 0]
  238. # Try executing the library first. This should suceed
  239. # for a glibc library, and return the version
  240. # information.
  241. if {![catch {
  242. set vdata [lindex [split [exec $libc] \n] 0]
  243. }]} {
  244. regexp {version ([0-9]+(\.[0-9]+)*)} $vdata -> v
  245. foreach {major minor} [split $v .] break
  246. set v glibc${major}.${minor}
  247. return 1
  248. } else {
  249. # We had trouble executing the library. We are now
  250. # inspecting its name to determine the version
  251. # number. This code by Larry McVoy.
  252. if {[regexp -- {libc-([0-9]+)\.([0-9]+)} $libc -> major minor]} {
  253. set v glibc${major}.${minor}
  254. return 1
  255. }
  256. }
  257. return 0
  258. }
  259. # -- platform::patterns
  260. #
  261. # Given an exact platform identifier, i.e. _not_ the generic
  262. # identifier it assembles a list of exact platform identifier
  263. # describing platform which should be compatible with the
  264. # input.
  265. #
  266. # I.e. packages for all platforms in the result list should be
  267. # loadable on the specified platform.
  268. # << Should we add the generic identifier to the list as well ? In
  269. # general it is not compatible I believe. So better not. In many
  270. # cases the exact identifier is identical to the generic one
  271. # anyway.
  272. # >>
  273. proc ::platform::patterns {id} {
  274. set res [list $id]
  275. if {$id eq "tcl"} {return $res}
  276. switch -glob -- $id {
  277. solaris*-* {
  278. if {[regexp {solaris([^-]*)-(.*)} $id -> v cpu]} {
  279. if {$v eq ""} {return $id}
  280. foreach {major minor} [split $v .] break
  281. incr minor -1
  282. for {set j $minor} {$j >= 6} {incr j -1} {
  283. lappend res solaris${major}.${j}-${cpu}
  284. }
  285. }
  286. }
  287. linux*-* {
  288. if {[regexp {linux-glibc([^-]*)-(.*)} $id -> v cpu]} {
  289. foreach {major minor} [split $v .] break
  290. incr minor -1
  291. for {set j $minor} {$j >= 0} {incr j -1} {
  292. lappend res linux-glibc${major}.${j}-${cpu}
  293. }
  294. }
  295. }
  296. macosx-powerpc {
  297. lappend res macosx-universal
  298. }
  299. macosx-x86_64 {
  300. lappend res macosx-i386-x86_64
  301. }
  302. macosx-ix86 {
  303. lappend res macosx-universal macosx-i386-x86_64
  304. }
  305. macosx*-* {
  306. # 10.5+,11.0+
  307. if {[regexp {macosx([^-]*)-(.*)} $id -> v cpu]} {
  308. switch -exact -- $cpu {
  309. ix86 {
  310. lappend alt i386-x86_64
  311. lappend alt universal
  312. }
  313. x86_64 {
  314. if {[lindex [split $::tcl_platform(osVersion) .] 0] < 19} {
  315. set alt i386-x86_64
  316. } else {
  317. set alt {}
  318. }
  319. }
  320. arm {
  321. lappend alt x86_64
  322. }
  323. default { set alt {} }
  324. }
  325. if {$v ne ""} {
  326. foreach {major minor} [split $v .] break
  327. set res {}
  328. if {$major eq 11} {
  329. # Add 11.0 to 11.minor to patterns.
  330. for {set j $minor} {$j >= 0} {incr j -1} {
  331. lappend res macosx${major}.${j}-${cpu}
  332. foreach a $alt {
  333. lappend res macosx${major}.${j}-$a
  334. }
  335. }
  336. set major 10
  337. set minor 15
  338. }
  339. # Add 10.5 to 10.minor to patterns.
  340. for {set j $minor} {$j >= 5} {incr j -1} {
  341. if {$cpu ne "arm"} {
  342. lappend res macosx${major}.${j}-${cpu}
  343. }
  344. foreach a $alt {
  345. lappend res macosx${major}.${j}-$a
  346. }
  347. }
  348. # Add unversioned patterns for 10.3/10.4 builds.
  349. lappend res macosx-${cpu}
  350. foreach a $alt {
  351. lappend res macosx-$a
  352. }
  353. } else {
  354. # No version, just do unversioned patterns.
  355. foreach a $alt {
  356. lappend res macosx-$a
  357. }
  358. }
  359. } else {
  360. # no v, no cpu ... nothing
  361. }
  362. }
  363. }
  364. lappend res tcl ; # Pure tcl packages are always compatible.
  365. return $res
  366. }
  367. # ### ### ### ######### ######### #########
  368. ## Ready
  369. package provide platform 1.0.15
  370. # ### ### ### ######### ######### #########
  371. ## Demo application
  372. if {[info exists argv0] && ($argv0 eq [info script])} {
  373. puts ====================================
  374. parray tcl_platform
  375. puts ====================================
  376. puts Generic\ identification:\ [::platform::generic]
  377. puts Exact\ identification:\ \ \ [::platform::identify]
  378. puts ====================================
  379. puts Search\ patterns:
  380. puts *\ [join [::platform::patterns [::platform::identify]] \n*\ ]
  381. puts ====================================
  382. exit 0
  383. }