ice.bbclass, apply patch from #1495, closes #1495
[openembedded.git] / classes / icecc.bbclass
1 # IceCream distributed compiling support
2 #
3 # Stages directories with symlinks from gcc/g++ to icecc, for both
4 # native and cross compilers. Depending on each configure or compile,
5 # the directories are added at the head of the PATH list and ICECC_CXX
6 # and ICEC_CC are set.
7 #
8 # For the cross compiler, creates a tar.bz2 of our toolchain and sets
9 # ICECC_VERSION accordingly.
10 #
11 # This class needs ICECC_PATH to be set already. It must have
12 # been exported from the shell running bitbake. Setting it in
13 # local.conf is not adequate.
14 #
15 # This class objdump, ldconfig, grep, sed installed on the build host.
16
17 def icc_determine_gcc_version(gcc):
18     """
19     Hack to determine the version of GCC
20
21     'i686-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc. build 5363)'
22     """
23     import os
24     return os.popen("%s --version" % gcc ).readline().split()[2]
25
26 def create_env(bb,d):
27     """
28     Create a tar.bz2 of the current toolchain
29     """
30
31     # Constin native-native compilation no environment needed if
32     # host prefix is empty (let us duplicate the query for ease)
33     prefix = bb.data.expand('${HOST_PREFIX}', d)
34     if len(prefix) == 0:
35         return ""
36
37     import tarfile, socket, time, os
38     ice_dir = bb.data.expand('${CROSS_DIR}', d)
39     prefix  = bb.data.expand('${HOST_PREFIX}' , d)
40     distro  = bb.data.expand('${DISTRO}', d)
41     target_sys = bb.data.expand('${TARGET_SYS}',  d)
42     target_prefix = bb.data.expand('${TARGET_PREFIX}',  d)
43     float   = bb.data.getVar('${TARGET_FPU}', d) or "hard"
44     name    = socket.gethostname()
45
46     # Stupid check to determine if we have built a libc and a cross
47     # compiler.
48     try:
49         os.stat(os.path.join(ice_dir, target_sys, 'lib', 'ld-linux.so.2'))
50         os.stat(os.path.join(ice_dir, target_sys, 'bin', 'g++'))
51     except: # no cross compiler built yet
52         return ""
53
54     VERSION = icc_determine_gcc_version( os.path.join(ice_dir,target_sys,"bin","g++") )
55     cross_name = prefix + distro + target_sys + float +VERSION+ name
56     tar_file = os.path.join(ice_dir, 'ice', cross_name + '.tar.bz2')
57
58     try:
59         os.stat(tar_file)
60         # tar file already exists
61         return tar_file
62     except: 
63         try:
64             os.makedirs(os.path.join(ice_dir,'ice'))
65         except:
66             # directory already exists, continue
67             pass
68
69     # FIXME find out the version of the compiler
70     # Consider using -print-prog-name={cc1,cc1plus}
71     # and            -print-file-name=specs
72
73     # We will use the GCC to tell us which tools to use
74     #  What we need is:
75     #        -gcc
76     #        -g++
77     #        -as
78     #        -cc1
79     #        -cc1plus
80     #  and we add them to /usr/bin
81
82     tar = tarfile.open(tar_file, 'w:bz2')
83
84     # Now add the required files
85     tar.add(os.path.join(ice_dir,target_sys,'bin','gcc'),
86             os.path.join("usr","bin","gcc") )
87     tar.add(os.path.join(ice_dir,target_sys,'bin','g++'),
88             os.path.join("usr","bin","g++") )
89     tar.add(os.path.join(ice_dir,target_sys,'bin','as'),
90             os.path.join("usr","bin","as") )
91
92     cc = bb.data.getVar('CC', d, True)
93
94     # use bitbake's PATH so that the cross-compiler is actually found on the PATH
95     oldpath = os.environ['PATH']
96     os.environ['PATH'] = bb.data.getVar('PATH', d, True)
97
98     # FIXME falsely assuming there is only a single NEEDED per file
99     # FIXME falsely assuming the lib path is /lib
100
101     # which libc does the compiler need? (for example: libc.so.6)
102     libc = os.popen("objdump -x `which %s` | sed -n 's/.*NEEDED *//p'" % cc).read()[:-1]
103     # what is the absolute path of libc? (for example: /lib/libc.so.6)
104     # FIXME assuming only one entry is returned, which easily breaks
105     libc = os.popen("ldconfig -p | grep -e %s$ | sed 's:[^/]*/:/:'" % libc).read()[:-1]
106
107     # which loader does the compiler need?
108     ldlinux = os.popen("objdump -x %s | sed -n 's/.*NEEDED *//p'" % libc).read()[:-1]
109     ldlinux = os.popen("ldconfig -p | grep -e %s$ | sed 's:[^/]*/:/:'" % ldlinux).read()[:-1]
110
111     tar.add(libc)
112     tar.add(ldlinux)
113   
114     # Now let us find cc1 and cc1plus
115     cc1 = os.popen("%s -print-prog-name=cc1" % cc).read()[:-1]
116     cc1plus = os.popen("%s -print-prog-name=cc1plus" % cc).read()[:-1]
117     spec = os.popen("%s -print-file-name=specs" % cc).read()[:-1]
118
119     os.environ['PATH'] = oldpath
120
121     # CC1 and CC1PLUS should be there...
122     #tar.add(cc1, os.path.join('usr', 'bin', 'cc1'))
123     #tar.add(cc1plus, os.path.join('usr', 'bin', 'cc1plus'))
124
125     # I think they should remain absolute paths (as gcc expects them there)
126     tar.add(cc1)
127     tar.add(cc1plus)
128
129     # spec - if it exists
130     if os.path.exists(spec):
131         tar.add(spec)
132
133     tar.close()
134     return tar_file
135
136
137 def create_path(compilers, type, bb, d):
138     """
139     Create Symlinks for the icecc in the staging directory
140     """
141     import os
142
143     staging = os.path.join(bb.data.expand('${STAGING_DIR}', d), "ice", type)
144     icecc   = bb.data.getVar('ICECC_PATH', d)
145
146     # Create the dir if necessary
147     try:
148         os.stat(staging)
149     except:
150         os.makedirs(staging)
151
152     for compiler in compilers:
153         gcc_path = os.path.join(staging, compiler)
154         try:
155             os.stat(gcc_path)
156         except:
157             os.symlink(icecc, gcc_path)
158
159     return staging + ":"
160
161 def use_icc_version(bb,d):
162     # Constin native native
163     prefix = bb.data.expand('${HOST_PREFIX}', d)
164     if len(prefix) == 0:
165         return "no"
166
167     blacklist = [ "cross", "native" ]
168
169     for black in blacklist:
170         if bb.data.inherits_class(black, d):
171             return "no"
172
173     return "yes"
174
175 def icc_path(bb,d,compile):
176     native = bb.data.expand('${PN}', d)
177     blacklist = [ "ulibc", "glibc", "ncurses" ]
178     for black in blacklist:
179         if black in native:
180             return ""
181
182     blacklist = [ "cross", "native" ]
183     for black in blacklist:
184         if bb.data.inherits_class(black, d):
185             compile = False
186
187     prefix = bb.data.expand('${HOST_PREFIX}', d)
188     if compile and len(prefix) != 0:
189         return create_path( [prefix+"gcc", prefix+"g++"], "cross", bb, d)
190     elif not compile or len(prefix) == 0:
191         return create_path( ["gcc", "g++"], "native", bb, d)
192
193 def icc_version(bb,d):
194     return create_env(bb,d)
195
196 #
197 # set the icecream environment variables
198 do_configure_prepend() {
199     export PATH=${@icc_path(bb,d,False)}$PATH
200     export ICECC_CC="gcc"
201     export ICECC_CXX="g++"
202 }
203
204 do_compile_prepend() {
205     export PATH=${@icc_path(bb,d,True)}$PATH
206     export ICECC_CC="${HOST_PREFIX}gcc"
207     export ICECC_CXX="${HOST_PREFIX}g++"
208
209     if [ "${@use_icc_version(bb,d)}" = "yes" ]; then
210         print ICECC_VERSION="${@icc_version(bb,d)}"
211         export ICECC_VERSION="${@icc_version(bb,d)}"
212     fi
213 }