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