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