==SCons==
[blender.git] / tools / Blender.py
1 #!/usr/bin/env python
2
3 """
4 tools.BlenderEnvironment
5
6 This environment builds on SCons.Script.SConscript.SConsEnvironment
7
8 * library repository
9 * custom printout
10 * wrapper functions
11
12 TODO: clean up and sanitise code - crosscheck with btools and SConstruct
13 to kill any code duplication
14 """
15
16 import os.path
17 import string
18 import glob
19 import time
20 import sys
21
22 from SCons.Script.SConscript import SConsEnvironment
23 import SCons.Action
24 import SCons.Util
25 import SCons.Builder
26 import SCons.Tool
27 import bcolors
28 bc = bcolors.bcolors()
29
30 Split = SCons.Util.Split
31 Action = SCons.Action.Action
32 Builder = SCons.Builder.Builder
33 GetBuildPath = SConsEnvironment.GetBuildPath
34
35 # a few globals
36 root_build_dir = ''
37 quickie = None # Anything else than None if BF_QUICK has been passed
38 quicklist = [] # The list of libraries/programs to compile during a quickie
39 program_list = [] # A list holding Nodes to final binaries, used to create installs
40 arguments = None
41 targets = None
42
43 #some internals
44 blenderdeps = [] # don't manipulate this one outside this module!
45
46 ##### LIB STUFF ##########
47
48 possible_types = ['core'] # can be set in ie. SConstruct
49 libs = {}
50 def init_lib_dict():
51     for pt in possible_types:
52         libs[pt] = {}
53
54 # helper func for add_lib_to_dict
55 def internal_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
56     if not libname in dict[libtype]:
57         done = None
58         while not done:
59             if dict[libtype].has_key(priority):
60                 priority = priority + 1
61             else:
62                 done = True
63         dict[libtype][priority] = libname
64
65 # libtype and priority can both be lists, for defining lib in multiple places
66 def add_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
67     if not dict or not libtype or not libname:
68         print "Passed wrong arg"
69         Exit()
70
71     if type(libtype) is str and type(priority) is int:
72         internal_lib_to_dict(dict, libtype, libname, priority)
73     elif type(libtype) is list and type(priority) is list:
74         if len(libtype)==len(priority):
75             for lt, p in zip(libtype, priority):
76                 internal_lib_to_dict(dict, lt, libname, p)
77         else:
78             print "libtype and priority lists are unequal in length"
79             Exit()
80     else:
81         print "Wrong type combinations for libtype and priority. Only str and int or list and list"
82         Exit()
83
84 #libs = init_lib_dict(libs)
85
86 def create_blender_liblist(lenv = None, libtype = None):
87     if not lenv or not libtype:
88         print "missing arg"
89
90     lst = []
91     if libtype in possible_types:
92         sortlist = []
93         for k,v in libs[libtype].iteritems():
94             sortlist.append(k)
95         sortlist.sort()
96         curlib = libs[libtype]
97         for sk in sortlist:
98             v = curlib[sk]
99         #for k,v in sorted(libs[libtype].iteritems()):
100             lst.append('#' + root_build_dir + 'lib/'+lenv['LIBPREFIX'] + v + lenv['LIBSUFFIX'])
101
102     return lst
103
104 ## TODO: static linking
105 def setup_staticlibs(lenv):
106     statlibs = [
107         #here libs for static linking
108     ]
109     libincs = [
110         '/usr/lib',
111         lenv['BF_PYTHON_LIBPATH'],
112         lenv['BF_OPENGL_LIBPATH'],
113         lenv['BF_SDL_LIBPATH'],
114         lenv['BF_JPEG_LIBPATH'],
115         lenv['BF_TIFF_LIBPATH'],
116         lenv['BF_PNG_LIBPATH'],
117         lenv['BF_GETTEXT_LIBPATH'],
118         lenv['BF_ZLIB_LIBPATH'],
119         lenv['BF_OPENAL_LIBPATH'],
120         lenv['BF_FREETYPE_LIBPATH'],
121 #        lenv['BF_QUICKTIME_LIBPATH'],
122         lenv['BF_ICONV_LIBPATH']
123         ]
124     libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
125
126     return statlibs, libincs
127
128 def setup_syslibs(lenv):
129     syslibs = [
130         lenv['BF_PYTHON_LIB'],
131         lenv['BF_JPEG_LIB'],
132         lenv['BF_PNG_LIB'],
133         lenv['BF_ZLIB_LIB'],
134         lenv['BF_OPENAL_LIB'],
135         lenv['BF_FREETYPE_LIB'],
136         lenv['BF_GETTEXT_LIB']
137
138         #here libs for linking
139         ]
140     if lenv['OURPLATFORM']=='win32vc':
141             syslibs += Split(lenv['BF_ICONV_LIB'])
142     syslibs += Split(lenv['BF_TIFF_LIB'])
143     syslibs += Split(lenv['BF_OPENEXR_LIB'])
144     syslibs += Split(lenv['BF_SDL_LIB'])
145     syslibs += Split(lenv['BF_OPENGL_LIB'])
146     syslibs += Split(lenv['LLIBS'])
147
148     return syslibs
149
150 def propose_priorities():
151     print bc.OKBLUE+"Priorities:"+bc.ENDC
152     for t in possible_types:
153         print bc.OKGREEN+"\t"+t+bc.ENDC
154         new_priority = 0
155         sortlist = []
156         for k,v in libs[t].iteritems():
157             sortlist.append(k)
158         sortlist.sort()
159         curlib = libs[t]
160         for sk in sortlist:
161             v = curlib[sk]
162             #for p,v in sorted(libs[t].iteritems()):
163             print "\t\t",new_priority, v
164             new_priority += 5
165
166 ## TODO: see if this can be made in an emitter
167 def buildinfo(lenv, build_type):
168     """
169     Generate a buildinfo object
170     """
171     build_date = time.strftime ("%Y-%m-%d")
172     build_time = time.strftime ("%H:%M:%S")
173     obj = []
174     if lenv['BF_BUILDINFO']==1: #user_options_dict['USE_BUILDINFO'] == 1:
175         if sys.platform=='win32':
176             build_info_file = open("source/creator/winbuildinfo.h", 'w')
177             build_info_file.write("char *build_date=\"%s\";\n"%build_date)
178             build_info_file.write("char *build_time=\"%s\";\n"%build_time)
179             build_info_file.write("char *build_platform=\"win32\";\n")
180             build_info_file.write("char *build_type=\"dynamic\";\n")
181             build_info_file.close()
182             lenv.Append (CPPDEFINES = ['NAN_BUILDINFO', 'BUILD_DATE'])
183         else:
184             lenv.Append (CPPDEFINES = ['BUILD_TIME=\'"%s"\''%(build_time),
185                                         'BUILD_DATE=\'"%s"\''%(build_date),
186                                         'BUILD_TYPE=\'"dynamic"\'',
187                                         'NAN_BUILDINFO',
188                                         'BUILD_PLATFORM=\'"%s"\''%(sys.platform)])
189         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type,
190                         [root_build_dir+'source/creator/buildinfo.c'])]
191     return obj
192
193 ##### END LIB STUFF ############
194
195 ##### ACTION STUFF #############
196
197 def my_compile_print(target, source, env):
198     a = '%s' % (source[0])
199     d, f = os.path.split(a)
200     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
201
202 def my_moc_print(target, source, env):
203     a = '%s' % (source[0])
204     d, f = os.path.split(a)
205     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
206
207 def my_linking_print(target, source, env):
208     t = '%s' % (target[0])
209     d, f = os.path.split(t)
210     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
211
212 def my_program_print(target, source, env):
213     t = '%s' % (target[0])
214     d, f = os.path.split(t)
215     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
216
217 def msvc_hack(env):
218     static_lib = SCons.Tool.createStaticLibBuilder(env)
219     program = SCons.Tool.createProgBuilder(env)
220     
221     env['BUILDERS']['Library'] = static_lib
222     env['BUILDERS']['StaticLibrary'] = static_lib
223     env['BUILDERS']['Program'] = program
224         
225 def set_quiet_output(env):
226     mycaction = Action("$CCCOM", strfunction=my_compile_print)
227     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
228     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
229     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
230     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
231     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
232
233     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
234     static_ob.add_action('.c', mycaction)
235     static_ob.add_action('.cpp', mycppaction)
236     shared_ob.add_action('.c', myshcaction)
237     shared_ob.add_action('.cpp', myshcppaction)
238
239     static_lib = SCons.Builder.Builder(action = mylibaction,
240                                        emitter = '$LIBEMITTER',
241                                        prefix = '$LIBPREFIX',
242                                        suffix = '$LIBSUFFIX',
243                                        src_suffix = '$OBJSUFFIX',
244                                        src_builder = 'StaticObject')
245
246     program = SCons.Builder.Builder(action = mylinkaction,
247                                     emitter = '$PROGEMITTER',
248                                     prefix = '$PROGPREFIX',
249                                     suffix = '$PROGSUFFIX',
250                                     src_suffix = '$OBJSUFFIX',
251                                     src_builder = 'Object',
252                                     target_scanner = SCons.Defaults.ProgScan)
253
254     env['BUILDERS']['Object'] = static_ob
255     env['BUILDERS']['StaticObject'] = static_ob
256     env['BUILDERS']['StaticLibrary'] = static_lib
257     env['BUILDERS']['Library'] = static_lib
258     env['BUILDERS']['Program'] = program
259
260
261 #### END ACTION STUFF #########
262
263 class BlenderEnvironment(SConsEnvironment):
264
265     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None):
266         if not self or not libname or not sources:
267             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
268             Exit()
269         if libname in quickie or len(quickie)==0:
270             print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
271             lenv = self.Copy()
272             lenv.Append(CPPPATH=includes)
273             lenv.Append(CPPDEFINES=defines)
274             if lenv['WITH_BF_GAMEENGINE']:
275                     lenv.Append(CPPDEFINES=['GAMEBLENDER=1'])
276             if lenv['BF_DEBUG']:
277                     lenv.Append(CFLAGS = lenv['BF_DEBUG_FLAGS'], CCFLAGS = lenv['BF_DEBUG_FLAGS'])
278             else:
279                     lenv.Append(CFLAGS = lenv['REL_CFLAGS'], CCFLAGS = lenv['REL_CCFLAGS'])
280             if lenv['BF_PROFILE']:
281                     lenv.Append(CFLAGS = lenv['BF_PROFILE_FLAGS'], CCFLAGS = lenv['BF_PROFILE_FLAGS'])
282             if compileflags:
283                 lenv.Append(CFLAGS = compileflags)
284                 lenv.Append(CCFLAGS = compileflags)
285             lenv.Append(CFLAGS = Split(lenv['C_WARN']))
286             lenv.Append(CCFLAGS = Split(lenv['CC_WARN']))
287             lib = lenv.Library(target= '#'+root_build_dir+'lib/'+libname, source=sources)
288             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
289         else:
290             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
291         # note: libs is a global
292         add_lib_to_dict(libs, libtype, libname, priority)
293
294     def BlenderProg(self=None, builddir=None, progname=None, sources=None, includes=None, libs=None, libpath=None):
295         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
296         lenv = self.Copy()
297         if lenv['OURPLATFORM']=='win32-vc':
298             lenv.Append(LINKFLAGS = Split(lenv['PLATFORM_LINKFLAGS']))
299         if  lenv['OURPLATFORM']=='darwin':
300             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
301             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
302             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
303         lenv.Append(CPPPATH=includes)
304         lenv.Append(LIBPATH=libpath)
305         lenv.Append(LIBS=libs)
306         if lenv['WITH_BF_QUICKTIME']:
307              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
308              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
309         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
310         SConsEnvironment.Default(self, prog)
311         program_list.append(prog)
312
313 ## TODO: have register for libs/programs, so that we test only that
314 #  which have expressed their need to be tested in their own sconscript
315     def BlenderUnitTest(env, source, **kwargs):
316         test = env.Program(source, **kwargs)
317         env.AddPostAction(test, test[0].abspath)
318         env.Alias('check', test)
319         env.AlwaysBuild(test)
320         return test
321
322     def Glob(lenv, pattern):
323         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
324         files = []
325         for i in glob.glob(path + pattern):
326             files.append(string.replace(i, path, ''))
327         return files