e3d4740d0e5671c5f8310a7096e4e4f10afd1382
[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_FREETYPE_LIBPATH'],
120 #        lenv['BF_QUICKTIME_LIBPATH'],
121         lenv['BF_ICONV_LIBPATH']
122         ]
123     libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
124
125     if lenv['WITH_BF_OPENAL']:
126         lenv['BF_OPENAL_LIBPATH']
127
128     return statlibs, libincs
129
130 def setup_syslibs(lenv):
131     syslibs = [
132         lenv['BF_PYTHON_LIB'],
133         lenv['BF_JPEG_LIB'],
134         lenv['BF_PNG_LIB'],
135         lenv['BF_ZLIB_LIB'],
136         lenv['BF_FREETYPE_LIB'],
137         lenv['BF_GETTEXT_LIB']
138         ]
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     syslibs += Split(lenv['BF_TIFF_LIB'])
144     if lenv['WITH_BF_OPENEXR']:
145         syslibs += Split(lenv['BF_OPENEXR_LIB'])
146     syslibs += Split(lenv['BF_SDL_LIB'])
147     syslibs += Split(lenv['BF_OPENGL_LIB'])
148     syslibs += Split(lenv['LLIBS'])
149
150     return syslibs
151
152 def propose_priorities():
153     print bc.OKBLUE+"Priorities:"+bc.ENDC
154     for t in possible_types:
155         print bc.OKGREEN+"\t"+t+bc.ENDC
156         new_priority = 0
157         sortlist = []
158         for k,v in libs[t].iteritems():
159             sortlist.append(k)
160         sortlist.sort()
161         curlib = libs[t]
162         for sk in sortlist:
163             v = curlib[sk]
164             #for p,v in sorted(libs[t].iteritems()):
165             print "\t\t",new_priority, v
166             new_priority += 5
167
168 ## TODO: see if this can be made in an emitter
169 def buildinfo(lenv, build_type):
170     """
171     Generate a buildinfo object
172     """
173     build_date = time.strftime ("%Y-%m-%d")
174     build_time = time.strftime ("%H:%M:%S")
175     obj = []
176     if lenv['BF_BUILDINFO']==1: #user_options_dict['USE_BUILDINFO'] == 1:
177         if sys.platform=='win32':
178             build_info_file = open("source/creator/winbuildinfo.h", 'w')
179             build_info_file.write("char *build_date=\"%s\";\n"%build_date)
180             build_info_file.write("char *build_time=\"%s\";\n"%build_time)
181             build_info_file.write("char *build_platform=\"win32\";\n")
182             build_info_file.write("char *build_type=\"dynamic\";\n")
183             build_info_file.close()
184             lenv.Append (CPPDEFINES = ['NAN_BUILDINFO', 'BUILD_DATE'])
185         else:
186             lenv.Append (CPPDEFINES = ['BUILD_TIME=\'"%s"\''%(build_time),
187                                         'BUILD_DATE=\'"%s"\''%(build_date),
188                                         'BUILD_TYPE=\'"dynamic"\'',
189                                         'NAN_BUILDINFO',
190                                         'BUILD_PLATFORM=\'"%s"\''%(sys.platform)])
191         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type,
192                         [root_build_dir+'source/creator/buildinfo.c'])]
193     return obj
194
195 ##### END LIB STUFF ############
196
197 ##### ACTION STUFF #############
198
199 def my_compile_print(target, source, env):
200     a = '%s' % (source[0])
201     d, f = os.path.split(a)
202     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
203
204 def my_moc_print(target, source, env):
205     a = '%s' % (source[0])
206     d, f = os.path.split(a)
207     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
208
209 def my_linking_print(target, source, env):
210     t = '%s' % (target[0])
211     d, f = os.path.split(t)
212     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
213
214 def my_program_print(target, source, env):
215     t = '%s' % (target[0])
216     d, f = os.path.split(t)
217     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
218
219 def msvc_hack(env):
220     static_lib = SCons.Tool.createStaticLibBuilder(env)
221     program = SCons.Tool.createProgBuilder(env)
222     
223     env['BUILDERS']['Library'] = static_lib
224     env['BUILDERS']['StaticLibrary'] = static_lib
225     env['BUILDERS']['Program'] = program
226         
227 def set_quiet_output(env):
228     mycaction = Action("$CCCOM", strfunction=my_compile_print)
229     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
230     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
231     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
232     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
233     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
234
235     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
236     static_ob.add_action('.c', mycaction)
237     static_ob.add_action('.cpp', mycppaction)
238     shared_ob.add_action('.c', myshcaction)
239     shared_ob.add_action('.cpp', myshcppaction)
240
241     static_lib = SCons.Builder.Builder(action = mylibaction,
242                                        emitter = '$LIBEMITTER',
243                                        prefix = '$LIBPREFIX',
244                                        suffix = '$LIBSUFFIX',
245                                        src_suffix = '$OBJSUFFIX',
246                                        src_builder = 'StaticObject')
247
248     program = SCons.Builder.Builder(action = mylinkaction,
249                                     emitter = '$PROGEMITTER',
250                                     prefix = '$PROGPREFIX',
251                                     suffix = '$PROGSUFFIX',
252                                     src_suffix = '$OBJSUFFIX',
253                                     src_builder = 'Object',
254                                     target_scanner = SCons.Defaults.ProgScan)
255
256     env['BUILDERS']['Object'] = static_ob
257     env['BUILDERS']['StaticObject'] = static_ob
258     env['BUILDERS']['StaticLibrary'] = static_lib
259     env['BUILDERS']['Library'] = static_lib
260     env['BUILDERS']['Program'] = program
261
262
263 #### END ACTION STUFF #########
264
265 class BlenderEnvironment(SConsEnvironment):
266
267     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None):
268         if not self or not libname or not sources:
269             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
270             Exit()
271         if libname in quickie or len(quickie)==0:
272             print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
273             lenv = self.Copy()
274             lenv.Append(CPPPATH=includes)
275             lenv.Append(CPPDEFINES=defines)
276             if lenv['WITH_BF_GAMEENGINE']:
277                     lenv.Append(CPPDEFINES=['GAMEBLENDER=1'])
278             if lenv['BF_DEBUG']:
279                     lenv.Append(CFLAGS = lenv['BF_DEBUG_FLAGS'], CCFLAGS = lenv['BF_DEBUG_FLAGS'])
280             else:
281                     lenv.Append(CFLAGS = lenv['REL_CFLAGS'], CCFLAGS = lenv['REL_CCFLAGS'])
282             if lenv['BF_PROFILE']:
283                     lenv.Append(CFLAGS = lenv['BF_PROFILE_FLAGS'], CCFLAGS = lenv['BF_PROFILE_FLAGS'])
284             if compileflags:
285                 lenv.Append(CFLAGS = compileflags)
286                 lenv.Append(CCFLAGS = compileflags)
287             lenv.Append(CFLAGS = Split(lenv['C_WARN']))
288             lenv.Append(CCFLAGS = Split(lenv['CC_WARN']))
289             lib = lenv.Library(target= '#'+root_build_dir+'lib/'+libname, source=sources)
290             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
291         else:
292             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
293         # note: libs is a global
294         add_lib_to_dict(libs, libtype, libname, priority)
295
296     def BlenderProg(self=None, builddir=None, progname=None, sources=None, includes=None, libs=None, libpath=None):
297         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
298         lenv = self.Copy()
299         if lenv['OURPLATFORM']=='win32-vc':
300             lenv.Append(LINKFLAGS = Split(lenv['PLATFORM_LINKFLAGS']))
301         if  lenv['OURPLATFORM']=='darwin':
302             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
303             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
304             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
305         lenv.Append(CPPPATH=includes)
306         lenv.Append(LIBPATH=libpath)
307         lenv.Append(LIBS=libs)
308         if lenv['WITH_BF_QUICKTIME']:
309              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
310              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
311         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
312         SConsEnvironment.Default(self, prog)
313         program_list.append(prog)
314
315 ## TODO: have register for libs/programs, so that we test only that
316 #  which have expressed their need to be tested in their own sconscript
317     def BlenderUnitTest(env, source, **kwargs):
318         test = env.Program(source, **kwargs)
319         env.AddPostAction(test, test[0].abspath)
320         env.Alias('check', test)
321         env.AlwaysBuild(test)
322         return test
323
324     def Glob(lenv, pattern):
325         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
326         files = []
327         for i in glob.glob(path + pattern):
328             files.append(string.replace(i, path, ''))
329         return files