/[ascend]/trunk/SConstruct
ViewVC logotype

Contents of /trunk/SConstruct

Parent Directory Parent Directory | Revision Log Revision Log


Revision 709 - (show annotations) (download)
Wed Jun 28 16:28:57 2006 UTC (13 years, 5 months ago) by johnpye
File size: 34708 byte(s)
Monster commit!
Lots of recommenting and reorganising of external relations-related stuff.
Replaced a lot of ascmalloc and asccalloc calls with the new ASC_NEW* macros.
Fixed (?) the problem Art is having with icons in PyGTK.
Turned on -Wall in SConstruct and fixed up a stack of warnings.
Removed the redundant exit(2) from after Asc_Panic calls and added __attribute__((noreturn)).
Set doxygen to create callgraphs to level 2, updated doxyfile to version 1.4.7.
Fixed up building of extfntest.c.
1 import os, commands, platform, distutils.sysconfig, os.path
2
3 version = "0.9.5.94"
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 = "tcl84"
16 default_tk_lib = "tk84"
17 default_tktable_lib = "Tktable28"
18 default_install_assets = "glade/"
19 icon_extension = '.png'
20 default_tcl = "c:\\Tcl"
21 if os.environ.get('MSYSTEM')=="MINGW32":
22 default_tcl_libpath="$TCL\\bin"
23 else:
24 default_tcl_libpath="$TCL\\lib"
25 default_rel_distdir = '.'
26 default_absolute_paths = False
27 default_ida_prefix = "c:/mingw"
28 need_libm = False
29 python_exe = "c:\\Python24\\python.exe"
30 else:
31 default_tcl_lib = "tcl8.4"
32 default_tk_lib = "tk8.4"
33 default_tktable_lib = "Tktable2.8"
34 default_install_assets = "$INSTALL_ASCDATA/glade/"
35 icon_extension = '.svg'
36 default_tcl = '/usr'
37 default_tcl_libpath = "$TCL/lib"
38 default_rel_distdir = '../share/ascend'
39 default_absolute_paths = True
40 default_ida_prefix="/usr/local"
41 need_libm = True
42 if not os.path.isdir(default_tcl):
43 default_tcl = '/usr'
44 python_exe = distutils.sysconfig.EXEC_PREFIX+"/bin/python"
45
46 opts.Add(
47 'CC'
48 ,'C Compiler command'
49 ,None
50 )
51
52 opts.Add(
53 'CXX'
54 ,'C++ Compiler command'
55 ,None
56 )
57
58 opts.Add(BoolOption(
59 'GCOV'
60 , 'Whether to enable coverage testing in object code'
61 , False
62 ))
63
64 # Package linking option
65 opts.Add(EnumOption(
66 'PACKAGE_LINKING'
67 , 'Style of linking for external libraries'
68 , 'DYNAMIC_PACKAGES'
69 , ['DYNAMIC_PACKAGES', 'STATIC_PACKAGES', 'NO_PACKAGES']
70 ))
71
72 opts.Add(BoolOption(
73 'WITH_GCCVISIBILITY'
74 ,"Whether to use GCC Visibility features (only applicable if available)"
75 ,True
76 ))
77
78 # You can turn off building of Tcl/Tk interface
79 opts.Add(BoolOption(
80 'WITH_TCLTK'
81 ,"Set to False if you don't want to build the original Tcl/Tk GUI."
82 , True
83 ))
84
85 # You can turn off the building of the Python interface
86 opts.Add(BoolOption(
87 'WITH_PYTHON'
88 ,"Set to False if you don't want to build Python wrappers."
89 , True
90 ))
91
92 # Which solvers will we allow?
93 opts.Add(ListOption(
94 'WITH_SOLVERS'
95 ,"List of the solvers you want to build. The default is the minimum that"
96 +" works."
97 ,["QRSLV","CMSLV","LSOD","IDA"]
98 ,['QRSLV','MPS','SLV','OPTSQP'
99 ,'NGSLV','CMSLV','LRSLV','MINOS','CONOPT'
100 ,'LSOD','OPTSQP',"IDA"
101 ]
102 ))
103
104 # Where will the local copy of the help files be kept?
105 opts.Add(PackageOption(
106 'WITH_LOCAL_HELP'
107 , "Directory containing the local copy of the help files (optional)"
108 , "no"
109 ))
110
111 # Will bintoken support be enabled?
112 opts.Add(BoolOption(
113 'WITH_BINTOKEN'
114 ,"Enable bintoken support? This means compiling models as C-code before"
115 +" running them, to increase solving speed for large models."
116 ,False
117 ))
118
119 # What should the default ASCENDLIBRARY path be?
120 # Note: users can change it by editing their ~/.ascend.ini
121 opts.Add(
122 'DEFAULT_ASCENDLIBRARY'
123 ,"Set the default value of the ASCENDLIBRARY -- the location where"
124 +" ASCEND will look for models when running ASCEND"
125 ,"$INSTALL_ASCDATA/models"
126 )
127
128 # Where is SWIG?
129 opts.Add(
130 'SWIG'
131 ,"SWIG location, probably only required for MinGW and MSVC users."
132 +" Enter the location as a Windows-style path, for example"
133 +" 'c:\\msys\\1.0\\home\\john\\swigwin-1.3.29\\swig.exe'."
134 )
135
136 # Build the test suite?
137 opts.Add(BoolOption(
138 'WITH_CUNIT'
139 ,"You can disable CUnit tests with this option. This will basically stop"
140 +" SCons from parsing the SConscript files relating to the 'test'"
141 +" target, which just might make things marginally faster. Probably"
142 +" you can just ignore this option though. SCons will sniff for Cunit"
143 +" but build the tests only if you specify the 'test' target."
144 ,True
145 ))
146
147 # Where are the CUnit includes?
148 opts.Add(PackageOption(
149 'CUNIT_CPPPATH'
150 ,"Where are your CUnit include files?"
151 ,'off'
152 ))
153
154 # Where are the CUnit libraries?
155 opts.Add(PackageOption(
156 'CUNIT_LIBPATH'
157 ,"Where are your CUnit libraries?"
158 ,'off'
159 ))
160
161 opts.Add(PackageOption(
162 "IDA_PREFIX"
163 ,"Prefix for your IDA install (IDA ./configure --prefix)"
164 ,default_ida_prefix
165 ))
166
167 opts.Add(
168 'IDA_CPPPATH'
169 ,"Where is your ida.h?"
170 ,"$IDA_PREFIX/include"
171 )
172
173 opts.Add(
174 'IDA_LIBPATH'
175 ,"Where are your SUNDIALS libraries installed?"
176 ,"$IDA_PREFIX/lib"
177 )
178
179 opts.Add(
180 "IDA_LIB"
181 ,"What libraries to link to for use of IDA (comma-separated). Note that"
182 +" you will need to include the math library in this list (for now)."
183 ,'sundials_ida,sundials_nvecserial,m'
184 )
185
186 opts.Add(
187 "F2C_LIB"
188 ,"F2C library (eg. g2c, gfortran, f2c)"
189 ,"g2c"
190 )
191
192 opts.Add(PackageOption(
193 "F2C_LIBPATH"
194 ,"Directory containing F2C library (i.e. g2c, gfortran, f2c, etc.), if not already accessible"
195 ,"off"
196 ))
197
198 opts.Add(
199 'TCL'
200 ,'Base of Tcl distribution'
201 ,default_tcl
202 )
203
204 # Where are the Tcl includes?
205 opts.Add(
206 'TCL_CPPPATH'
207 ,"Where are your Tcl include files?"
208 ,"$TCL/include"
209 )
210
211 # Where are the Tcl libs?
212 opts.Add(
213 'TCL_LIBPATH'
214 ,"Where are your Tcl libraries?"
215 ,default_tcl_libpath
216 )
217
218 # What is the name of the Tcl lib?
219 opts.Add(
220 'TCL_LIB'
221 ,"Name of Tcl lib (eg 'tcl' or 'tcl83'), for full path to static library (if STATIC_TCLTK is set)"
222 ,default_tcl_lib
223 )
224
225 # Where are the Tk includes?
226 opts.Add(
227 'TK_CPPPATH'
228 ,"Where are your Tk include files?"
229 ,'$TCL_CPPPATH'
230 )
231
232 # Where are the Tk libs?
233 opts.Add(
234 'TK_LIBPATH'
235 ,"Where are your Tk libraries?"
236 ,'$TCL_LIBPATH'
237 )
238
239 # What is the name of the Tk lib?
240 opts.Add(
241 'TK_LIB'
242 ,"Name of Tk lib (eg 'tk' or 'tk83'), or full path to static library"
243 ,default_tk_lib
244 )
245
246 # Static linking to TkTable
247
248 opts.Add(BoolOption(
249 'STATIC_TCLTK'
250 ,'Set true for static linking for Tcl/Tk and TkTable. EXPERIMENTAL'
251 ,False
252 ))
253
254 opts.Add(
255 'TKTABLE_LIBPATH'
256 ,'Location of TkTable static library'
257 ,'$TCL_LIBPATH/Tktable2.8'
258 )
259
260 opts.Add(
261 'TKTABLE_LIB'
262 ,'Stem name of TkTable (eg tktable2.8, no ".so" or "lib") shared library, or full path of static tktable (/usr/lib/...)'
263 ,default_tktable_lib
264 )
265
266 opts.Add(
267 'TKTABLE_CPPPATH'
268 ,'Location of TkTable header file'
269 ,'$TCL_CPPPATH'
270 )
271
272 opts.Add(
273 'X11'
274 ,'Base X11 directory. Only used when STATIC_TCLTK is turned on. EXPERIMENTAL'
275 ,'/usr/X11R6'
276 )
277
278 opts.Add(
279 'X11_LIBPATH'
280 ,'Location of X11 lib. EXPERIMENTAL'
281 ,'$X11/lib'
282 )
283
284 opts.Add(
285 'X11_CPPPATH'
286 ,'Location of X11 includes. EXPERIMENTAL'
287 ,'$X11/include'
288 )
289
290 opts.Add(
291 'X11_LIB'
292 ,'Name of X11 lib. EXPERIMENTAL'
293 ,'X11'
294 )
295
296 opts.Add(
297 'INSTALL_PREFIX'
298 ,'Root location for installed files'
299 ,'/usr/local'
300 )
301
302 opts.Add(
303 'INSTALL_BIN'
304 ,'Location to put binaries during installation'
305 ,"$INSTALL_PREFIX/bin"
306 )
307
308 opts.Add(
309 'INSTALL_LIB'
310 ,'Location to put libraries during installation'
311 ,"$INSTALL_PREFIX/lib"
312 )
313
314 opts.Add(
315 'INSTALL_SHARE'
316 ,'Common shared-file location on this system'
317 ,"$INSTALL_PREFIX/share"
318 )
319
320
321 opts.Add(
322 'INSTALL_ASCDATA'
323 ,"Location of ASCEND shared data (TK, python, models etc)"
324 ,"$INSTALL_SHARE/ascend"
325 )
326
327 opts.Add(
328 'INSTALL_INCLUDE'
329 ,'Location to put header files during installation'
330 ,"$INSTALL_PREFIX/include"
331 )
332
333 opts.Add(
334 'PYGTK_ASSETS'
335 ,'Default location for Glade assets (placed in pygtk/config.py)'
336 ,default_install_assets
337 )
338
339 opts.Add(BoolOption(
340 'DEBUG'
341 ,"Compile source with debugger symbols, eg for use with 'gdb'"
342 ,False
343 ))
344
345 opts.Add(BoolOption(
346 'MALLOC_DEBUG'
347 ,"Compile with debugging version of MALLOC. Required for full CUnit testing"
348 ,False
349 ))
350
351 opts.Add(
352 'INSTALL_ROOT'
353 ,'For use by RPM only: location of %{buildroot} during rpmbuild'
354 ,""
355 )
356
357 opts.Add(
358 'DISTTAR_NAME'
359 ,"Stem name of the tarball created by 'scons dist'. So for 'ascend-aaa.tar.bz2', set this to 'ascend-aaa'."
360 ,"ascend-"+version
361 )
362
363 opts.Add(
364 'RELEASE'
365 ,"Release number for use in RPM spec file. This should always start with a zero for releases made by the ASCEND group, in order that third parties can make 'patch' releases of higher version numbers."
366 ,"0"
367 )
368
369 opts.Add(BoolOption(
370 'ABSOLUTE_PATHS'
371 ,"Whether to use absolute or relative paths in the installed Tcl/Tk interface. If you want to build an RPM, set this to false."
372 ,default_absolute_paths
373 ))
374
375 opts.Add(
376 'WIN_INSTALLER_NAME'
377 ,"Name of the installer .exe to create under Windows (minus the '.exe')"
378 ,"ascend-"+version
379 )
380
381 opts.Add(BoolOption(
382 'WITH_XTERM_COLORS'
383 ,"Set to 0 if you don't want xterm colour codes in the console output"
384 ,True
385 ))
386
387 if platform.system()!="Windows":
388 opts.Add(BoolOption(
389 'WITH_GCCVISIBILITY'
390 , 'Whether to use GCC Visibility extensions when building with GCC 4.0'
391 , True
392 ))
393
394 # TODO: OTHER OPTIONS?
395 # TODO: flags for optimisation
396 # TODO: turning on/off bintoken functionality
397 # TODO: Where will the 'Makefile.bt' file be installed?
398
399 # Import the outside environment
400
401 if os.environ.get('OSTYPE')=='msys':
402 env = Environment(
403 ENV=os.environ
404 , tools=['mingw','lex','yacc','fortran','swig','disttar','nsis']
405 , toolpath=['scons']
406 )
407 env['IS_MINGW']=True
408 else:
409 env = Environment(
410 ENV=os.environ
411 ,tools=['default','lex','yacc','fortran','swig','disttar','nsis']
412 , toolpath=['scons']
413 )
414
415 if platform.system()=='Windows' and env.has_key('MSVS'):
416 print "INCLUDE =",env['ENV']['INCLUDE']
417 print "LIB =",env['ENV']['LIB']
418 print "PATH =",env['ENV']['PATH']
419 env.Append(CPPPATH=env['ENV']['INCLUDE'])
420 env.Append(LIBPATH=env['ENV']['LIB'])
421 env.Append(CPPDEFINES=['_CRT_SECURE_NO_DEPRECATE'])
422 env.Append(CCFLAGS=['/Za'])
423
424 opts.Update(env)
425 opts.Save('options.cache',env)
426
427 Help(opts.GenerateHelpText(env))
428
429 with_tcltk = env.get('WITH_TCLTK')
430 without_tcltk_reason = "disabled by options/config.py"
431
432 with_python = env.get('WITH_PYTHON')
433 without_python_reason = "disabled by options/config.py"
434
435 with_cunit = env.get('WITH_CUNIT')
436 without_cunit_reason = "not requested"
437
438 if platform.system()=="Windows":
439 with_installer=1
440 else:
441 with_installer=0
442 without_installer_reason = "only possible under Windows"
443
444 if 'LSOD' in env['WITH_SOLVERS']:
445 with_lsode=True
446 else:
447 with_lsode=False
448 without_lsode_reason = "not requested (WITH_SOLVERS)"
449
450 if 'IDA' in env['WITH_SOLVERS']:
451 with_ida=True
452 else:
453 with_ida=False
454 without_ida_reason = "not requested (WITH_SOLVERS)"
455
456
457 #print "SOLVERS:",env['WITH_SOLVERS']
458 #print "WITH_BINTOKEN:",env['WITH_BINTOKEN']
459 #print "DEFAULT_ASCENDLIBRARY:",env['DEFAULT_ASCENDLIBRARY']
460
461 can_install = True
462 if platform.system()=='Windows':
463 can_install = False
464
465 env['CAN_INSTALL']=can_install
466
467 print "TCL_CPPPATH =",env['TCL_CPPPATH']
468 print "TCL_LIBPATH =",env['TCL_LIBPATH']
469 print "TCL_LIB =",env['TCL_LIB']
470 print "CC =",env['CC']
471 print "CXX =",env['CXX']
472 print "FORTRAN=",env.get('FORTRAN')
473
474 print "ABSOLUTE PATHS =",env['ABSOLUTE_PATHS']
475 #------------------------------------------------------
476 # SPECIAL CONFIGURATION TESTS
477
478 need_fortran = False
479
480 #----------------
481 # SWIG
482
483 import os,re
484
485 def get_swig_version(env):
486 cmd = env['SWIG']+' -version'
487 (cin,coutcerr) = os.popen4(cmd)
488 output = coutcerr.read()
489
490 restr = "SWIG\\s+Version\\s+(?P<maj>[0-9]+)\\.(?P<min>[0-9]+)\\.(?P<pat>[0-9]+)\\s*$"
491 expr = re.compile(restr,re.M);
492 m = expr.search(output);
493 if not m:
494 return None
495 maj = int(m.group('maj'))
496 min = int(m.group('min'))
497 pat = int(m.group('pat'))
498
499 return (maj,min,pat)
500
501
502 def CheckSwigVersion(context):
503
504 try:
505 context.Message("Checking version of SWIG... ")
506 maj,min,pat = get_swig_version(context.env)
507 except:
508 context.Result("Failed to detect version, or failed to run SWIG")
509 return 0;
510
511 context.env['SWIGVERSION']=tuple([maj,min,pat])
512
513 if maj == 1 and (
514 min > 3
515 or (min == 3 and pat >= 24)
516 ):
517 context.Result("ok, %d.%d.%d" % (maj,min,pat))
518 return 1;
519 else:
520 context.Result("too old, %d.%d.%d" % (maj,min,pat))
521 return 0;
522
523 #----------------
524 # General purpose library-and-header test
525
526 class KeepContext:
527 def __init__(self,context,varprefix,static=False):
528 self.keep = {}
529 for k in ['LIBS','LIBPATH','CPPPATH','LINKFLAGS']:
530 #print "Keeping env %s = %s" % (k,context.env.get(k))
531 self.keep[k]=context.env.get(k)
532
533 if context.env.has_key(varprefix+'_CPPPATH'):
534 context.env.Append(CPPPATH=[env[varprefix+'_CPPPATH']])
535 #print "Adding '"+str(cpppath_add)+"' to cpp path"
536
537 if static:
538 staticlib=env[varprefix+'_LIB']
539 #print "STATIC LIB = ",staticlib
540 context.env.Append(
541 LINKFLAGS=[staticlib]
542 )
543 else:
544 if context.env.has_key(varprefix+'_LIBPATH'):
545 context.env.Append(LIBPATH=[env[varprefix+'_LIBPATH']])
546 #print "Adding '"+str(libpath_add)+"' to lib path"
547
548 if context.env.has_key(varprefix+'_LIB'):
549 context.env.Append(LIBS=[env[varprefix+'_LIB']])
550 #print "Adding '"+str(env[varprefix+'_LIB'])+"' to libs"
551
552 def restore(self,context):
553 #print "RESTORING CONTEXT"
554 #print self.keep
555 #print "..."
556 for k in self.keep:
557 if self.keep[k]==None:
558 if context.env.has_key(k):
559 #print "Clearing "+str(k)
560 del context.env[k];
561 else:
562 #print "Restoring %s to '%s'" %(k,self.keep.get(k))
563 context.env[k]=self.keep[k];
564
565 def CheckExtLib(context,libname,text,ext='.c',varprefix=None,static=False):
566 """This method will check for variables LIBNAME_LIBPATH
567 and LIBNAME_CPPPATH and try to compile and link the
568 file with the provided text, linking with the
569 library libname."""
570
571 if static:
572 context.Message( 'Checking for static '+libname+'... ' )
573 else:
574 context.Message( 'Checking for '+libname+'... ' )
575
576 if varprefix==None:
577 varprefix = libname.upper()
578
579 #print "LIBS is currently:",context.env.get('LIBS')
580 keep = KeepContext(context,varprefix,static)
581
582 if not context.env.has_key(varprefix+'_LIB'):
583 # if varprefix_LIB were in env, KeepContext would
584 # have appended it already
585 context.env.Append(LIBS=[libname])
586
587 is_ok = context.TryLink(text,ext)
588
589 #print "Link success? ",(is_ok != 0)
590
591 keep.restore(context)
592
593 # print "Restored CPPPATH="+str(context.env['CPPPATH'])
594 # print "Restored LIBS="+str(context.env['LIBS'])
595 # print "Restored LIBPATH="+str(context.env['LIBPATH'])
596
597 context.Result(is_ok)
598 return is_ok
599
600 #----------------
601 # GCC
602
603 gcc_test_text = """
604 #ifndef __GNUC__
605 # error "Not using GCC"
606 #endif
607
608 int main(void){
609 return __GNUC__;
610 }
611 """
612
613 def CheckGcc(context):
614 context.Message("Checking for GCC... ")
615 is_ok = context.TryCompile(gcc_test_text,".c")
616 context.Result(is_ok)
617 return is_ok
618
619 #----------------
620 # GCC VISIBILITY feature
621
622 gccvisibility_test_text = """
623 #if __GNUC__ < 4
624 # error "Require GCC version 4 or newer"
625 #endif
626
627 __attribute__ ((visibility("default"))) int x;
628
629 int main(void){
630 extern int x;
631 x = 4;
632 }
633 """
634
635 def CheckGccVisibility(context):
636 context.Message("Checking for GCC 'visibility' capability... ")
637 if not context.env.has_key('WITH_GCCVISIBILITY') or not env['WITH_GCCVISIBILITY']:
638 context.Result("disabled")
639 return 0
640 is_ok = context.TryCompile(gccvisibility_test_text,".c")
641 context.Result(is_ok)
642 return is_ok
643
644 #----------------
645 # YACC
646
647 yacc_test_text = """
648 %{
649 #include <stdio.h>
650 %}
651 %token MSG
652 %start ROOT
653 %%
654 ROOT:
655 MSG { printf("HELLO"); }
656 ;
657 %%
658 """
659
660 def CheckYacc(context):
661 context.Message("Checking for Yacc ('%s')... " % context.env.get('YACC'))
662 is_ok = context.TryCompile(yacc_test_text,".y")
663 context.Result(is_ok)
664 return is_ok
665
666 #----------------
667 # CUnit test
668
669 cunit_test_text = """
670 #include <CUnit/CUnit.h>
671 int maxi(int i1, int i2){
672 return (i1 > i2) ? i1 : i2;
673 }
674
675 void test_maxi(void){
676 CU_ASSERT(maxi(0,2) == 2);
677 CU_ASSERT(maxi(0,-2) == 0);
678 CU_ASSERT(maxi(2,2) == 2);
679
680 }
681 int main(void){
682 /* CU_initialize_registry() */
683 return 0;
684 }
685 """
686
687 def CheckCUnit(context):
688 return CheckExtLib(context,'cunit',cunit_test_text)
689
690 #----------------
691 # MATH test
692
693 math_test_text = """
694 #ifndef _ALL_SOURCE
695 # define _ALL_SOURCE
696 #endif
697 #ifndef _XOPEN_SOURCE
698 # define _XOPEN_SOURCE
699 #endif
700 #ifndef _XOPEN_SOURCE_EXTENDED
701 # define _XOPEN_SOURCE_EXTENDED 1
702 #endif
703 #include <math.h>
704 int main(void){
705 double x = 1.0; double y = 1.0; int i = 1;
706 acosh(x); asinh(x); atanh(x); cbrt(x); expm1(x); erf(x); erfc(x); isnan(x);
707 j0(x); j1(x); jn(i,x); ilogb(x); logb(x); log1p(x); rint(x);
708 y0(x); y1(x); yn(i,x);
709 #ifdef _THREAD_SAFE
710 gamma_r(x,&i);
711 lgamma_r(x,&i);
712 #else
713 gamma(x);
714 lgamma(x);
715 #endif
716 hypot(x,y); nextafter(x,y); remainder(x,y); scalb(x,y);
717 return 0;
718 }
719 """
720
721 def CheckMath(context):
722 context.Message('Checking for IEE math library... ')
723 libsave=context.env.get('LIBS');
724 context.env.AppendUnique(LIBS=['m'])
725 is_ok=context.TryLink(math_test_text,".c")
726 context.Result(is_ok)
727 if not is_ok:
728 context.env['LIBS']=libsave
729 return is_ok
730
731 #----------------
732 # IDA test
733
734 ida_test_text = """
735 #include <ida.h>
736 #include <nvector_serial.h>
737 #include <ida_spgmr.h>
738 int main(){
739 void *ida_mem;
740 ida_mem = IDACreate();
741 }
742 """
743
744 def CheckIDA(context):
745 context.Message( 'Checking for IDA (SUNDIALS)... ' )
746
747 # add SUNDIALS subdirectories as well (what a pain)
748 if context.env.get('IDA_CPPPATH'):
749 extra = [context.env['IDA_CPPPATH']+"/ida",context.env['IDA_CPPPATH']+"/sundials"]
750 context.env.Append(CPPPATH=extra)
751
752 if ',' in context.env.get('IDA_LIB'):
753 context.env['IDA_LIB']=context.env['IDA_LIB'].split(',')
754 #print "IDA_LIB NOW =",context.env['IDA_LIB']
755 else:
756 print "NO COMMA IN IDA_LIB:",context.env['IDA_LIB']
757
758 keep = KeepContext(context,"IDA")
759
760 is_ok = context.TryLink(ida_test_text,".c")
761 context.Result(is_ok)
762
763 keep.restore(context)
764
765 if is_ok:
766 context.env.Append(IDA_CPPPATH_EXTRA=extra)
767
768 return is_ok
769
770 #----------------
771 # Tcl test
772
773 # TCL and TK required version 8.1, 8.2, 8.3, or 8.4:
774 tcltk_minor_newest_acceptable = 4
775 tcltk_major_required = 8
776
777 tcl_check_text = r"""
778 #include <tcl.h>
779 #include <stdio.h>
780 int main(void){
781 printf("%s",TCL_PATCH_LEVEL);
782 return 0;
783 }
784 """
785
786 def CheckTcl(context):
787 return CheckExtLib(context,'tcl',tcl_check_text,static=env['STATIC_TCLTK'])
788
789 def CheckTclVersion(context):
790 keep = KeepContext(context,'TCL',static=env['STATIC_TCLTK'])
791 context.Message("Checking Tcl version... ")
792 (is_ok,output) = context.TryRun(tcl_check_text,'.c')
793 keep.restore(context)
794 if not is_ok:
795 context.Result("failed to run check")
796 return 0
797
798 major,minor,patch = tuple([int(i) for i in output.split(".")])
799 if major != tcltk_major_required or minor > tcltk_minor_newest_acceptable:
800 context.Result(output+" (bad version)")
801 # bad version
802 return 0
803
804 # good version
805 context.Result(output+", good")
806 return 1
807
808 #----------------
809 # Tk test
810
811 tk_check_text = r"""
812 #include <tk.h>
813 #include <stdio.h>
814 int main(void){
815 printf("%s",TK_PATCH_LEVEL);
816 return 0;
817 }
818 """
819 def CheckTk(context):
820 return CheckExtLib(context,'tk',tk_check_text,static=env['STATIC_TCLTK'])
821
822
823 def CheckTkVersion(context):
824 keep = KeepContext(context,'TK',static=context.env['STATIC_TCLTK'])
825 context.Message("Checking Tk version... ")
826 #print "LINKFLAGS =",context.env['LINKFLAGS']
827 (is_ok,output) = context.TryRun(tk_check_text,'.c')
828 keep.restore(context)
829 if not is_ok:
830 context.Result("failed to run check")
831 return 0
832
833 major,minor,patch = tuple([int(i) for i in output.split(".")])
834 if major != tcltk_major_required or minor > tcltk_minor_newest_acceptable:
835 # bad version
836 context.Result(output+" (bad version)")
837 return 0
838
839 # good version
840 context.Result(output+" (good)")
841 return 1
842
843 #----------------
844 # Tktable test
845
846 tktable_check_text = r"""
847 #include <tkTable.h>
848 #include <stdio.h>
849 int main(void){
850 Table mytable;
851 return 0;
852 }
853 """
854
855 def CheckTkTable(context):
856 return CheckExtLib(context,'tktable',tktable_check_text,static=env['STATIC_TCLTK'])
857
858 #---------------
859 # X11 test
860
861 x11_check_text = r"""
862 #include <X11/Xlib.h>
863 #include <X11/IntrinsicP.h>
864 #include <X11/Intrinsic.h>
865 #include <X11/ObjectP.h>
866 #include <X11/Object.h>
867 int main(void){
868 Object mything;
869 return 0;
870 }
871 """
872
873 def CheckX11(context):
874 return CheckExtLib(context,'X11',x11_check_text)
875
876 #----------------
877 # GCC Version sniffing
878
879 # TODO FIXME
880
881 gcc_version4 = False
882
883 #------------------------------------------------------
884 # CONFIGURATION
885
886 conf = Configure(env
887 , custom_tests = {
888 'CheckMath' : CheckMath
889 , 'CheckSwigVersion' : CheckSwigVersion
890 , 'CheckCUnit' : CheckCUnit
891 , 'CheckTcl' : CheckTcl
892 , 'CheckTclVersion' : CheckTclVersion
893 , 'CheckTk' : CheckTk
894 , 'CheckTkVersion' : CheckTkVersion
895 , 'CheckGcc' : CheckGcc
896 , 'CheckGccVisibility' : CheckGccVisibility
897 , 'CheckYacc' : CheckYacc
898 , 'CheckTkTable' : CheckTkTable
899 , 'CheckX11' : CheckX11
900 , 'CheckIDA' : CheckIDA
901 # , 'CheckIsNan' : CheckIsNan
902 # , 'CheckCppUnitConfig' : CheckCppUnitConfig
903 }
904 # , config_h = "config.h"
905 )
906
907
908 # Math library
909
910 if need_libm:
911 if not conf.CheckMath():
912 print 'Did not find math library, exiting!'
913 Exit(1)
914 #pass
915
916 # Where is 'isnan'?
917
918 if not conf.CheckFunc('isnan'):
919 print "Didn't find isnan"
920 # Exit(1)
921
922 # GCC visibility
923
924 if conf.CheckGcc():
925 conf.env['HAVE_GCC']=True;
926 if env['WITH_GCCVISIBILITY'] and conf.CheckGccVisibility():
927 conf.env['HAVE_GCCVISIBILITY']=True;
928 conf.env.Append(CCFLAGS=['-fvisibility=hidden'])
929 conf.env.Append(CPPDEFINES=['HAVE_GCCVISIBILITY'])
930 conf.env.Append(CCFLAGS=['-Wall'])
931
932 # YACC
933
934 if not conf.CheckYacc():
935 print "YACC NOT FOUND OR NOT WORKING"
936 else:
937 conf.env['HAVE_YACC']=True
938
939 conf.env['HAVE_LEX']=True
940
941 # Tcl/Tk
942
943 if with_tcltk:
944 if conf.CheckTcl():
945 if conf.CheckTclVersion():
946 if conf.CheckTk():
947 if with_tcltk and conf.CheckTkVersion():
948 if env['STATIC_TCLTK']:
949 if conf.CheckTkTable():
950 pass
951 else:
952 without_tcltk_reason = "TkTable not found"
953 with_tcltk = False
954 else:
955 without_tcltk_reason = "Require Tk version <= 8.4. See 'scons -h'"
956 with_tcltk = False
957 else:
958 without_tcltk_reason = "Tk not found."
959 with_tcltk = False
960 else:
961 without_tcltk_reason = "Require Tcl <= 8.4 Tcl."
962 with_tcltk = False
963
964 else:
965 without_tcltk_reason = "Tcl not found."
966 with_tcltk = False
967
968 if env['STATIC_TCLTK']:
969 conf.CheckX11()
970
971 # Python... obviously we're already running python, so we just need to
972 # check that we can link to the python library OK:
973
974 if platform.system()=="Windows":
975 python_lib='python24'
976 else:
977 python_lib='python2.4'
978
979 # SWIG version
980
981 if not conf.CheckSwigVersion():
982 without_python_reason = 'SWIG >= 1.3.24 is required'
983 with_python = False
984
985 # CUnit
986
987 if with_cunit:
988 if not conf.CheckCUnit():
989 without_cunit_reason = 'CUnit not found'
990 with_cunit = False
991 #print "CUNIT NOT FOUND, LIBS=",conf.env.get('LIBS')
992
993 # IDA
994
995 if not with_ida:
996 without_ida_reason = "Not selected (see config option WITH_SOLVERS)"
997 elif not conf.CheckIDA():
998 with_ida = False
999 without_ida_reason = "IDA not found"
1000
1001 # BLAS
1002
1003 need_blas=False
1004
1005 if with_lsode:
1006 need_fortran = True
1007 need_blas=True
1008
1009 if need_blas:
1010 if conf.CheckLib('blas'):
1011 with_local_blas = False
1012 without_local_blas_reason = "Found BLAS installed on system"
1013 else:
1014 with_local_blas = True
1015 need_fortran = True
1016 else:
1017 with_local_blas= False;
1018 without_local_blas_reason = "BLAS not required"
1019
1020 # FORTRAN
1021
1022 if need_fortran:
1023 conf.env.Tool('fortran')
1024 detect_fortran = conf.env.Detect(['g77','f77','gfortran'])
1025 if detect_fortran:
1026 # For some reason, g77 doesn't get detected properly on MinGW
1027 if not env.has_key('F77') and not env.has_key('FORTRAN'):
1028 conf.env.Replace(F77=detect_fortran)
1029 conf.env.Replace(F77COM='$F77 $F77FLAGS -c -o $TARGET $SOURCE')
1030 conf.env.Replace(F77FLAGS='')
1031 #print "F77:",conf.env['F77']
1032 #print "F77COM:",conf.env['F77COM']
1033 #print "F77FLAGS:",conf.env['F77FLAGS']
1034 fortran_builder = Builder(
1035 action='$F77COM'
1036 , suffix='.o'
1037 , src_suffix='.f'
1038 )
1039 conf.env.Append(BUILDERS={'Fortran':fortran_builder})
1040 else:
1041 with_lsode=False;
1042 without_lsode_reason="FORTRAN-77 required but not found"
1043
1044 #else:
1045 # print "FORTRAN not required"
1046
1047 # F2C
1048
1049 if need_fortran:
1050 if platform.system()=="Windows":
1051 conf.env.Append(LIBPATH='c:\mingw\lib')
1052
1053
1054 # TODO: -D_HPUX_SOURCE is needed
1055
1056 # TODO: check size of void*
1057
1058 # TODO: detect if dynamic libraries are possible or not
1059
1060 if platform.system()=="Windows" and env.has_key('MSVS'):
1061 if not conf.CheckHeader('windows.h') and env['PACKAGE_LINKING']=='DYNAMIC_PACKAGES':
1062 print "Reverting to STATIC_PACKAGES since windows.h is not available. Probably you "\
1063 +"need to install the Microsoft Windows Server 2003 Platform SDK, or similar."
1064 env['PACKAGE_LINKING']='STATIC_PACKAGES'
1065
1066 if with_python and not conf.CheckHeader(['basetsd.h','BaseTsd.h']):
1067 with_python = 0;
1068 without_python_reason = "Header file 'basetsd.h' not found. Install the MS Platform SDK."
1069
1070 conf.env.Append(CPPDEFINES=env['PACKAGE_LINKING'])
1071
1072 conf.Finish()
1073
1074 env.Append(PYTHON_LIBPATH=[distutils.sysconfig.PREFIX+"/libs"])
1075 env.Append(PYTHON_LIB=[python_lib])
1076 env.Append(PYTHON_CPPPATH=[distutils.sysconfig.get_python_inc()])
1077
1078 #---------------------------------------
1079 # SUBSTITUTION DICTIONARY for .in files
1080
1081 release = env.get('RELEASE')
1082 if release=="0.":
1083 release="0"
1084
1085 subst_dict = {
1086 '@DEFAULT_ASCENDLIBRARY@':env['DEFAULT_ASCENDLIBRARY']
1087 , '@GLADE_FILE@':'ascend.glade'
1088 , '@HELP_ROOT@':''
1089 , '@ICON_EXTENSION@':icon_extension
1090 , '@INSTALL_ASCDATA@':env['INSTALL_ASCDATA']
1091 , '@INSTALL_BIN@':env['INSTALL_BIN']
1092 , '@INSTALL_INCLUDE@':env['INSTALL_INCLUDE']
1093 , '@INSTALL_LIB@':env['INSTALL_LIB']
1094 , '@PYGTK_ASSETS@':env['PYGTK_ASSETS']
1095 , '@VERSION@':version
1096 , '@RELEASE@':release
1097 , '@DISTTAR_NAME@':env['DISTTAR_NAME']
1098 , '@WEBHELPROOT@':'http://pye.dyndns.org/ascend/manual/'
1099 , '@ASC_SHLIBSUFFIX@':env['SHLIBSUFFIX']
1100 , '@ASC_SHLIBPREFIX@':env['SHLIBPREFIX']
1101 , '@ASC_ENV_TK_DEFAULT@' : '$$ASCENDDIST/tcltk'
1102 , '@ASC_DISTDIR_REL_BIN@' : default_rel_distdir
1103 , '@PYTHON@' : python_exe
1104 }
1105
1106 if env.get('WITH_LOCAL_HELP'):
1107 print "WITH_LOCAL_HELP:",env['WITH_LOCAL_HELP']
1108 subst_dict['@HELP_ROOT@']=env['WITH_LOCAL_HELP']
1109
1110 # bool options...
1111 for k,v in {
1112 'ABSOLUTE_PATHS' : 'ASC_ABSOLUTE_PATHS'
1113 ,'WITH_XTERM_COLORS' : 'ASC_XTERM_COLORS'
1114 ,'MALLOC_DEBUG' : 'MALLOC_DEBUG'
1115 }.iteritems():
1116 if env.get(k):
1117 # subst_dict['@'+v+'@']='1'
1118 subst_dict["/\\* #define "+v+' @'+v+"@ \\*/"]='# define '+v+' 1 '
1119
1120 if with_ida:
1121 subst_dict["/\\* #define ASC_WITH_IDA @ASC_WITH_IDA@ \\*/"]='#define ASC_WITH_IDA '
1122
1123 if with_lsode:
1124 subst_dict["/\\* #define ASC_WITH_LSODE @ASC_WITH_LSODE@ \\*/"]='#define ASC_WITH_LSODE '
1125
1126 if with_python:
1127 subst_dict['@ASCXX_USE_PYTHON@']="1"
1128 env['WITH_PYTHON']=1;
1129
1130 if env.has_key('HAVE_GCCVISIBILITY'):
1131 subst_dict['@HAVE_GCCVISIBILITY@'] = "1"
1132
1133 env.Append(SUBST_DICT=subst_dict)
1134
1135 #------------------------------------------------------
1136 # RECIPE: SWIG scanner
1137
1138 import SCons.Script
1139
1140 SWIGScanner = SCons.Scanner.ClassicCPP(
1141 "SWIGScan"
1142 , ".i"
1143 , "CPPPATH"
1144 , '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")'
1145 )
1146
1147 env.Append(SCANNERS=[SWIGScanner])
1148
1149 #------------------------------------------------------
1150 # RECIPE: 'SubstInFile', used in pygtk SConscript
1151
1152 import re
1153 from SCons.Script import * # the usual scons stuff you get in a SConscript
1154
1155 def TOOL_SUBST(env):
1156 """Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT
1157 from the source to the target.
1158 The values of SUBST_DICT first have any construction variables expanded
1159 (its keys are not expanded).
1160 If a value of SUBST_DICT is a python callable function, it is called and
1161 the result is expanded as the value.
1162 If there's more than one source and more than one target, each target gets
1163 substituted from the corresponding source.
1164 """
1165 env.Append(TOOLS = 'SUBST')
1166 def do_subst_in_file(targetfile, sourcefile, dict):
1167 """Replace all instances of the keys of dict with their values.
1168 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
1169 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
1170 """
1171 try:
1172 f = open(sourcefile, 'rb')
1173 contents = f.read()
1174 f.close()
1175 except:
1176 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
1177 for (k,v) in dict.items():
1178 contents = re.sub(k, v, contents)
1179 try:
1180 f = open(targetfile, 'wb')
1181 f.write(contents)
1182 f.close()
1183 except:
1184 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
1185 return 0 # success
1186
1187 def subst_in_file(target, source, env):
1188 if not env.has_key('SUBST_DICT'):
1189 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
1190 d = dict(env['SUBST_DICT']) # copy it
1191 for (k,v) in d.items():
1192 if callable(v):
1193 d[k] = env.subst(v())
1194 elif SCons.Util.is_String(v):
1195 d[k]=env.subst(v)
1196 else:
1197 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
1198 for (t,s) in zip(target, source):
1199 return do_subst_in_file(str(t), str(s), d)
1200
1201 def subst_in_file_string(target, source, env):
1202 """This is what gets printed on the console."""
1203 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
1204 for (t,s) in zip(target, source)])
1205
1206 def subst_emitter(target, source, env):
1207 """Add dependency from substituted SUBST_DICT to target.
1208 Returns original target, source tuple unchanged.
1209 """
1210 d = env['SUBST_DICT'].copy() # copy it
1211 for (k,v) in d.items():
1212 if callable(v):
1213 d[k] = env.subst(v())
1214 elif SCons.Util.is_String(v):
1215 d[k]=env.subst(v)
1216 Depends(target, SCons.Node.Python.Value(d))
1217 return target, source
1218
1219 subst_action=SCons.Action.Action(subst_in_file, subst_in_file_string)
1220 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
1221
1222 TOOL_SUBST(env)
1223
1224 #------------------------------------------------------
1225 # Recipe for 'CHMOD' ACTION
1226
1227 import SCons
1228 from SCons.Script.SConscript import SConsEnvironment
1229 SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
1230 lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
1231
1232 def InstallPerm(env, dest, files, perm):
1233 obj = env.Install(dest, files)
1234 for i in obj:
1235 env.AddPostAction(i, env.Chmod(str(i), perm))
1236
1237 SConsEnvironment.InstallPerm = InstallPerm
1238
1239 # define wrappers
1240 SConsEnvironment.InstallProgram = lambda env, dest, files: InstallPerm(env, dest, files, 0755)
1241 SConsEnvironment.InstallHeader = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
1242 SConsEnvironment.InstallShared = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
1243
1244 #------------------------------------------------------
1245 # BUILD...
1246
1247 # so that #include <modulename/headername.h> works across all modules...
1248 env.Append(CPPPATH=['#base/generic'])
1249
1250 if env['DEBUG']:
1251 env.Append(CCFLAGS=['-g'])
1252
1253 if env['GCOV']:
1254 env.Append(
1255 CPPFLAGS=['-g','-fprofile-arcs','-ftest-coverage']
1256 , LIBS=['gcov']
1257 , LINKFLAGS=['-fprofile-arcs','-ftest-coverage']
1258 )
1259
1260 if with_ida:
1261 env.Append(WITH_IDA=1)
1262
1263 #-------------
1264 # TCL/TK GUI
1265
1266 if with_tcltk:
1267 env.SConscript(['tcltk/generic/interface/SConscript'],'env')
1268 else:
1269 print "Skipping... Tcl/Tk GUI isn't being built:",without_tcltk_reason
1270
1271 #-------------
1272 # PYTHON INTERFACE
1273
1274 if with_python:
1275 env.SConscript(['pygtk/SConscript'],'env')
1276 else:
1277 print "Skipping... Python GUI isn't being built:",without_python_reason
1278
1279 #------------
1280 # BASE/GENERIC SUBDIRECTORIES
1281
1282 libascend_env = env.Copy()
1283
1284 dirs = ['general','utilities','compiler','solver','packages']
1285
1286 srcs = []
1287 for d in dirs:
1288 heresrcs = libascend_env.SConscript('base/generic/'+d+'/SConscript','libascend_env')
1289 srcs += heresrcs
1290
1291 #-------------
1292 # IMPORTED CODE: LSODE, BLAS, etc
1293
1294 if with_lsode:
1295 srcs += env.SConscript(['lsod/SConscript'],'env')
1296 srcs += env.SConscript(['linpack/SConscript'],'env')
1297 else:
1298 print "Skipping... LSODE won't be built:", without_lsode_reason
1299
1300 if with_local_blas:
1301 srcs += env.SConscript(['blas/SConscript'],'env')
1302 else:
1303 print "Skipping... BLAS won't be built:", without_local_blas_reason
1304
1305 if not with_ida:
1306 print "Skipping... IDA won't be built:", without_ida_reason
1307
1308 #-------------
1309 # LIBASCEND -- all base/generic functionality
1310
1311 libascend = libascend_env.SharedLibrary('ascend',srcs)
1312
1313 env.Alias('libascend',libascend)
1314
1315 #-------------
1316 # UNIT TESTS
1317
1318 if with_cunit:
1319 testdirs = ['general','solver','utilities']
1320 testsrcs = []
1321 for testdir in testdirs:
1322 path = 'base/generic/'+testdir+'/test/'
1323 env.SConscript([path+'SConscript'],'env')
1324 testsrcs += [i.path for i in env['TESTSRCS_'+testdir.upper()]]
1325
1326 #print "TESTSRCS =",testsrcs
1327
1328 env.SConscript(['test/SConscript'],'env')
1329 env.SConscript(['base/generic/test/SConscript'],'env')
1330
1331 env.Alias('test',[env.Dir('test'),env.Dir('base/generic/test')])
1332
1333 else:
1334 print "Skipping... CUnit tests aren't being built:",without_cunit_reason
1335
1336 #------------------------------------------------------
1337 # CREATE ASCEND-CONFIG scriptlet
1338
1339 ascendconfig = env.SubstInFile('ascend-config.in')
1340
1341 #------------------------------------------------------
1342 # INSTALLATION
1343
1344 if env.get('CAN_INSTALL'):
1345 # the models directory only needs to be processed for installation, no other processing required.
1346 env.SConscript(['models/SConscript'],'env')
1347
1348 dirs = ['INSTALL_BIN','INSTALL_ASCDATA','INSTALL_LIB']
1349 install_dirs = [env['INSTALL_ROOT']+env[d] for d in dirs]
1350
1351 # TODO: add install options
1352 env.Alias('install',install_dirs)
1353
1354 env.InstallShared(env['INSTALL_ROOT']+env['INSTALL_LIB'],libascend)
1355
1356 env.InstallProgram(env['INSTALL_ROOT']+env['INSTALL_BIN'],ascendconfig)
1357
1358 #------------------------------------------------------
1359 # WINDOWS INSTALLER
1360 # For the windows installer, please see pygtk/SConscript
1361
1362 if with_installer:
1363 pass
1364 else:
1365 print "Skipping... Windows installer isn't being built:",without_installer_reason
1366
1367 #------------------------------------------------------
1368 # PROJECT FILE for MSVC
1369
1370 env.SConscript(['base/msvc/SConscript'],['env','libascend']);
1371
1372 #------------------------------------------------------
1373 # CREATE the SPEC file for generation of RPM packages
1374
1375 if platform.system()=="Linux":
1376 env.SubstInFile('ascend.spec.in')
1377
1378 #------------------------------------------------------
1379 # DISTRIBUTION TAR FILE
1380
1381 env['DISTTAR_FORMAT']='bz2'
1382 env.Append(
1383 DISTTAR_EXCLUDEEXTS=['.o','.os','.so','.a','.dll','.cc','.cache','.pyc','.cvsignore','.dblite','.log','.pl']
1384 , DISTTAR_EXCLUDEDIRS=['CVS','.svn','.sconf_temp', 'dist']
1385 )
1386
1387 tar = env.DistTar("dist/"+env['DISTTAR_NAME']
1388 , [env.Dir('#')]
1389 )
1390
1391 env.Depends(tar,'ascend.spec')
1392
1393 #------------------------------------------------------
1394 # RPM BUILD
1395
1396 #if platform.system()=="Linux":
1397 # pass
1398
1399 #------------------------------------------------------
1400 # DEFAULT TARGETS
1401
1402 default_targets =['libascend']
1403 if with_tcltk:
1404 default_targets.append('tcltk')
1405 if with_python:
1406 default_targets.append('pygtk')
1407 if with_installer:
1408 default_targets.append('installer')
1409
1410 env.Default(default_targets)
1411
1412 print "Building targets:"," ".join([str(i) for i in BUILD_TARGETS])
1413
1414 # vim: set syntax=python:
1415

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