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