Canonicalize architectures
[zeroinstall.git] / zeroinstall / injector / arch.py
blob17766ccd85fd4c05b08ca85dd7d15308fac4180a
1 """
2 Information about the current system's architecture.
4 This module provides information about the current system. It is used to determine
5 whether an implementation is suitable for this machine, and to compare different implementations.
7 For example, it will indicate that:
9 - An i486 machine cannot run an i686 binary.
10 - An i686 machine can run an i486 binary, but would prefer an i586 one.
11 - A Windows binary cannot run on a Linux machine.
13 Each dictionary maps from a supported architecture type to a preference level. Lower numbers are
14 better, Unsupported architectures are not listed at all.
15 """
17 # Copyright (C) 2009, Thomas Leonard
18 # See the README file for details, or visit http://0install.net.
20 from zeroinstall import _
21 import os
23 # os_ranks and mapping are mappings from names to how good they are.
24 # 1 => Native (best)
25 # Higher numbers are worse but usable.
26 try:
27 _uname = os.uname()
28 except AttributeError:
29 # No uname. Probably Windows.
30 import sys
31 p = sys.platform
32 import platform
33 bits, linkage = platform.architecture()
34 if p == 'win32' and (bits == '' or bits == '32bit'):
35 _uname = ('Windows', 'i486')
36 elif p == 'win64' or (p == 'win32' and bits == '64bit'):
37 _uname = ('Windows', 'x86_64')
38 else:
39 _uname = (p, 'i486')
41 def _get_os_ranks(target_os):
42 if target_os.startswith('CYGWIN_NT'):
43 target_os = 'Cygwin'
44 elif target_os == 'SunOS':
45 target_os = 'Solaris'
47 # Special case Mac OS X, to separate it from Darwin/X
48 # (Mac OS X also includes the closed Apple frameworks)
49 if os.path.exists('/System/Library/Frameworks/Carbon.framework'):
50 target_os = 'MacOSX'
52 # Binaries compiled for _this_ OS are best...
53 os_ranks = {target_os : 1}
55 # If target_os appears in the first column of this table, all
56 # following OS types on the line will also run on this one
57 # (earlier ones preferred):
58 _os_matrix = {
59 'Cygwin': ['Windows'],
60 'MacOSX': ['Darwin'],
63 for supported in _os_matrix.get(target_os, []):
64 os_ranks[supported] = len(os_ranks) + 1
66 # At the lowest priority, try an OS-independent implementation
67 os_ranks[None] = len(os_ranks) + 1
68 return os_ranks
70 os_ranks = _get_os_ranks(_uname[0])
72 # All chosen machine-specific implementations must come from the same group
73 # Unlisted archs are in group 0
74 machine_groups = {
75 'x86_64': 64,
76 'ppc64': 64,
79 def _get_machine_ranks(target_machine):
80 if target_machine == 'x86':
81 target_machine = 'i386'
82 elif target_machine == 'amd64':
83 target_machine = 'x86_64'
84 elif target_machine == 'Power Macintosh':
85 target_machine = 'ppc'
86 elif target_machine == 'i86pc':
87 target_machine = 'i686'
89 # Binaries compiled for _this_machine are best...
90 machine_ranks = {target_machine : 0}
92 # If target_machine appears in the first column of this table, all
93 # following machine types on the line will also run on this one
94 # (earlier ones preferred):
95 _machine_matrix = {
96 'i486': ['i386'],
97 'i586': ['i486', 'i386'],
98 'i686': ['i586', 'i486', 'i386'],
99 'x86_64': ['i686', 'i586', 'i486', 'i386'],
100 'ppc': ['ppc32'],
101 'ppc64': ['ppc'],
103 for supported in _machine_matrix.get(target_machine, []):
104 machine_ranks[supported] = len(machine_ranks)
106 # At the lowest priority, try a machine-independant implementation
107 machine_ranks[None] = len(machine_ranks)
108 return machine_ranks
110 machine_ranks = _get_machine_ranks(_uname[-1])
112 class Architecture:
113 """A description of an architecture. Use by L{solver} to make sure it chooses
114 compatible versions.
115 @ivar os_ranks: supported operating systems and their desirability
116 @type os_ranks: {str: int}
117 @ivar machine_ranks: supported CPU types and their desirability
118 @type machine_ranks: {str: int}
119 @ivar child_arch: architecture for dependencies (usually C{self})
120 @type child_arch: L{Architecture}
121 @ivar use: matching values for <requires use='...'>; otherwise the dependency is ignored
122 @type use: set(str)
125 use = frozenset([None])
127 def __init__(self, os_ranks, machine_ranks):
128 self.os_ranks = os_ranks
129 self.machine_ranks = machine_ranks
130 self.child_arch = self
132 def __str__(self):
133 return _("<Arch: %(os_ranks)s %(machine_ranks)s>") % {'os_ranks': self.os_ranks, 'machine_ranks': self.machine_ranks}
135 class SourceArchitecture(Architecture):
136 """Matches source code that creates binaries for a particular architecture.
137 Note that the L{child_arch} here is the binary; source code depends on binary tools,
138 not on other source packages.
140 def __init__(self, binary_arch):
141 Architecture.__init__(self, binary_arch.os_ranks, {'src': 1})
142 self.child_arch = binary_arch
144 def get_host_architecture():
145 """Get an Architecture that matches implementations that will run on the host machine.
146 @rtype: L{Architecture}"""
147 return Architecture(os_ranks, machine_ranks)
149 def get_architecture(os, machine):
150 """Get an Architecture that matches binaries that will work on the given system.
151 @param os: OS type, or None for host's type
152 @param machine: CPU type, or None for host's type
153 @return: an Architecture object
154 @rtype: L{Architecture}"""
156 if os is None:
157 target_os_ranks = os_ranks
158 else:
159 target_os_ranks = _get_os_ranks(os)
160 if machine is None:
161 target_machine_ranks = machine_ranks
162 else:
163 target_machine_ranks = _get_machine_ranks(machine)
165 return Architecture(target_os_ranks, target_machine_ranks)