tweak SCons to use Split() to parse BF_DEBUG_FLAGS and BF_PROFILE_FLAGS
[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 resources = []
43
44 #some internals
45 blenderdeps = [] # don't manipulate this one outside this module!
46
47 ##### LIB STUFF ##########
48
49 possible_types = ['core'] # can be set in ie. SConstruct
50 libs = {}
51
52 def getresources():
53     return resources
54
55 def init_lib_dict():
56     for pt in possible_types:
57         libs[pt] = {}
58
59 # helper func for add_lib_to_dict
60 def internal_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
61     if not libname in dict[libtype]:
62         done = None
63         while not done:
64             if dict[libtype].has_key(priority):
65                 priority = priority + 1
66             else:
67                 done = True
68         dict[libtype][priority] = libname
69
70 # libtype and priority can both be lists, for defining lib in multiple places
71 def add_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
72     if not dict or not libtype or not libname:
73         print "Passed wrong arg"
74         Exit()
75
76     if type(libtype) is str and type(priority) is int:
77         internal_lib_to_dict(dict, libtype, libname, priority)
78     elif type(libtype) is list and type(priority) is list:
79         if len(libtype)==len(priority):
80             for lt, p in zip(libtype, priority):
81                 internal_lib_to_dict(dict, lt, libname, p)
82         else:
83             print "libtype and priority lists are unequal in length"
84             Exit()
85     else:
86         print "Wrong type combinations for libtype and priority. Only str and int or list and list"
87         Exit()
88
89 def create_blender_liblist(lenv = None, libtype = None):
90     if not lenv or not libtype:
91         print "missing arg"
92
93     lst = []
94     if libtype in possible_types:
95         sortlist = []
96         for k,v in libs[libtype].iteritems():
97             sortlist.append(k)
98         sortlist.sort()
99         curlib = libs[libtype]
100         for sk in sortlist:
101             v = curlib[sk]
102             lst.append('#' + root_build_dir + 'lib/'+lenv['LIBPREFIX'] + v + lenv['LIBSUFFIX'])
103
104     return lst
105
106 ## TODO: static linking
107 def setup_staticlibs(lenv):
108     statlibs = [
109         #here libs for static linking
110     ]
111     libincs = [
112         '/usr/lib',
113         lenv['BF_PYTHON_LIBPATH'],
114         lenv['BF_OPENGL_LIBPATH'],
115         lenv['BF_SDL_LIBPATH'],
116         lenv['BF_JPEG_LIBPATH'],
117         lenv['BF_PNG_LIBPATH'],
118         lenv['BF_ZLIB_LIBPATH'],
119         lenv['BF_ICONV_LIBPATH']
120         ]
121     libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
122
123     if lenv['WITH_BF_INTERNATIONAL']:
124         libincs += Split(lenv['BF_GETTEXT_LIBPATH'])
125         libincs += Split(lenv['BF_FREETYPE_LIBPATH'])
126     if lenv['WITH_BF_OPENAL']:
127         libincs += Split(lenv['BF_OPENAL_LIBPATH'])
128
129     if lenv['WITH_BF_STATICOPENGL']:
130         statlibs += Split(lenv['BF_OPENGL_LIB_STATIC'])
131
132     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross'):
133         libincs += Split(lenv['BF_PTHREADS_LIBPATH'])
134
135     return statlibs, libincs
136
137 def setup_syslibs(lenv):
138     syslibs = [
139         lenv['BF_PYTHON_LIB'],
140         lenv['BF_JPEG_LIB'],
141         lenv['BF_PNG_LIB'],
142         lenv['BF_ZLIB_LIB']
143         ]
144     if lenv['WITH_BF_INTERNATIONAL']:
145         syslibs += Split(lenv['BF_FREETYPE_LIB'])
146         syslibs += Split(lenv['BF_GETTEXT_LIB'])
147     if lenv['WITH_BF_OPENAL']:
148        syslibs += Split(lenv['BF_OPENAL_LIB'])
149     if lenv['OURPLATFORM']=='win32vc':
150         syslibs += Split(lenv['BF_ICONV_LIB'])
151     if lenv['WITH_BF_OPENEXR']:
152         syslibs += Split(lenv['BF_OPENEXR_LIB'])
153     if lenv['WITH_BF_FFMPEG']:
154         syslibs += Split(lenv['BF_FFMPEG_LIB'])
155     syslibs += Split(lenv['BF_SDL_LIB'])
156     if not lenv['WITH_BF_STATICOPENGL']:
157         syslibs += Split(lenv['BF_OPENGL_LIB'])
158     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw','linuxcross'):
159         syslibs += Split(lenv['BF_PTHREADS_LIB'])
160
161     syslibs += Split(lenv['LLIBS'])
162
163     return syslibs
164
165 def propose_priorities():
166     print bc.OKBLUE+"Priorities:"+bc.ENDC
167     for t in possible_types:
168         print bc.OKGREEN+"\t"+t+bc.ENDC
169         new_priority = 0
170         sortlist = []
171         for k,v in libs[t].iteritems():
172             sortlist.append(k)
173         sortlist.sort()
174         curlib = libs[t]
175         for sk in sortlist:
176             v = curlib[sk]
177             #for p,v in sorted(libs[t].iteritems()):
178             print "\t\t",new_priority, v
179             new_priority += 5
180
181 ## TODO: see if this can be made in an emitter
182 def buildinfo(lenv, build_type):
183     """
184     Generate a buildinfo object
185     """
186     build_date = time.strftime ("%Y-%m-%d")
187     build_time = time.strftime ("%H:%M:%S")
188     obj = []
189     if lenv['BF_BUILDINFO']==1: #user_options_dict['USE_BUILDINFO'] == 1:
190         if sys.platform=='win32':
191             build_info_file = open("source/creator/winbuildinfo.h", 'w')
192             build_info_file.write("char *build_date=\"%s\";\n"%build_date)
193             build_info_file.write("char *build_time=\"%s\";\n"%build_time)
194             build_info_file.write("char *build_platform=\"win32\";\n")
195             build_info_file.write("char *build_type=\"dynamic\";\n")
196             build_info_file.close()
197             lenv.Append (CPPDEFINES = ['NAN_BUILDINFO', 'BUILD_DATE'])
198         else:
199             lenv.Append (CPPDEFINES = ['BUILD_TIME=\'"%s"\''%(build_time),
200                                         'BUILD_DATE=\'"%s"\''%(build_date),
201                                         'BUILD_TYPE=\'"dynamic"\'',
202                                         'NAN_BUILDINFO',
203                                         'BUILD_PLATFORM=\'"%s"\''%(sys.platform)])
204         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type,
205                         [root_build_dir+'source/creator/buildinfo.c'])]
206     return obj
207
208 ##### END LIB STUFF ############
209
210 ##### ACTION STUFF #############
211
212 def my_compile_print(target, source, env):
213     a = '%s' % (source[0])
214     d, f = os.path.split(a)
215     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
216
217 def my_moc_print(target, source, env):
218     a = '%s' % (source[0])
219     d, f = os.path.split(a)
220     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
221
222 def my_linking_print(target, source, env):
223     t = '%s' % (target[0])
224     d, f = os.path.split(t)
225     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
226
227 def my_program_print(target, source, env):
228     t = '%s' % (target[0])
229     d, f = os.path.split(t)
230     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
231
232 def msvc_hack(env):
233     static_lib = SCons.Tool.createStaticLibBuilder(env)
234     program = SCons.Tool.createProgBuilder(env)
235     
236     env['BUILDERS']['Library'] = static_lib
237     env['BUILDERS']['StaticLibrary'] = static_lib
238     env['BUILDERS']['Program'] = program
239         
240 def set_quiet_output(env):
241     mycaction = Action("$CCCOM", strfunction=my_compile_print)
242     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
243     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
244     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
245     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
246     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
247
248     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
249     static_ob.add_action('.c', mycaction)
250     static_ob.add_action('.cpp', mycppaction)
251     shared_ob.add_action('.c', myshcaction)
252     shared_ob.add_action('.cpp', myshcppaction)
253
254     static_lib = SCons.Builder.Builder(action = mylibaction,
255                                        emitter = '$LIBEMITTER',
256                                        prefix = '$LIBPREFIX',
257                                        suffix = '$LIBSUFFIX',
258                                        src_suffix = '$OBJSUFFIX',
259                                        src_builder = 'StaticObject')
260
261     program = SCons.Builder.Builder(action = mylinkaction,
262                                     emitter = '$PROGEMITTER',
263                                     prefix = '$PROGPREFIX',
264                                     suffix = '$PROGSUFFIX',
265                                     src_suffix = '$OBJSUFFIX',
266                                     src_builder = 'Object',
267                                     target_scanner = SCons.Defaults.ProgScan)
268
269     env['BUILDERS']['Object'] = static_ob
270     env['BUILDERS']['StaticObject'] = static_ob
271     env['BUILDERS']['StaticLibrary'] = static_lib
272     env['BUILDERS']['Library'] = static_lib
273     env['BUILDERS']['Program'] = program
274
275 def  my_appit_print(target, source, env):
276     a = '%s' % (target[0])
277     d, f = os.path.split(a)
278     return "making bundle for " + f
279
280 def AppIt(target=None, source=None, env=None):
281     import shutil
282     import commands
283     import os.path
284     
285     a = '%s' % (target[0])
286     builddir, b = os.path.split(a)
287     bldroot = env.Dir('.').abspath
288     binary = env['BINARYKIND']
289     
290     sourcedir = bldroot + '/source/darwin/%s.app'%binary
291     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
292     targetinfo = builddir +'/' + "%s.app/Contents/Info.plist"%binary
293     cmd = builddir + '/' +'%s.app'%binary
294     
295     if os.path.isdir(cmd):
296         shutil.rmtree(cmd)
297     shutil.copytree(sourcedir, cmd)
298     cmd = "cat %s | sed s/VERSION/`cat release/VERSION`/ | sed s/DATE/`date +'%%Y-%%b-%%d'`/ > %s"%(sourceinfo,targetinfo)
299     commands.getoutput(cmd)
300     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,builddir, binary, binary)
301     commands.getoutput(cmd)
302     cmd = 'mkdir %s/%s.app/Contents/MacOS/.blender/'%(builddir, binary)
303     print cmd
304     commands.getoutput(cmd)
305     cmd = builddir + '/%s.app/Contents/MacOS/.blender'%binary
306     shutil.copy(bldroot + '/bin/.blender/.bfont.ttf', cmd)
307     shutil.copy(bldroot + '/bin/.blender/.Blanguages', cmd)
308     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/Resources/'%(bldroot,builddir,binary)
309     commands.getoutput(cmd) 
310     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
311     commands.getoutput(cmd) 
312     cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
313     commands.getoutput(cmd)
314     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(builddir,binary, binary)
315     commands.getoutput(cmd)
316     cmd = 'find %s/%s.app -name CVS -prune -exec rm -rf {} \;'%(builddir, binary)
317     commands.getoutput(cmd)
318     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(builddir, binary)
319     commands.getoutput(cmd)
320
321 #### END ACTION STUFF #########
322
323 class BlenderEnvironment(SConsEnvironment):
324
325     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
326         global libs
327         if not self or not libname or not source:
328             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
329             Exit()
330         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross'):
331             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
332             Exit()
333         
334         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
335         lenv = self.Copy()
336         res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
337       
338         SConsEnvironment.Default(self, res)
339         resources.append(res)
340
341     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None):
342         if not self or not libname or not sources:
343             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
344             Exit()
345         if libname in quickie or len(quickie)==0:
346             print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
347             lenv = self.Copy()
348             lenv.Append(CPPPATH=includes)
349             lenv.Append(CPPDEFINES=defines)
350             if lenv['WITH_BF_GAMEENGINE']:
351                     lenv.Append(CPPDEFINES=['GAMEBLENDER=1'])
352             if lenv['BF_DEBUG']:
353                     lenv.Append(CCFLAGS = Split(lenv['BF_DEBUG_FLAGS']), CXXFLAGS = Split(lenv['BF_DEBUG_FLAGS']))
354             else:
355                     lenv.Append(CCFLAGS = lenv['REL_CFLAGS'], CXXFLAGS = lenv['REL_CCFLAGS'])
356             if lenv['BF_PROFILE']:
357                     lenv.Append(CCFLAGS = Split(lenv['BF_PROFILE_FLAGS']), CXXFLAGS = Split(lenv['BF_PROFILE_FLAGS']))
358             if compileflags:
359                 lenv.Append(CCFLAGS = compileflags)
360                 lenv.Append(CXXFLAGS = compileflags)
361             lenv.Append(CCFLAGS = Split(lenv['C_WARN']))
362             lenv.Append(CXXFLAGS = Split(lenv['CC_WARN']))
363             lib = lenv.Library(target= '#'+root_build_dir+'lib/'+libname, source=sources)
364             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
365         else:
366             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
367         # note: libs is a global
368         add_lib_to_dict(libs, libtype, libname, priority)
369
370     def BlenderProg(self=None, builddir=None, progname=None, sources=None, includes=None, libs=None, libpath=None, binarykind=''):
371         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
372         lenv = self.Copy()
373         if lenv['OURPLATFORM'] in ['win32-vc', 'cygwin']:
374             lenv.Append(LINKFLAGS = Split(lenv['PLATFORM_LINKFLAGS']))
375         if  lenv['OURPLATFORM']=='darwin':
376             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
377             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
378             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
379         if lenv['BF_PROFILE']:
380                 lenv.Append(LINKFLAGS = lenv['BF_PROFILE_FLAGS'])
381         lenv.Append(CPPPATH=includes)
382         lenv.Append(LIBPATH=libpath)
383         lenv.Append(LIBS=libs)
384         if lenv['WITH_BF_QUICKTIME']:
385              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
386              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
387         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
388         SConsEnvironment.Default(self, prog)
389         program_list.append(prog)
390         if  lenv['OURPLATFORM']=='darwin':
391             lenv['BINARYKIND'] = binarykind
392             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
393
394     def Glob(lenv, pattern):
395         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
396         files = []
397         for i in glob.glob(path + pattern):
398             files.append(string.replace(i, path, ''))
399         return files