/[ascend]/trunk/SConstruct
ViewVC logotype

Contents of /trunk/SConstruct

Parent Directory Parent Directory | Revision Log Revision Log


Revision 721 - (show annotations) (download)
Mon Jul 3 06:22:42 2006 UTC (16 years, 11 months ago) by johnpye
File size: 34828 byte(s)
Added '--models' option to ascend-config.
Fixed a bug with packages.c wrt generating absolute paths to library files.
Added SCons directives to install header files in INSTALL_INCLUDE subdirs.
Changed rel.c so that init function is only run if provided in the package.
Added 'ospath_getabs' to convert relative paths into absolute paths by adding prefix of fully-resolved '.'
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 env['INSTALL_MODELS']=env['INSTALL_ASCDATA']+"/models/"
468
469 print "TCL_CPPPATH =",env['TCL_CPPPATH']
470 print "TCL_LIBPATH =",env['TCL_LIBPATH']
471 print "TCL_LIB =",env['TCL_LIB']
472 print "CC =",env['CC']
473 print "CXX =",env['CXX']
474 print "FORTRAN=",env.get('FORTRAN')
475
476 print "ABSOLUTE PATHS =",env['ABSOLUTE_PATHS']
477 #------------------------------------------------------
478 # SPECIAL CONFIGURATION TESTS
479
480 need_fortran = False
481
482 #----------------
483 # SWIG
484
485 import os,re
486
487 def get_swig_version(env):
488 cmd = env['SWIG']+' -version'
489 (cin,coutcerr) = os.popen4(cmd)
490 output = coutcerr.read()
491
492 restr = "SWIG\\s+Version\\s+(?P<maj>[0-9]+)\\.(?P<min>[0-9]+)\\.(?P<pat>[0-9]+)\\s*$"
493 expr = re.compile(restr,re.M);
494 m = expr.search(output);
495 if not m:
496 return None
497 maj = int(m.group('maj'))
498 min = int(m.group('min'))
499 pat = int(m.group('pat'))
500
501 return (maj,min,pat)
502
503
504 def CheckSwigVersion(context):
505
506 try:
507 context.Message("Checking version of SWIG... ")
508 maj,min,pat = get_swig_version(context.env)
509 except:
510 context.Result("Failed to detect version, or failed to run SWIG")
511 return 0;
512
513 context.env['SWIGVERSION']=tuple([maj,min,pat])
514
515 if maj == 1 and (
516 min > 3
517 or (min == 3 and pat >= 24)
518 ):
519 context.Result("ok, %d.%d.%d" % (maj,min,pat))
520 return 1;
521 else:
522 context.Result("too old, %d.%d.%d" % (maj,min,pat))
523 return 0;
524
525 #----------------
526 # General purpose library-and-header test
527
528 class KeepContext:
529 def __init__(self,context,varprefix,static=False):
530 self.keep = {}
531 for k in ['LIBS','LIBPATH','CPPPATH','LINKFLAGS']:
532 #print "Keeping env %s = %s" % (k,context.env.get(k))
533 self.keep[k]=context.env.get(k)
534
535 if context.env.has_key(varprefix+'_CPPPATH'):
536 context.env.Append(CPPPATH=[env[varprefix+'_CPPPATH']])
537 #print "Adding '"+str(cpppath_add)+"' to cpp path"
538
539 if static:
540 staticlib=env[varprefix+'_LIB']
541 #print "STATIC LIB = ",staticlib
542 context.env.Append(
543 LINKFLAGS=[staticlib]
544 )
545 else:
546 if context.env.has_key(varprefix+'_LIBPATH'):
547 context.env.Append(LIBPATH=[env[varprefix+'_LIBPATH']])
548 #print "Adding '"+str(libpath_add)+"' to lib path"
549
550 if context.env.has_key(varprefix+'_LIB'):
551 context.env.Append(LIBS=[env[varprefix+'_LIB']])
552 #print "Adding '"+str(env[varprefix+'_LIB'])+"' to libs"
553
554 def restore(self,context):
555 #print "RESTORING CONTEXT"
556 #print self.keep
557 #print "..."
558 for k in self.keep:
559 if self.keep[k]==None:
560 if context.env.has_key(k):
561 #print "Clearing "+str(k)
562 del context.env[k];
563 else:
564 #print "Restoring %s to '%s'" %(k,self.keep.get(k))
565 context.env[k]=self.keep[k];
566
567 def CheckExtLib(context,libname,text,ext='.c',varprefix=None,static=False):
568 """This method will check for variables LIBNAME_LIBPATH
569 and LIBNAME_CPPPATH and try to compile and link the
570 file with the provided text, linking with the
571 library libname."""
572
573 if static:
574 context.Message( 'Checking for static '+libname+'... ' )
575 else:
576 context.Message( 'Checking for '+libname+'... ' )
577
578 if varprefix==None:
579 varprefix = libname.upper()
580
581 #print "LIBS is currently:",context.env.get('LIBS')
582 keep = KeepContext(context,varprefix,static)
583
584 if not context.env.has_key(varprefix+'_LIB'):
585 # if varprefix_LIB were in env, KeepContext would
586 # have appended it already
587 context.env.Append(LIBS=[libname])
588
589 is_ok = context.TryLink(text,ext)
590
591 #print "Link success? ",(is_ok != 0)
592
593 keep.restore(context)
594
595 # print "Restored CPPPATH="+str(context.env['CPPPATH'])
596 # print "Restored LIBS="+str(context.env['LIBS'])
597 # print "Restored LIBPATH="+str(context.env['LIBPATH'])
598
599 context.Result(is_ok)
600 return is_ok
601
602 #----------------
603 # GCC
604
605 gcc_test_text = """
606 #ifndef __GNUC__
607 # error "Not using GCC"
608 #endif
609
610 int main(void){
611 return __GNUC__;
612 }
613 """
614
615 def CheckGcc(context):
616 context.Message("Checking for GCC... ")
617 is_ok = context.TryCompile(gcc_test_text,".c")
618 context.Result(is_ok)
619 return is_ok
620
621 #----------------
622 # GCC VISIBILITY feature
623
624 gccvisibility_test_text = """
625 #if __GNUC__ < 4
626 # error "Require GCC version 4 or newer"
627 #endif
628
629 __attribute__ ((visibility("default"))) int x;
630
631 int main(void){
632 extern int x;
633 x = 4;
634 }
635 """
636
637 def CheckGccVisibility(context):
638 context.Message("Checking for GCC 'visibility' capability... ")
639 if not context.env.has_key('WITH_GCCVISIBILITY') or not env['WITH_GCCVISIBILITY']:
640 context.Result("disabled")
641 return 0
642 is_ok = context.TryCompile(gccvisibility_test_text,".c")
643 context.Result(is_ok)
644 return is_ok
645
646 #----------------
647 # YACC
648
649 yacc_test_text = """
650 %{
651 #include <stdio.h>
652 %}
653 %token MSG
654 %start ROOT
655 %%
656 ROOT:
657 MSG { printf("HELLO"); }
658 ;
659 %%
660 """
661
662 def CheckYacc(context):
663 context.Message("Checking for Yacc ('%s')... " % context.env.get('YACC'))
664 is_ok = context.TryCompile(yacc_test_text,".y")
665 context.Result(is_ok)
666 return is_ok
667
668 #----------------
669 # CUnit test
670
671 cunit_test_text = """
672 #include <CUnit/CUnit.h>
673 int maxi(int i1, int i2){
674 return (i1 > i2) ? i1 : i2;
675 }
676
677 void test_maxi(void){
678 CU_ASSERT(maxi(0,2) == 2);
679 CU_ASSERT(maxi(0,-2) == 0);
680 CU_ASSERT(maxi(2,2) == 2);
681
682 }
683 int main(void){
684 /* CU_initialize_registry() */
685 return 0;
686 }
687 """
688
689 def CheckCUnit(context):
690 return CheckExtLib(context,'cunit',cunit_test_text)
691
692 #----------------
693 # MATH test
694
695 math_test_text = """
696 #ifndef _ALL_SOURCE
697 # define _ALL_SOURCE
698 #endif
699 #ifndef _XOPEN_SOURCE
700 # define _XOPEN_SOURCE
701 #endif
702 #ifndef _XOPEN_SOURCE_EXTENDED
703 # define _XOPEN_SOURCE_EXTENDED 1
704 #endif
705 #include <math.h>
706 int main(void){
707 double x = 1.0; double y = 1.0; int i = 1;
708 acosh(x); asinh(x); atanh(x); cbrt(x); expm1(x); erf(x); erfc(x); isnan(x);
709 j0(x); j1(x); jn(i,x); ilogb(x); logb(x); log1p(x); rint(x);
710 y0(x); y1(x); yn(i,x);
711 #ifdef _THREAD_SAFE
712 gamma_r(x,&i);
713 lgamma_r(x,&i);
714 #else
715 gamma(x);
716 lgamma(x);
717 #endif
718 hypot(x,y); nextafter(x,y); remainder(x,y); scalb(x,y);
719 return 0;
720 }
721 """
722
723 def CheckMath(context):
724 context.Message('Checking for IEE math library... ')
725 libsave=context.env.get('LIBS');
726 context.env.AppendUnique(LIBS=['m'])
727 is_ok=context.TryLink(math_test_text,".c")
728 context.Result(is_ok)
729 if not is_ok:
730 context.env['LIBS']=libsave
731 return is_ok
732
733 #----------------
734 # IDA test
735
736 ida_test_text = """
737 #include <ida.h>
738 #include <nvector_serial.h>
739 #include <ida_spgmr.h>
740 int main(){
741 void *ida_mem;
742 ida_mem = IDACreate();
743 }
744 """
745
746 def CheckIDA(context):
747 context.Message( 'Checking for IDA (SUNDIALS)... ' )
748
749 # add SUNDIALS subdirectories as well (what a pain)
750 if context.env.get('IDA_CPPPATH'):
751 extra = [context.env['IDA_CPPPATH']+"/ida",context.env['IDA_CPPPATH']+"/sundials"]
752 context.env.Append(CPPPATH=extra)
753
754 if ',' in context.env.get('IDA_LIB'):
755 context.env['IDA_LIB']=context.env['IDA_LIB'].split(',')
756 #print "IDA_LIB NOW =",context.env['IDA_LIB']
757 else:
758 print "NO COMMA IN IDA_LIB:",context.env['IDA_LIB']
759
760 keep = KeepContext(context,"IDA")
761
762 is_ok = context.TryLink(ida_test_text,".c")
763 context.Result(is_ok)
764
765 keep.restore(context)
766
767 if is_ok:
768 context.env.Append(IDA_CPPPATH_EXTRA=extra)
769
770 return is_ok
771
772 #----------------
773 # Tcl test
774
775 # TCL and TK required version 8.1, 8.2, 8.3, or 8.4:
776 tcltk_minor_newest_acceptable = 4
777 tcltk_major_required = 8
778
779 tcl_check_text = r"""
780 #include <tcl.h>
781 #include <stdio.h>
782 int main(void){
783 printf("%s",TCL_PATCH_LEVEL);
784 return 0;
785 }
786 """
787
788 def CheckTcl(context):
789 return CheckExtLib(context,'tcl',tcl_check_text,static=env['STATIC_TCLTK'])
790
791 def CheckTclVersion(context):
792 keep = KeepContext(context,'TCL',static=env['STATIC_TCLTK'])
793 context.Message("Checking Tcl version... ")
794 (is_ok,output) = context.TryRun(tcl_check_text,'.c')
795 keep.restore(context)
796 if not is_ok:
797 context.Result("failed to run check")
798 return 0
799
800 major,minor,patch = tuple([int(i) for i in output.split(".")])
801 if major != tcltk_major_required or minor > tcltk_minor_newest_acceptable:
802 context.Result(output+" (bad version)")
803 # bad version
804 return 0
805
806 # good version
807 context.Result(output+", good")
808 return 1
809
810 #----------------
811 # Tk test
812
813 tk_check_text = r"""
814 #include <tk.h>
815 #include <stdio.h>
816 int main(void){
817 printf("%s",TK_PATCH_LEVEL);
818 return 0;
819 }
820 """
821 def CheckTk(context):
822 return CheckExtLib(context,'tk',tk_check_text,static=env['STATIC_TCLTK'])
823
824
825 def CheckTkVersion(context):
826 keep = KeepContext(context,'TK',static=context.env['STATIC_TCLTK'])
827 context.Message("Checking Tk version... ")
828 #print "LINKFLAGS =",context.env['LINKFLAGS']
829 (is_ok,output) = context.TryRun(tk_check_text,'.c')
830 keep.restore(context)
831 if not is_ok:
832 context.Result("failed to run check")
833 return 0
834
835 major,minor,patch = tuple([int(i) for i in output.split(".")])
836 if major != tcltk_major_required or minor > tcltk_minor_newest_acceptable:
837 # bad version
838 context.Result(output+" (bad version)")
839 return 0
840
841 # good version
842 context.Result(output+" (good)")
843 return 1
844
845 #----------------
846 # Tktable test
847
848 tktable_check_text = r"""
849 #include <tkTable.h>
850 #include <stdio.h>
851 int main(void){
852 Table mytable;
853 return 0;
854 }
855 """
856
857 def CheckTkTable(context):
858 return CheckExtLib(context,'tktable',tktable_check_text,static=env['STATIC_TCLTK'])
859
860 #---------------
861 # X11 test
862
863 x11_check_text = r"""
864 #include <X11/Xlib.h>
865 #include <X11/IntrinsicP.h>
866 #include <X11/Intrinsic.h>
867 #include <X11/ObjectP.h>
868 #include <X11/Object.h>
869 int main(void){
870 Object mything;
871 return 0;
872 }
873 """
874
875 def CheckX11(context):
876 return CheckExtLib(context,'X11',x11_check_text)
877
878 #----------------
879 # GCC Version sniffing
880
881 # TODO FIXME
882
883 gcc_version4 = False
884
885 #------------------------------------------------------
886 # CONFIGURATION
887
888 conf = Configure(env
889 , custom_tests = {
890 'CheckMath' : CheckMath
891 , 'CheckSwigVersion' : CheckSwigVersion
892 , 'CheckCUnit' : CheckCUnit
893 , 'CheckTcl' : CheckTcl
894 , 'CheckTclVersion' : CheckTclVersion
895 , 'CheckTk' : CheckTk
896 , 'CheckTkVersion' : CheckTkVersion
897 , 'CheckGcc' : CheckGcc
898 , 'CheckGccVisibility' : CheckGccVisibility
899 , 'CheckYacc' : CheckYacc
900 , 'CheckTkTable' : CheckTkTable
901 , 'CheckX11' : CheckX11
902 , 'CheckIDA' : CheckIDA
903 # , 'CheckIsNan' : CheckIsNan
904 # , 'CheckCppUnitConfig' : CheckCppUnitConfig
905 }
906 # , config_h = "config.h"
907 )
908
909
910 # Math library
911
912 if need_libm:
913 if not conf.CheckMath():
914 print 'Did not find math library, exiting!'
915 Exit(1)
916 #pass
917
918 # Where is 'isnan'?
919
920 if not conf.CheckFunc('isnan'):
921 print "Didn't find isnan"
922 # Exit(1)
923
924 # GCC visibility
925
926 if conf.CheckGcc():
927 conf.env['HAVE_GCC']=True;
928 if env['WITH_GCCVISIBILITY'] and conf.CheckGccVisibility():
929 conf.env['HAVE_GCCVISIBILITY']=True;
930 conf.env.Append(CCFLAGS=['-fvisibility=hidden'])
931 conf.env.Append(CPPDEFINES=['HAVE_GCCVISIBILITY'])
932 conf.env.Append(CCFLAGS=['-Wall'])
933
934 # YACC
935
936 if not conf.CheckYacc():
937 print "YACC NOT FOUND OR NOT WORKING"
938 else:
939 conf.env['HAVE_YACC']=True
940
941 conf.env['HAVE_LEX']=True
942
943 # Tcl/Tk
944
945 if with_tcltk:
946 if conf.CheckTcl():
947 if conf.CheckTclVersion():
948 if conf.CheckTk():
949 if with_tcltk and conf.CheckTkVersion():
950 if env['STATIC_TCLTK']:
951 if conf.CheckTkTable():
952 pass
953 else:
954 without_tcltk_reason = "TkTable not found"
955 with_tcltk = False
956 else:
957 without_tcltk_reason = "Require Tk version <= 8.4. See 'scons -h'"
958 with_tcltk = False
959 else:
960 without_tcltk_reason = "Tk not found."
961 with_tcltk = False
962 else:
963 without_tcltk_reason = "Require Tcl <= 8.4 Tcl."
964 with_tcltk = False
965
966 else:
967 without_tcltk_reason = "Tcl not found."
968 with_tcltk = False
969
970 if env['STATIC_TCLTK']:
971 conf.CheckX11()
972
973 # Python... obviously we're already running python, so we just need to
974 # check that we can link to the python library OK:
975
976 if platform.system()=="Windows":
977 python_lib='python24'
978 else:
979 python_lib='python2.4'
980
981 # SWIG version
982
983 if not conf.CheckSwigVersion():
984 without_python_reason = 'SWIG >= 1.3.24 is required'
985 with_python = False
986
987 # CUnit
988
989 if with_cunit:
990 if not conf.CheckCUnit():
991 without_cunit_reason = 'CUnit not found'
992 with_cunit = False
993 #print "CUNIT NOT FOUND, LIBS=",conf.env.get('LIBS')
994
995 # IDA
996
997 if not with_ida:
998 without_ida_reason = "Not selected (see config option WITH_SOLVERS)"
999 elif not conf.CheckIDA():
1000 with_ida = False
1001 without_ida_reason = "IDA not found"
1002
1003 # BLAS
1004
1005 need_blas=False
1006
1007 if with_lsode:
1008 need_fortran = True
1009 need_blas=True
1010
1011 if need_blas:
1012 if conf.CheckLib('blas'):
1013 with_local_blas = False
1014 without_local_blas_reason = "Found BLAS installed on system"
1015 else:
1016 with_local_blas = True
1017 need_fortran = True
1018 else:
1019 with_local_blas= False;
1020 without_local_blas_reason = "BLAS not required"
1021
1022 # FORTRAN
1023
1024 if need_fortran:
1025 conf.env.Tool('fortran')
1026 detect_fortran = conf.env.Detect(['g77','f77','gfortran'])
1027 if detect_fortran:
1028 # For some reason, g77 doesn't get detected properly on MinGW
1029 if not env.has_key('F77') and not env.has_key('FORTRAN'):
1030 conf.env.Replace(F77=detect_fortran)
1031 conf.env.Replace(F77COM='$F77 $F77FLAGS -c -o $TARGET $SOURCE')
1032 conf.env.Replace(F77FLAGS='')
1033 #print "F77:",conf.env['F77']
1034 #print "F77COM:",conf.env['F77COM']
1035 #print "F77FLAGS:",conf.env['F77FLAGS']
1036 fortran_builder = Builder(
1037 action='$F77COM'
1038 , suffix='.o'
1039 , src_suffix='.f'
1040 )
1041 conf.env.Append(BUILDERS={'Fortran':fortran_builder})
1042 else:
1043 with_lsode=False;
1044 without_lsode_reason="FORTRAN-77 required but not found"
1045
1046 #else:
1047 # print "FORTRAN not required"
1048
1049 # F2C
1050
1051 if need_fortran:
1052 if platform.system()=="Windows":
1053 conf.env.Append(LIBPATH='c:\mingw\lib')
1054
1055
1056 # TODO: -D_HPUX_SOURCE is needed
1057
1058 # TODO: check size of void*
1059
1060 # TODO: detect if dynamic libraries are possible or not
1061
1062 if platform.system()=="Windows" and env.has_key('MSVS'):
1063 if not conf.CheckHeader('windows.h') and env['PACKAGE_LINKING']=='DYNAMIC_PACKAGES':
1064 print "Reverting to STATIC_PACKAGES since windows.h is not available. Probably you "\
1065 +"need to install the Microsoft Windows Server 2003 Platform SDK, or similar."
1066 env['PACKAGE_LINKING']='STATIC_PACKAGES'
1067
1068 if with_python and not conf.CheckHeader(['basetsd.h','BaseTsd.h']):
1069 with_python = 0;
1070 without_python_reason = "Header file 'basetsd.h' not found. Install the MS Platform SDK."
1071
1072 conf.env.Append(CPPDEFINES=env['PACKAGE_LINKING'])
1073
1074 conf.Finish()
1075
1076 env.Append(PYTHON_LIBPATH=[distutils.sysconfig.PREFIX+"/libs"])
1077 env.Append(PYTHON_LIB=[python_lib])
1078 env.Append(PYTHON_CPPPATH=[distutils.sysconfig.get_python_inc()])
1079
1080 #---------------------------------------
1081 # SUBSTITUTION DICTIONARY for .in files
1082
1083 release = env.get('RELEASE')
1084 if release=="0.":
1085 release="0"
1086
1087 subst_dict = {
1088 '@DEFAULT_ASCENDLIBRARY@':env['DEFAULT_ASCENDLIBRARY']
1089 , '@GLADE_FILE@':'ascend.glade'
1090 , '@HELP_ROOT@':''
1091 , '@ICON_EXTENSION@':icon_extension
1092 , '@INSTALL_ASCDATA@':env['INSTALL_ASCDATA']
1093 , '@INSTALL_BIN@':env['INSTALL_BIN']
1094 , '@INSTALL_INCLUDE@':env['INSTALL_INCLUDE']
1095 , '@INSTALL_LIB@':env['INSTALL_LIB']
1096 , '@INSTALL_MODELS@':env['INSTALL_MODELS']
1097 , '@PYGTK_ASSETS@':env['PYGTK_ASSETS']
1098 , '@VERSION@':version
1099 , '@RELEASE@':release
1100 , '@DISTTAR_NAME@':env['DISTTAR_NAME']
1101 , '@WEBHELPROOT@':'http://pye.dyndns.org/ascend/manual/'
1102 , '@ASC_SHLIBSUFFIX@':env['SHLIBSUFFIX']
1103 , '@ASC_SHLIBPREFIX@':env['SHLIBPREFIX']
1104 , '@ASC_ENV_TK_DEFAULT@' : '$$ASCENDDIST/tcltk'
1105 , '@ASC_DISTDIR_REL_BIN@' : default_rel_distdir
1106 , '@PYTHON@' : python_exe
1107 }
1108
1109 if env.get('WITH_LOCAL_HELP'):
1110 print "WITH_LOCAL_HELP:",env['WITH_LOCAL_HELP']
1111 subst_dict['@HELP_ROOT@']=env['WITH_LOCAL_HELP']
1112
1113 # bool options...
1114 for k,v in {
1115 'ABSOLUTE_PATHS' : 'ASC_ABSOLUTE_PATHS'
1116 ,'WITH_XTERM_COLORS' : 'ASC_XTERM_COLORS'
1117 ,'MALLOC_DEBUG' : 'MALLOC_DEBUG'
1118 }.iteritems():
1119 if env.get(k):
1120 # subst_dict['@'+v+'@']='1'
1121 subst_dict["/\\* #define "+v+' @'+v+"@ \\*/"]='# define '+v+' 1 '
1122
1123 if with_ida:
1124 subst_dict["/\\* #define ASC_WITH_IDA @ASC_WITH_IDA@ \\*/"]='#define ASC_WITH_IDA '
1125
1126 if with_lsode:
1127 subst_dict["/\\* #define ASC_WITH_LSODE @ASC_WITH_LSODE@ \\*/"]='#define ASC_WITH_LSODE '
1128
1129 if with_python:
1130 subst_dict['@ASCXX_USE_PYTHON@']="1"
1131 env['WITH_PYTHON']=1;
1132
1133 if env.has_key('HAVE_GCCVISIBILITY'):
1134 subst_dict['@HAVE_GCCVISIBILITY@'] = "1"
1135
1136 env.Append(SUBST_DICT=subst_dict)
1137
1138 #------------------------------------------------------
1139 # RECIPE: SWIG scanner
1140
1141 import SCons.Script
1142
1143 SWIGScanner = SCons.Scanner.ClassicCPP(
1144 "SWIGScan"
1145 , ".i"
1146 , "CPPPATH"
1147 , '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")'
1148 )
1149
1150 env.Append(SCANNERS=[SWIGScanner])
1151
1152 #------------------------------------------------------
1153 # RECIPE: 'SubstInFile', used in pygtk SConscript
1154
1155 import re
1156 from SCons.Script import * # the usual scons stuff you get in a SConscript
1157
1158 def TOOL_SUBST(env):
1159 """Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT
1160 from the source to the target.
1161 The values of SUBST_DICT first have any construction variables expanded
1162 (its keys are not expanded).
1163 If a value of SUBST_DICT is a python callable function, it is called and
1164 the result is expanded as the value.
1165 If there's more than one source and more than one target, each target gets
1166 substituted from the corresponding source.
1167 """
1168 env.Append(TOOLS = 'SUBST')
1169 def do_subst_in_file(targetfile, sourcefile, dict):
1170 """Replace all instances of the keys of dict with their values.
1171 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
1172 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
1173 """
1174 try:
1175 f = open(sourcefile, 'rb')
1176 contents = f.read()
1177 f.close()
1178 except:
1179 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
1180 for (k,v) in dict.items():
1181 contents = re.sub(k, v, contents)
1182 try:
1183 f = open(targetfile, 'wb')
1184 f.write(contents)
1185 f.close()
1186 except:
1187 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
1188 return 0 # success
1189
1190 def subst_in_file(target, source, env):
1191 if not env.has_key('SUBST_DICT'):
1192 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
1193 d = dict(env['SUBST_DICT']) # copy it
1194 for (k,v) in d.items():
1195 if callable(v):
1196 d[k] = env.subst(v())
1197 elif SCons.Util.is_String(v):
1198 d[k]=env.subst(v)
1199 else:
1200 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
1201 for (t,s) in zip(target, source):
1202 return do_subst_in_file(str(t), str(s), d)
1203
1204 def subst_in_file_string(target, source, env):
1205 """This is what gets printed on the console."""
1206 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
1207 for (t,s) in zip(target, source)])
1208
1209 def subst_emitter(target, source, env):
1210 """Add dependency from substituted SUBST_DICT to target.
1211 Returns original target, source tuple unchanged.
1212 """
1213 d = env['SUBST_DICT'].copy() # copy it
1214 for (k,v) in d.items():
1215 if callable(v):
1216 d[k] = env.subst(v())
1217 elif SCons.Util.is_String(v):
1218 d[k]=env.subst(v)
1219 Depends(target, SCons.Node.Python.Value(d))
1220 return target, source
1221
1222 subst_action=SCons.Action.Action(subst_in_file, subst_in_file_string)
1223 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
1224
1225 TOOL_SUBST(env)
1226
1227 #------------------------------------------------------
1228 # Recipe for 'CHMOD' ACTION
1229
1230 import SCons
1231 from SCons.Script.SConscript import SConsEnvironment
1232 SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
1233 lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
1234
1235 def InstallPerm(env, dest, files, perm):
1236 obj = env.Install(dest, files)
1237 for i in obj:
1238 env.AddPostAction(i, env.Chmod(str(i), perm))
1239
1240 SConsEnvironment.InstallPerm = InstallPerm
1241
1242 # define wrappers
1243 SConsEnvironment.InstallProgram = lambda env, dest, files: InstallPerm(env, dest, files, 0755)
1244 SConsEnvironment.InstallHeader = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
1245 SConsEnvironment.InstallShared = lambda env, dest, files: InstallPerm(env, dest, files, 0644)
1246
1247 #------------------------------------------------------
1248 # BUILD...
1249
1250 # so that #include <modulename/headername.h> works across all modules...
1251 env.Append(CPPPATH=['#base/generic'])
1252
1253 if env['DEBUG']:
1254 env.Append(CCFLAGS=['-g'])
1255
1256 if env['GCOV']:
1257 env.Append(
1258 CPPFLAGS=['-g','-fprofile-arcs','-ftest-coverage']
1259 , LIBS=['gcov']
1260 , LINKFLAGS=['-fprofile-arcs','-ftest-coverage']
1261 )
1262
1263 if with_ida:
1264 env.Append(WITH_IDA=1)
1265
1266 #-------------
1267 # TCL/TK GUI
1268
1269 if with_tcltk:
1270 env.SConscript(['tcltk/generic/interface/SConscript'],'env')
1271 else:
1272 print "Skipping... Tcl/Tk GUI isn't being built:",without_tcltk_reason
1273
1274 #-------------
1275 # PYTHON INTERFACE
1276
1277 if with_python:
1278 env.SConscript(['pygtk/SConscript'],'env')
1279 else:
1280 print "Skipping... Python GUI isn't being built:",without_python_reason
1281
1282 #------------
1283 # BASE/GENERIC SUBDIRECTORIES
1284
1285 libascend_env = env.Copy()
1286
1287 dirs = ['general','utilities','compiler','solver','packages']
1288
1289 srcs = []
1290 for d in dirs:
1291 heresrcs = libascend_env.SConscript('base/generic/'+d+'/SConscript','libascend_env')
1292 srcs += heresrcs
1293
1294 #-------------
1295 # IMPORTED CODE: LSODE, BLAS, etc
1296
1297 if with_lsode:
1298 srcs += env.SConscript(['lsod/SConscript'],'env')
1299 srcs += env.SConscript(['linpack/SConscript'],'env')
1300 else:
1301 print "Skipping... LSODE won't be built:", without_lsode_reason
1302
1303 if with_local_blas:
1304 srcs += env.SConscript(['blas/SConscript'],'env')
1305 else:
1306 print "Skipping... BLAS won't be built:", without_local_blas_reason
1307
1308 if not with_ida:
1309 print "Skipping... IDA won't be built:", without_ida_reason
1310
1311 #-------------
1312 # LIBASCEND -- all base/generic functionality
1313
1314 libascend = libascend_env.SharedLibrary('ascend',srcs)
1315
1316 env.Alias('libascend',libascend)
1317
1318 #-------------
1319 # UNIT TESTS
1320
1321 if with_cunit:
1322 testdirs = ['general','solver','utilities']
1323 testsrcs = []
1324 for testdir in testdirs:
1325 path = 'base/generic/'+testdir+'/test/'
1326 env.SConscript([path+'SConscript'],'env')
1327 testsrcs += [i.path for i in env['TESTSRCS_'+testdir.upper()]]
1328
1329 #print "TESTSRCS =",testsrcs
1330
1331 env.SConscript(['test/SConscript'],'env')
1332 env.SConscript(['base/generic/test/SConscript'],'env')
1333
1334 env.Alias('test',[env.Dir('test'),env.Dir('base/generic/test')])
1335
1336 else:
1337 print "Skipping... CUnit tests aren't being built:",without_cunit_reason
1338
1339 #------------------------------------------------------
1340 # CREATE ASCEND-CONFIG scriptlet
1341
1342 ascendconfig = env.SubstInFile('ascend-config.in')
1343
1344 #------------------------------------------------------
1345 # INSTALLATION
1346
1347 if env.get('CAN_INSTALL'):
1348 # the models directory only needs to be processed for installation, no other processing required.
1349 env.SConscript(['models/SConscript'],'env')
1350
1351 dirs = ['INSTALL_BIN','INSTALL_ASCDATA','INSTALL_LIB', 'INSTALL_INCLUDE']
1352 install_dirs = [env['INSTALL_ROOT']+env[d] for d in dirs]
1353
1354 # TODO: add install options
1355 env.Alias('install',install_dirs)
1356
1357 env.InstallShared(env['INSTALL_ROOT']+env['INSTALL_LIB'],libascend)
1358
1359 env.InstallProgram(env['INSTALL_ROOT']+env['INSTALL_BIN'],ascendconfig)
1360
1361 #------------------------------------------------------
1362 # WINDOWS INSTALLER
1363 # For the windows installer, please see pygtk/SConscript
1364
1365 if with_installer:
1366 pass
1367 else:
1368 print "Skipping... Windows installer isn't being built:",without_installer_reason
1369
1370 #------------------------------------------------------
1371 # PROJECT FILE for MSVC
1372
1373 env.SConscript(['base/msvc/SConscript'],['env','libascend']);
1374
1375 #------------------------------------------------------
1376 # CREATE the SPEC file for generation of RPM packages
1377
1378 if platform.system()=="Linux":
1379 env.SubstInFile('ascend.spec.in')
1380
1381 #------------------------------------------------------
1382 # DISTRIBUTION TAR FILE
1383
1384 env['DISTTAR_FORMAT']='bz2'
1385 env.Append(
1386 DISTTAR_EXCLUDEEXTS=['.o','.os','.so','.a','.dll','.cc','.cache','.pyc','.cvsignore','.dblite','.log','.pl']
1387 , DISTTAR_EXCLUDEDIRS=['CVS','.svn','.sconf_temp', 'dist']
1388 )
1389
1390 tar = env.DistTar("dist/"+env['DISTTAR_NAME']
1391 , [env.Dir('#')]
1392 )
1393
1394 env.Depends(tar,'ascend.spec')
1395
1396 #------------------------------------------------------
1397 # RPM BUILD
1398
1399 #if platform.system()=="Linux":
1400 # pass
1401
1402 #------------------------------------------------------
1403 # DEFAULT TARGETS
1404
1405 default_targets =['libascend']
1406 if with_tcltk:
1407 default_targets.append('tcltk')
1408 if with_python:
1409 default_targets.append('pygtk')
1410 if with_installer:
1411 default_targets.append('installer')
1412
1413 env.Default(default_targets)
1414
1415 print "Building targets:"," ".join([str(i) for i in BUILD_TARGETS])
1416
1417 # vim: set syntax=python:
1418

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