platform-1.0.18.tm 11 KB

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