/[ascend]/trunk/SConstruct
ViewVC logotype

Contents of /trunk/SConstruct

Parent Directory Parent Directory | Revision Log Revision Log


Revision 509 - (show annotations) (download)
Wed Apr 19 05:13:08 2006 UTC (14 years, 5 months ago) by johnpye
File size: 28732 byte(s)
SWIG < 1.3.28 can't do GCC visibility, so turn off for python interface in that case.
1 import os, commands, platform, distutils.sysconfig, os.path
2
3 version = "0.9.6rc0"
4
5 #------------------------------------------------------
6 # OPTIONS
7 #
8 # Note that if you set the options via the command line, they will be
9 # remembered in the file 'options.cache'. It's a feature ;-)
10
11 opts = Options(['options.cache', 'config.py'])
12 #print "PLATFORM = ",platform.system()
13
14 if platform.system()=="Windows":
15 default_tcl_lib = "tcl83"
16 default_tk_lib = "tk83"
17 default_tktable_lib = "Tktable28"
18 default_install_assets = "glade/"
19 icon_extension = '.png'
20 else:
21 default_tcl_lib = "tcl"
22 default_tk_lib = "tk"
23 default_tktable_lib = "Tktable2.8"
24 default_install_assets = "$INSTALL_DATA/ascend/glade/"
25 icon_extension = '.svg'
26
27
28 # Package linking option
29 opts.Add(EnumOption(
30 'PACKAGE_LINKING'
31 , 'Style of linking for external libraries'
32 , 'DYNAMIC_PACKAGES'
33 , ['DYNAMIC_PACKAGES', 'STATIC_PACKAGES', 'NO_PACKAGES']
34 ))
35
36 # You can turn off building of Tcl/Tk interface
37 opts.Add(BoolOption(
38 'WITHOUT_TCLTK'
39 ,"Set to True if you don't want to build the original Tcl/Tk GUI."
40 , False
41 ))
42
43 # You can turn off the building of the Python interface
44 opts.Add(BoolOption(
45 'WITHOUT_PYTHON'
46 ,"Set to True if you don't want to build Python wrappers."
47 , False
48 ))
49
50 # Which solvers will we allow?
51 opts.Add(ListOption(
52 'WITH_SOLVERS'
53 ,"List of the solvers you want to build. The default is the minimum that"
54 +" works."
55 ,["QRSLV","CMSLV"]
56 ,['QRSLV','MPS','SLV','OPTSQP'
57 ,'NGSLV','CMSLV','LRSLV','MINOS','CONOPT'
58 ,'LSOD','OPTSQP'
59 ]
60 ))
61
62 # Where will the local copy of the help files be kept?
63 opts.Add(PackageOption(
64 'WITH_LOCAL_HELP'
65 , "Directory containing the local copy of the help files (optional)"
66 , "no"
67 ))
68
69 # Will bintoken support be enabled?
70 opts.Add(BoolOption(
71 'WITH_BINTOKEN'
72 ,"Enable bintoken support? This means compiling models as C-code before"
73 +" running them, to increase solving speed for large models."
74 ,False
75 ))
76
77 # What should the default ASCENDLIBRARY path be?
78 # Note: users can change it by editing their ~/.ascend.ini
79 opts.Add(
80 'DEFAULT_ASCENDLIBRARY'
81 ,"Set the default value of the ASCENDLIBRARY -- the location where"
82 +" ASCEND will look for models when running ASCEND"
83 ,"$INSTALL_DATA/models"
84 )
85
86 # Where is SWIG?
87 opts.Add(
88 'SWIG'
89 ,"SWIG location, probably only required for MinGW and MSVC users."
90 +" Enter the location as a Windows-style path, for example"
91 +" 'c:\\msys\\1.0\\home\\john\\swigwin-1.3.29\\swig.exe'."
92 )
93
94 # Build the test suite?
95 opts.Add(BoolOption(
96 'WITH_CUNIT_TESTS'
97 ,"Whether to build the CUnit tests. Default is off. If set to on,"
98 +" you must have CUnit installed somewhere that SCons can"
99 +" find it, or else use the CUNIT_* options to specify."
100 ,False
101 ))
102
103 # Where are the CUnit includes?
104 opts.Add(PackageOption(
105 'CUNIT_CPPPATH'
106 ,"Where are your CUnit include files?"
107 ,'off'
108 ))
109
110 # Where are the CUnit libraries?
111 opts.Add(PackageOption(
112 'CUNIT_LIBPATH'
113 ,"Where are your CUnit libraries?"
114 ,'off'
115 ))
116
117 # Where are the Tcl includes?
118 opts.Add(PackageOption(
119 'TCL_CPPPATH'
120 ,"Where are your Tcl include files?"
121 ,"disable"
122 ))
123
124 # Where are the Tcl libs?
125 opts.Add(PackageOption(
126 'TCL_LIBPATH'
127 ,"Where are your Tcl libraries?"
128 ,'off'
129 ))
130
131 # What is the name of the Tcl lib?
132 opts.Add(
133 'TCL_LIB'
134 ,"Name of Tcl lib (eg 'tcl' or 'tcl83')"
135 ,default_tcl_lib
136 )
137
138 # Where are the Tk includes?
139 opts.Add(PackageOption(
140 'TK_CPPPATH'
141 ,"Where are your Tk include files?"
142 ,'$TCL_CPPPATH'
143 ))
144
145 # Where are the Tk libs?
146 opts.Add(PackageOption(
147 'TK_LIBPATH'
148 ,"Where are your Tk libraries?"
149 ,'$TCL_LIBPATH'
150 ))
151
152 # What is the name of the Tk lib?
153 opts.Add(
154 'TK_LIB'
155 ,"Name of Tk lib (eg 'tk' or 'tk83')"
156 ,default_tk_lib
157 )
158
159 # Static linking to TkTable
160
161 opts.Add(BoolOption(
162 'STATIC_TKTABLE'
163 ,'Use static linking to TkTable or not'
164 ,False
165 ))
166
167 opts.Add(
168 'TKTABLE_LIBPATH'
169 ,'Location of TkTable static library'
170 ,'$TCL_LIBPATH/Tktable2.8'
171 )
172
173 opts.Add(
174 'TKTABLE_LIB'
175 ,'Name of TkTable static library (excluding suffix/prefix, eg libtktable2.8.so -> tktable2.8)'
176 ,default_tktable_lib
177 )
178
179 opts.Add(
180 'INSTALL_PREFIX'
181 ,'Root location for installed files'
182 ,'/usr/local'
183 )
184
185 opts.Add(
186 'INSTALL_BIN'
187 ,'Location to put binaries during installation'
188 ,"$INSTALL_PREFIX/bin"
189 )
190
191 opts.Add(
192 'INSTALL_LIB'
193 ,'Location to put binaries during installation'
194 ,"$INSTALL_PREFIX/lib"
195 )
196
197 opts.Add(
198 'INSTALL_DATA'
199 ,'Location to put data files during installation'
200 ,"$INSTALL_PREFIX/share"
201 )
202
203 opts.Add(
204 'INSTALL_INCLUDE'
205 ,'Location to put header files during installation'
206 ,"$INSTALL_PREFIX/include"
207 )
208
209 opts.Add(
210 'PYGTK_ASSETS'
211 ,'Default location for Glade assets (placed in pygtk/interface/config.py)'
212 ,default_install_assets
213 )
214
215 opts.Add(BoolOption(
216 'DEBUG'
217 ,"Compile source with debugger symbols, eg for use with 'gdb'"
218 ,False
219 ))
220
221 opts.Add(
222 'INSTALL_ROOT'
223 ,'For use by RPM only: location of %{buildroot} during rpmbuild'
224 ,""
225 )
226
227 if platform.system()=="Windows":
228 opts.Add(BoolOption(
229 'WITH_INSTALLER'
230 ,'Build the Windows Installer (setup program) using NSIS'
231 ,False
232 ))
233
234 # TODO: OTHER OPTIONS?
235 # TODO: flags for optimisation
236 # TODO: turning on/off bintoken functionality
237 # TODO: Where will the 'Makefile.bt' file be installed?
238
239 # Import the outside environment
240
241 if os.environ.has_key('OSTYPE') and os.environ['OSTYPE']=='msys':
242 env = Environment(ENV=os.environ, tools=['mingw','swig','lex','yacc'])
243 else:
244 env = Environment(ENV=os.environ)
245 Tool('lex')(env)
246 Tool('yacc')(env)
247 Tool('fortran')(env)
248 Tool('swig')(env)
249
250 if platform.system()=='Windows' and env.has_key('MSVS'):
251 print "INCLUDE =",env['ENV']['INCLUDE']
252 print "LIB =",env['ENV']['LIB']
253 print "PATH =",env['ENV']['PATH']
254 env.Append(CPPPATH=env['ENV']['INCLUDE'])
255 env.Append(LIBPATH=env['ENV']['LIB'])
256 #env.Append(CPPDEFINES=['_CRT_SECURE_NO_DEPRECATED','_CRT_SECURE_NO_DEPRECATE'])
257
258 opts.Update(env)
259 opts.Save('options.cache',env)
260
261 Help(opts.GenerateHelpText(env))
262
263 with_tcltk_gui = (env['WITHOUT_TCLTK']==False)
264 without_tcltk_reason = "disabled by options/config.py"
265
266 with_python = (env['WITHOUT_PYTHON']==False)
267 without_python_reason = "disabled by options/config.py"
268
269 with_cunit_tests = env['WITH_CUNIT_TESTS']
270 without_cunit_reason = "not requested"
271
272 #print "SOLVERS:",env['WITH_SOLVERS']
273 #print "WITH_BINTOKEN:",env['WITH_BINTOKEN']
274 #print "DEFAULT_ASCENDLIBRARY:",env['DEFAULT_ASCENDLIBRARY']
275
276 can_install = True
277 if platform.system()=='Windows':
278 can_install = False
279
280 env['CAN_INSTALL']=can_install
281
282
283 #------------------------------------------------------
284 # SPECIAL CONFIGURATION TESTS
285
286 need_fortran = False
287
288 #----------------
289 # SWIG
290
291 import os,re
292
293 def get_swig_version(env):
294 cmd = env['SWIG']+' -version'
295 (cin,coutcerr) = os.popen4(cmd)
296 output = coutcerr.read()
297
298 restr = "SWIG\\s+Version\\s+(?P<maj>[0-9]+)\\.(?P<min>[0-9]+)\\.(?P<pat>[0-9]+)\\s*$"
299 expr = re.compile(restr,re.M);
300 m = expr.search(output);
301 if not m:
302 return None
303 maj = int(m.group('maj'))
304 min = int(m.group('min'))
305 pat = int(m.group('pat'))
306
307 return (maj,min,pat)
308
309
310 def CheckSwigVersion(context):
311
312 try:
313 context.Message("Checking version of SWIG... ")
314 maj,min,pat = get_swig_version(context.env)
315 except:
316 context.Result("Failed to detect version, or failed to run SWIG")
317 return 0;
318
319 context.env['SWIGVERSION']=tuple([maj,min,pat])
320
321 if maj == 1 and (
322 min > 3
323 or (min == 3 and pat >= 24)
324 ):
325 context.Result("ok, %d.%d.%d" % (maj,min,pat))
326 return 1;
327 else:
328 context.Result("too old, %d.%d.%d" % (maj,min,pat))
329 return 0;
330
331 #----------------
332 # General purpose library-and-header test
333
334 class KeepContext:
335 def __init__(self,context,varprefix):
336 self.keep = {}
337 for k in ['LIBS','LIBPATH','CPPPATH']:
338 if context.env.has_key(k):
339 self.keep[k] = context.env[k]
340 else:
341 self.keep[k] = None
342
343 libpath_add = []
344 if context.env.has_key(varprefix+'_LIBPATH'):
345 libpath_add = [env[varprefix+'_LIBPATH']]
346 #print "Adding '"+str(libpath_add)+"' to lib path"
347
348 cpppath_add = []
349 if context.env.has_key(varprefix+'_CPPPATH'):
350 cpppath_add = [env[varprefix+'_CPPPATH']]
351 #print "Adding '"+str(cpppath_add)+"' to cpp path"
352
353 libs_add = []
354 if context.env.has_key(varprefix+'_LIB'):
355 libs_add = [env[varprefix+'_LIB']]
356 #print "Adding '"+str(libs_add)+"' to libs"
357
358 context.env.Append(
359 LIBPATH = libpath_add
360 , CPPPATH = cpppath_add
361 , LIBS = libs_add
362 )
363
364 def restore(self,context):
365 #print "RESTORING CONTEXT"
366 #print self.keep
367 #print "..."
368 for k in self.keep:
369 if self.keep[k]==None:
370 #print "Clearing "+str(k)
371 del context.env[k];
372 else:
373 #print "Restoring "+str(k)+" to '"+self.keep[k]+"'"
374 context.env[k]=self.keep[k];
375
376 def CheckExtLib(context,libname,text,ext='.c',varprefix=None):
377 """This method will check for variables LIBNAME_LIBPATH
378 and LIBNAME_CPPPATH and try to compile and link the
379 file with the provided text, linking with the
380 library libname."""
381
382 context.Message( 'Checking for '+libname+'... ' )
383
384 if varprefix==None:
385 varprefix = libname.upper()
386
387 keep = KeepContext(context,varprefix)
388
389 if not context.env.has_key(varprefix+'_LIB'):
390 # if varprefix_LIB were in env, KeepContext would
391 # have appended it already
392 context.env.Append(LIBS=libname)
393
394 is_ok = context.TryLink(text,ext)
395
396 # print "Link success? ",(is_ok != 0)
397
398 keep.restore(context)
399
400 # print "Restored CPPPATH="+str(context.env['CPPPATH'])
401 # print "Restored LIBS="+libname
402 # print "Restored LIBPATH="+str(context.env['LIBPATH'])
403
404 context.Result(is_ok)
405 return is_ok
406
407 #----------------
408 # GCC
409
410 gcc_test_text = """
411 #ifndef __GNUC__
412 # error "Not using GCC"
413 #endif
414
415 int main(void){
416 return __GNUC__;
417 }
418 """
419
420 def CheckGcc(context):
421 context.Message("Checking for GCC... ")
422 is_ok = context.TryCompile(gcc_test_text,".c")
423 context.Result(is_ok)
424 return is_ok
425
426 #----------------
427 # GCC VISIBILITY feature
428
429 gccvisibility_test_text = """
430 #if __GNUC__ < 4
431 # error "Require GCC version 4 or newer"
432 #endif
433
434 __attribute__ ((visibility("default"))) int x;
435
436 int main(void){
437 extern int x;
438 x = 4;
439 }
440 """
441
442 def CheckGccVisibility(context):
443 context.Message("Checking for GCC 'visibility' capability... ")
444 is_ok = context.TryCompile(gccvisibility_test_text,".c")
445 context.Result(is_ok)
446 return is_ok
447
448 #----------------
449 # YACC
450
451 yacc_test_text = """
452 %start ROOT
453 %token MSG
454 %%
455
456 ROOT:
457 MSG { print("HELLO"); }
458 ;
459 """
460
461 def CheckYacc(context):
462 context.Message("Checking for Yacc... ")
463 is_ok = context.TryCompile(yacc_test_text,".y")
464 context.Result(is_ok)
465 return is_ok
466
467 #----------------
468 # CUnit test
469
470 cunit_test_text = """
471 #include <CUnit/CUnit.h>
472 int maxi(int i1, int i2){
473 return (i1 > i2) ? i1 : i2;
474 }
475
476 void test_maxi(void){
477 CU_ASSERT(maxi(0,2) == 2);
478 CU_ASSERT(maxi(0,-2) == 0);
479 CU_ASSERT(maxi(2,2) == 2);
480
481 }
482 int main(void){
483 /* CU_initialize_registry() */
484 return 0;
485 }
486 """
487
488 def CheckCUnit(context):
489 return CheckExtLib(context,'cunit',cunit_test_text)
490
491 #----------------
492 # Tcl test
493
494 tcl_check_text = r"""
495 #include <tcl.h>
496 #include <stdio.h>
497 int main(void){
498 printf("%s",TCL_PATCH_LEVEL);
499 return 0;
500 }
501 """
502
503 def CheckTcl(context):
504 return CheckExtLib(context,'tcl',tcl_check_text)
505
506 def CheckTclVersion(context):
507 keep = KeepContext(context,'TCL')
508 context.Message("Checking Tcl version... ")
509 (is_ok,output) = context.TryRun(tcl_check_text,'.c')
510 keep.restore(context)
511 if not is_ok:
512 context.Result("failed to run check")
513 return 0
514
515 major,minor,patch = tuple(int(i) for i in output.split("."))
516 if major != 8 or minor > 3:
517 context.Result(output+" (bad version)")
518 # bad version
519 return 0
520
521 # good version
522 context.Result(output+" (good)")
523 return 1
524
525 #----------------
526 # Tk test
527
528 tk_check_text = r"""
529 #include <tk.h>
530 #include <stdio.h>
531 int main(void){
532 printf("%s",TK_PATCH_LEVEL);
533 return 0;
534 }
535 """
536 def CheckTk(context):
537 return CheckExtLib(context,'tk',tcl_check_text)
538
539
540 def CheckTkVersion(context):
541 keep = KeepContext(context,'TK')
542 context.Message("Checking Tk version... ")
543 (is_ok,output) = context.TryRun(tk_check_text,'.c')
544 keep.restore(context)
545 if not is_ok:
546 context.Result("failed to run check")
547 return 0
548 context.Result(output)
549
550 major,minor,patch = tuple(int(i) for i in output.split("."))
551 if major != 8 or minor > 3:
552 # bad version
553 return 0
554
555 # good version
556 return 1
557
558 #----------------
559 # GCC Version sniffing
560
561 # TODO FIXME
562
563 gcc_version4 = False
564
565 #------------------------------------------------------
566 # CONFIGURATION
567
568 conf = Configure(env
569 , custom_tests = {
570 'CheckSwigVersion' : CheckSwigVersion
571 , 'CheckCUnit' : CheckCUnit
572 , 'CheckTcl' : CheckTcl
573 , 'CheckTclVersion' : CheckTclVersion
574 , 'CheckTk' : CheckTk
575 , 'CheckTkVersion' : CheckTkVersion
576 , 'CheckGcc' : CheckGcc
577 , 'CheckGccVisibility' : CheckGccVisibility
578 , 'CheckYacc' : CheckYacc
579 # , 'CheckIsNan' : CheckIsNan
580 # , 'CheckCppUnitConfig' : CheckCppUnitConfig
581 }
582 # , config_h = "config.h"
583 )
584
585
586 # Math library
587
588 #if not conf.CheckFunc('sinh') and not conf.CheckLibWithHeader(['m','c','libc'], 'math.h', 'C'):
589 # print 'Did not find math library, exiting!'
590 # Exit(1)
591
592 # Where is 'isnan'?
593
594 if not conf.CheckFunc('isnan'):
595 print "Didn't find isnan"
596 # Exit(1)
597
598 # GCC visibility
599
600 if conf.CheckGcc():
601 conf.env['HAVE_GCC']=True;
602 if conf.CheckGccVisibility():
603 conf.env['HAVE_GCCVISIBILITY']=True;
604 conf.env.Append(CCFLAGS=['-fvisibility=hidden'])
605 conf.env.Append(CPPDEFINES=['HAVE_GCCVISIBILITY'])
606
607 # YACC
608
609 if not conf.CheckYacc():
610 print "YACC NOT FOUND OR NOT WORKING"
611 else:
612 conf.env['HAVE_YACC']=True
613
614 conf.env['HAVE_LEX']=True
615
616 # Tcl/Tk
617
618 if conf.CheckTcl():
619 if with_tcltk_gui and conf.CheckTclVersion():
620 if conf.CheckTk():
621 if with_tcltk_gui and not conf.CheckTkVersion():
622 without_tcltk_reason = "Require Tk version <= 8.3. See 'scons -h'"
623 with_tcltk_gui = False
624 else:
625 without_tcltk_reason = "Tk not found."
626 with_tcltk_gui = False
627 else:
628 without_tcltk_reason = "Require Tcl <= 8.3 Tcl."
629 with_tcltk_gui = False
630
631 else:
632 without_tcltk_reason = "Tcl not found."
633 with_tcltk_gui = False
634
635 # Python... obviously we're already running python, so we just need to
636 # check that we can link to the python library OK:
637
638 if platform.system()=="Windows":
639 python_lib='python24'
640 else:
641 python_lib='python2.4'
642
643 # SWIG version
644
645 if not conf.CheckSwigVersion():
646 without_python_reason = 'SWIG >= 1.3.24 is required'
647 with_python = False
648
649 # CUnit
650
651 if with_cunit_tests:
652 if not conf.CheckCUnit():
653 without_cunit_reason = 'CUnit not found'
654
655 # BLAS
656
657 need_blas=False
658 if with_tcltk_gui:
659 need_blas=True
660 if need_blas:
661 if conf.CheckLib('blas'):
662 print "FOUND BLAS"
663 with_local_blas = False
664 without_local_blas_reason = "Found BLAS installed on system"
665 else:
666 print "DIDN'T FIND BLAS"
667 with_local_blas = True
668 need_fortran = True
669
670 # FORTRAN
671
672 if need_fortran:
673 conf.env.Tool('f77')
674 detect_fortran = conf.env.Detect(['g77','f77'])
675 if detect_fortran:
676 # For some reason, g77 doesn't get detected properly on MinGW
677 if not env.has_key('F77'):
678 conf.env.Replace(F77=detect_fortran)
679 conf.env.Replace(F77COM='$F77 $F77FLAGS -c -o $TARGET $SOURCE')
680 conf.env.Replace(F77FLAGS='')
681 #print "F77:",conf.env['F77']
682 #print "F77COM:",conf.env['F77COM']
683 #print "F77FLAGS:",conf.env['F77FLAGS']
684 fortran_builder = Builder(
685 action='$F77COM'
686 , suffix='.o'
687 , src_suffix='.f'
688 )
689 conf.env.Append(BUILDERS={'Fortran':fortran_builder})
690 else:
691 print "FORTRAN-77 required but not found"
692 Exit(1)
693 #else:
694 # print "FORTRAN not required"
695
696 # TODO: -D_HPUX_SOURCE is needed
697
698 # TODO: check size of void*
699
700 # TODO: detect if dynamic libraries are possible or not
701
702 if platform.system()=="Windows" and env.has_key('MSVS'):
703 if not conf.CheckHeader('windows.h') and env['PACKAGE_LINKING']=='DYNAMIC_PACKAGES':
704 print "Reverting to STATIC_PACKAGES since windows.h is not available. Probably you "\
705 +"need to install the Microsoft Windows Server 2003 Platform SDK, or similar."
706 env['PACKAGE_LINKING']='STATIC_PACKAGES'
707
708 if with_python and not conf.CheckHeader('basetsd.h'):
709 with_python = 0;
710 without_python_reason = "Header file 'basetsd.h' not found. Install the MS Platform SDK."
711
712 conf.env.Append(CPPDEFINES=env['PACKAGE_LINKING'])
713
714 conf.Finish()
715
716 env.Append(PYTHON_LIBPATH=[distutils.sysconfig.PREFIX+"/libs"])
717 env.Append(PYTHON_LIB=[python_lib])
718 env.Append(PYTHON_CPPPATH=[distutils.sysconfig.get_python_inc()])
719
720 #---------------------------------------
721 # SUBSTITUTION DICTIONARY for .in files
722
723 subst_dict = {
724 '@DEFAULT_ASCENDLIBRARY@':env['DEFAULT_ASCENDLIBRARY']
725 , '@GLADE_FILE@':'ascend.glade'
726 , '@HELP_ROOT@':''
727 , '@ICON_EXTENSION@':icon_extension
728 , '@INSTALL_DATA@':env['INSTALL_DATA']
729 , '@INSTALL_BIN@':env['INSTALL_BIN']
730 , '@INSTALL_INCLUDE@':env['INSTALL_INCLUDE']
731 , '@PYGTK_ASSETS@':env['PYGTK_ASSETS']
732 , '@VERSION@':version
733 , '@WEBHELPROOT@':'http://pye.dyndns.org/ascend/manual/'
734 , '@ASC_SHLIBSUFFIX@':env['SHLIBSUFFIX']
735 , '@ASC_SHLIBPREFIX@':env['SHLIBPREFIX']
736 }
737
738 if env['WITH_LOCAL_HELP']:
739 print "WITH_LOCAL_HELP:",env['WITH_LOCAL_HELP']
740 subst_dict['@HELP_ROOT@']=env['WITH_LOCAL_HELP']
741
742 if with_python:
743 subst_dict['@ASCXX_USE_PYTHON@']="1"
744
745 if env.has_key('HAVE_GCCVISIBILITY'):
746 subst_dict['@HAVE_GCCVISIBILITY@'] = "1"
747
748 env.Append(SUBST_DICT=subst_dict)
749
750 #------------------------------------------------------
751 # RECIPE: 'SubstInFile', used in pygtk SConscript
752
753 import re
754 from SCons.Script import * # the usual scons stuff you get in a SConscript
755
756 def TOOL_SUBST(env):
757 """Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT
758 from the source to the target.
759 The values of SUBST_DICT first have any construction variables expanded
760 (its keys are not expanded).
761 If a value of SUBST_DICT is a python callable function, it is called and
762 the result is expanded as the value.
763 If there's more than one source and more than one target, each target gets
764 substituted from the corresponding source.
765 """
766 env.Append(TOOLS = 'SUBST')
767 def do_subst_in_file(targetfile, sourcefile, dict):
768 """Replace all instances of the keys of dict with their values.
769 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
770 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
771 """
772 try:
773 f = open(sourcefile, 'rb')
774 contents = f.read()
775 f.close()
776 except:
777 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
778 for (k,v) in dict.items():
779 contents = re.sub(k, v, contents)
780 try:
781 f = open(targetfile, 'wb')
782 f.write(contents)
783 f.close()
784 except:
785 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
786 return 0 # success
787
788 def subst_in_file(target, source, env):
789 if not env.has_key('SUBST_DICT'):
790 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
791 d = dict(env['SUBST_DICT']) # copy it
792 for (k,v) in d.items():
793 if callable(v):
794 d[k] = env.subst(v())
795 elif SCons.Util.is_String(v):
796 d[k]=env.subst(v)
797 else:
798 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
799 for (t,s) in zip(target, source):
800 return do_subst_in_file(str(t), str(s), d)
801
802 def subst_in_file_string(target, source, env):
803 """This is what gets printed on the console."""
804 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
805 for (t,s) in zip(target, source)])
806
807 def subst_emitter(target, source, env):
808 """Add dependency from substituted SUBST_DICT to target.
809 Returns original target, source tuple unchanged.
810 """
811 d = env['SUBST_DICT'].copy() # copy it
812 for (k,v) in d.items():
813 if callable(v):
814 d[k] = env.subst(v())
815 elif SCons.Util.is_String(v):
816 d[k]=env.subst(v)
817 Depends(target, SCons.Node.Python.Value(d))
818 return target, source
819
820 subst_action=SCons.Action.Action(subst_in_file, subst_in_file_string)
821 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
822
823 TOOL_SUBST(env)
824
825 #------------------------------------------------------
826 # Recipe for 'CHMOD' ACTION
827
828 import SCons
829 from SCons.Script.SConscript import SConsEnvironment
830 SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
831 lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
832
833 def InstallPerm(env, dest, files, perm):
834 obj = env.Install(dest, files)
835 for i in obj:
836 env.AddPostAction(i, env.Chmod(str(i), perm))
837
838 SConsEnvironment.InstallPerm = InstallPerm
839
840 # define wrappers
841 SConsEnvironment.InstallProgram = lambda env, dest, files: InstallPerm(env, dest, files, 0755)
842 SConsEnvironment.InstallHeader = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
843
844 #------------------------------------------------------
845 # NSIS Support for SCons
846
847 # Adapted version by John Pye, April 2006.
848 # from http://www.scons.org/cgi-sys/cgiwrap/scons/moin.cgi/NsisSconsTool
849 # Written by Mike Elkins, January 2004.
850
851 #This tool provides SCons support for the Nullsoft Scriptable Install System
852 #a windows installer builder available at http://nsis.sourceforge.net/home
853
854 #In addition, if you set NSISDEFINES to a dictionary, those variables will be passed
855 #to NSIS.
856
857 import SCons.Builder
858 import SCons.Util
859 import SCons.Scanner
860 import SCons.Sig
861 import os.path
862 import glob
863
864 def nsis_parse( sources, keyword, multiple ):
865 """
866 A function that knows how to read a .nsi file and figure
867 out what files are referenced, or find the 'OutFile' line.
868
869
870 sources is a list of nsi files.
871 keyword is the command ('File' or 'OutFile') to look for
872 multiple is true if you want all the args as a list, false if you
873 just want the first one.
874 """
875 stuff = []
876 for s in sources:
877 c = s.get_contents()
878 for l in c.split('\n'):
879 semi = l.find(';')
880 if (semi != -1):
881 l = l[:semi]
882 hash = l.find('#')
883 if (hash != -1):
884 l = l[:hash]
885 # Look for the keyword
886 l = l.strip()
887 spl = l.split(None,1)
888 if len(spl) > 1:
889 if spl[0].capitalize() == keyword.capitalize():
890 arg = spl[1]
891 if arg.startswith('"') and arg.endswith('"'):
892 arg = arg[1:-1]
893 if multiple:
894 stuff += [ arg ]
895 else:
896 return arg
897 return stuff
898
899
900 def nsis_path( filename, nsisdefines, rootdir ):
901 """
902 Do environment replacement, and prepend with the SCons root dir if
903 necessary
904 """
905 # We can't do variables defined by NSIS itself (like $INSTDIR),
906 # only user supplied ones (like ${FOO})
907 varPos = filename.find('${')
908 while varPos != -1:
909 endpos = filename.find('}',varPos)
910 assert endpos != -1
911 if not nsisdefines.has_key(filename[varPos+2:endpos]):
912 raise KeyError ("Could not find %s in NSISDEFINES" % filename[varPos+2:endpos])
913 val = nsisdefines[filename[varPos+2:endpos]]
914 if type(val) == list:
915 if varPos != 0 or endpos+1 != len(filename):
916 raise Exception("Can't use lists on variables that aren't complete filenames")
917 return val
918 filename = filename[0:varPos] + val + filename[endpos+1:]
919 varPos = filename.find('${')
920 return filename
921
922
923 def nsis_scanner( node, env, path ):
924 """
925 The scanner that looks through the source .nsi files and finds all lines
926 that are the 'File' command, fixes the directories etc, and returns them.
927 """
928 nodes = node.rfile()
929 if not node.exists():
930 return []
931 nodes = []
932 source_dir = node.get_dir()
933 for include in nsis_parse([node],'file',1):
934 exp = nsis_path(include,env['NSISDEFINES'],source_dir)
935 if type(exp) != list:
936 exp = [exp]
937 for p in exp:
938 for filename in glob.glob( os.path.abspath(
939 os.path.join(str(source_dir),p))):
940 # Why absolute path? Cause it breaks mysteriously without it :(
941 nodes.append(filename)
942 return nodes
943
944
945 def nsis_emitter( source, target, env ):
946 """
947 The emitter changes the target name to match what the command actually will
948 output, which is the argument to the OutFile command.
949 """
950 nsp = nsis_parse(source,'outfile',0)
951 if not nsp:
952 return (target,source)
953 x = (
954 nsis_path(nsp,env['NSISDEFINES'],''),
955 source)
956 return x
957
958 def quoteIfSpaced(text):
959 if ' ' in text:
960 return '"'+text+'"'
961 else:
962 return text
963
964 def toString(item,env):
965 if type(item) == list:
966 ret = ''
967 for i in item:
968 if ret:
969 ret += ' '
970 val = toString(i,env)
971 if ' ' in val:
972 val = "'"+val+"'"
973 ret += val
974 return ret
975 else:
976 # For convienence, handle #s here
977 if str(item).startswith('#'):
978 item = env.File(item).get_abspath()
979 return str(item)
980
981 def runNSIS(source,target,env,for_signature):
982 ret = env['NSIS']+" "
983 if env.has_key('NSISFLAGS'):
984 for flag in env['NSISFLAGS']:
985 ret += flag
986 ret += ' '
987 if env.has_key('NSISDEFINES'):
988 for d in env['NSISDEFINES']:
989 ret += '/D'+d
990 if env['NSISDEFINES'][d]:
991 ret +='='+quoteIfSpaced(toString(env['NSISDEFINES'][d],env))
992 ret += ' '
993 for s in source:
994 ret += quoteIfSpaced(str(s))
995 return ret
996
997 def find_nsis(env):
998 """
999 Try and figure out if NSIS is installed on this machine, and if so,
1000 where.
1001 """
1002 if SCons.Util.can_read_reg:
1003 # If we can read the registry, get the NSIS command from it
1004 try:
1005 k = SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
1006 'SOFTWARE\\NSIS')
1007 val, tok = SCons.Util.RegQueryValueEx(k,None)
1008 ret = val + os.path.sep + 'makensis.exe'
1009 if os.path.exists(ret):
1010 return '"' + ret + '"'
1011 else:
1012 return None
1013 except:
1014 pass # Couldn't find the key, just act like we can't read the registry
1015 # Hope it's on the path
1016 return env.WhereIs('makensis.exe')
1017
1018 def nsis_exists(env):
1019 """
1020 Is NSIS findable on this machine?
1021 """
1022 if find_nsis(env) != None:
1023 return 1
1024 return 0
1025
1026 env['BUILDERS']['Nsis'] = SCons.Builder.Builder(generator=runNSIS,
1027 src_suffix='.nsi',
1028 emitter=nsis_emitter)
1029
1030 env.Append(SCANNERS = SCons.Scanner.Scanner( function = nsis_scanner,
1031 skeys = ['.nsi']))
1032
1033 if not env.has_key('NSISDEFINES'):
1034 env['NSISDEFINES'] = {}
1035 env['NSIS'] = find_nsis(env)
1036
1037 #------------------------------------------------------
1038 # BUILD...
1039
1040 # so that #include <modulename/headername.h> works across all modules...
1041 env.Append(CPPPATH=['#base/generic'])
1042
1043 if gcc_version4:
1044 env.Append(CCFLAGS=['-fvisibility=hidden'])
1045
1046 if env['DEBUG']:
1047 env.Append(CCFLAGS=['-g'])
1048
1049 #-------------
1050 # TCL/TK GUI
1051
1052 if with_tcltk_gui:
1053 if with_local_blas:
1054 env.SConscript(['blas/SConscript'],'env')
1055 else:
1056 print "Skipping... BLAS won't be build:", without_local_blas_reason
1057
1058 env.SConscript(['lsod/SConscript'],'env')
1059
1060 env.SConscript(['linpack/SConscript'],'env')
1061 env.SConscript(['tcltk98/generic/interface/SConscript'],'env')
1062 else:
1063 print "Skipping... Tcl/Tk GUI isn't being built:",without_tcltk_reason
1064
1065 #-------------
1066 # PYTHON INTERFACE
1067
1068 if with_python:
1069 env.SConscript(['pygtk/interface/SConscript'],'env')
1070 else:
1071 print "Skipping... Python GUI isn't being built:",without_python_reason
1072
1073 #------------
1074 # BASE/GENERIC SUBDIRECTORIES
1075
1076 dirs = ['general','utilities','compiler','solver','packages']
1077
1078 srcs = []
1079 for d in dirs:
1080 heresrcs = env.SConscript('base/generic/'+d+'/SConscript','env')
1081 srcs += heresrcs
1082
1083 #-------------
1084 # LIBASCEND -- all base/generic functionality
1085
1086 libascend = env.SharedLibrary('ascend',srcs)
1087
1088 #-------------
1089 # UNIT TESTS
1090
1091 if with_cunit_tests:
1092 testdirs = ['general','solver','utilities']
1093 for testdir in testdirs:
1094 path = 'base/generic/'+testdir+'/test/'
1095 env.SConscript([path+'SConscript'],'env')
1096 env.SConscript(['test/SConscript'],'env')
1097 env.SConscript(['base/generic/test/SConscript'],'env')
1098
1099
1100 else:
1101 print "Skipping... CUnit tests aren't being built:",without_cunit_reason
1102
1103
1104 #------------------------------------------------------
1105 # INSTALLATION
1106
1107 if env.has_key('CAN_INSTALL') and env['CAN_INSTALL']:
1108 # the models directory only needs to be processed for installation, no other processing required.
1109 env.SConscript(['models/SConscript'],'env')
1110
1111 dirs = ['INSTALL_BIN','INSTALL_DATA','INSTALL_LIB']
1112 install_dirs = [env['INSTALL_ROOT']+env[d] for d in dirs]
1113
1114 # TODO: add install options
1115 env.Alias('install',install_dirs)
1116
1117 env.Install(env['INSTALL_ROOT']+env['INSTALL_LIB'],libascend)
1118
1119 #------------------------------------------------------
1120 # CREATE the SPEC file for generation of RPM packages
1121
1122 if platform.system()=="Linux":
1123 env.SubstInFile('ascend.spec.in')

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22