From 851eaf7040bdf421bc9e982503816e6ca06c7945 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Wed, 14 Dec 2011 03:53:39 +0000 Subject: [PATCH] SCons generates and installs locally customized utility scripts --- SConstruct | 11 ++- buildutils.py | 1 + subst.py | 207 +++++++++++++++++++++++++++++++++++++++++ tools/SConscript | 35 +++++++ tools/cantera_demos.m | 4 + tools/ctnew.in | 9 ++ tools/ctpath.m.in | 4 + tools/mixmaster.in | 4 + tools/setup_cantera.in | 47 ++++++++++ 9 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 subst.py create mode 100644 tools/cantera_demos.m create mode 100644 tools/ctnew.in create mode 100644 tools/ctpath.m.in create mode 100644 tools/mixmaster.in create mode 100644 tools/setup_cantera.in diff --git a/SConstruct b/SConstruct index 63c613caa..081a9152a 100644 --- a/SConstruct +++ b/SConstruct @@ -1,8 +1,10 @@ from buildutils import * +import subst import platform, sys, os -env = Environment() +env = Environment(tools = ['default', 'textfile']) env.AddMethod(RecursiveInstall) +subst.TOOL_SUBST(env) # ****************************************************** # *** Set system-dependent defaults for some options *** @@ -102,7 +104,7 @@ if env['python_package'] in ('full', 'default'): 'numpy', ('numpy', 'numarray', 'numeric')), PathVariable('python_array_home', 'Location for array package (e.g. if installed with --home)', - None, PathVariable.PathAccept), + '', PathVariable.PathAccept), PathVariable('cantera_python_home', 'where to install the python package', pyprefix, PathVariable.PathAccept) ) @@ -254,6 +256,7 @@ env['ct_tutdir'] = pjoin(env['prefix'], 'tutorials') env['ct_docdir'] = pjoin(env['prefix'], 'doc') env['ct_dir'] = env['prefix'] env['ct_mandir'] = pjoin(env['prefix'], 'man1') +env['ct_matlab_dir'] = pjoin(env['prefix'], 'matlab', 'toolbox') # ********************* # *** Build Cantera *** @@ -343,6 +346,10 @@ SConscript('build/tools/SConscript') inst = env.Install('$ct_datadir', mglob(env, pjoin('data','inputs'), 'cti', 'xml')) installTargets.extend(inst) +# Install exp3to2.sh (used by some of the tests) +inst = env.Install('$ct_bindir', pjoin('bin', 'exp3to2.sh')) +installTargets.extend(inst) + ### Meta-targets ### build_cantera = Alias('build', buildTargets) install_cantera = Alias('install', installTargets) diff --git a/buildutils.py b/buildutils.py index b2150020e..8b439ed0c 100644 --- a/buildutils.py +++ b/buildutils.py @@ -1,6 +1,7 @@ from glob import glob import os import shutil +import sys from os.path import join as pjoin class DefineDict(object): diff --git a/subst.py b/subst.py new file mode 100644 index 000000000..136e9f51d --- /dev/null +++ b/subst.py @@ -0,0 +1,207 @@ +# File: subst.py +# Author: Brian A. Vanderburg II +# Purpose: A generic SCons file substitution mechanism +# Copyright: This file is placed in the public domain. +# URL: http://www.scons.org/wiki/GenericSubstBuilder +############################################################################## + + +# Requirements +############################################################################## +import re + +from SCons.Script import * +import SCons.Errors + + +# Helper/core functions +############################################################################## + +# Do the substitution +def _subst_file(target, source, env, pattern, replace): + # Read file + f = open(source, "rU") + try: + contents = f.read() + finally: + f.close() + + # Substitute, make sure result is a string + def subfn(mo): + value = replace(env, mo) + if not SCons.Util.is_String(value): + raise SCons.Errors.UserError("Substitution must be a string.") + return value + + contents = re.sub(pattern, subfn, contents) + + # Write file + f = open(target, "wt") + try: + f.write(contents) + finally: + f.close() + +# Determine which keys are used +def _subst_keys(source, pattern): + # Read file + f = open(source, "rU") + try: + contents = f.read() + finally: + f.close() + + # Determine keys + keys = [] + def subfn(mo): + key = mo.group("key") + if key: + keys.append(key) + return '' + + re.sub(pattern, subfn, contents) + + return keys + +# Get the value of a key as a string, or None if it is not in the environment +def _subst_value(env, key): + # Why does "if key in env" result in "KeyError: 0:"? + try: + env[key] + except KeyError: + return None + + # env.subst already returns a string even if it is stored as a number + # such as env['HAVE_XYZ'] = 1 + return env.subst("${%s}" % key) + + +# Builder related functions +############################################################################## + +# Builder action +def _subst_action(target, source, env): + # Substitute in the files + pattern = env["SUBST_PATTERN"] + replace = env["SUBST_REPLACE"] + + for (t, s) in zip(target, source): + _subst_file(str(t), str(s), env, pattern, replace) + + return 0 + +# Builder message +def _subst_message(target, source, env): + items = ["Substituting vars from %s to %s" % (s, t) + for (t, s) in zip(target, source)] + + return "\n".join(items) + +# Builder dependency emitter +def _subst_emitter(target, source, env): + pattern = env["SUBST_PATTERN"] + for (t, s) in zip(target, source): + # When building, if a variant directory is used and source files + # are being duplicated, the source file will not be duplicated yet + # when this is called, so the source node must be used instead of + # the duplicated node + path = s.srcnode().abspath + + # Get keys used + keys = _subst_keys(path, pattern) + + d = dict() + for key in keys: + value = _subst_value(env, key) + if not value is None: + d[key] = value + + # Only the current target depends on this dictionary + Depends(t, SCons.Node.Python.Value(d)) + + return target, source + + +# Replace @key@ with the value of that key, and @@ with a single @ +############################################################################## + +_SubstFile_pattern = "@(?P\w*?)@" +def _SubstFile_replace(env, mo): + key = mo.group("key") + if not key: + return "@" + + value = _subst_value(env, key) + if value is None: + raise SCons.Errors.UserError("Error: key %s does not exist" % key) + return value + +def SubstFile(env, target, source): + return env.SubstGeneric(target, + source, + SUBST_PATTERN=_SubstFile_pattern, + SUBST_REPLACE=_SubstFile_replace) + + +# A substitutor similar to config.h header substitution +# Supported patterns are: +# +# Pattern: #define @key@ +# Found: #define key value +# Missing: /* #define key */ +# +# Pattern: #define @key@ default +# Found: #define key value +# Missing: #define key default +# +# Pattern: #undef @key@ +# Found: #define key value +# Missing: #undef key +# +# The "@" is used to that these defines can be used in addition to +# other defines that you do not desire to be replaced. +############################################################################## + +_SubstHeader_pattern = "(?m)^(?P\\s*?)(?P#define|#undef)\\s+?@(?P\w+?)@(?P.*?)$" +def _SubstHeader_replace(env, mo): + space = mo.group("space") + type = mo.group("type") + key = mo.group("key") + ending = mo.group("ending") + + value = _subst_value(env, key) + if not value is None: + # If found it is always #define key value + return "%s#define %s %s" % (space, key, value) + + # Not found + if type == "#define": + defval = ending.strip() + if defval: + # There is a default value + return "%s#define %s %s" % (space, key, defval) + else: + # There is no default value + return "%s/* #define %s */" % (space, key) + + # It was #undef + return "%s#undef %s" % (space, key) + +def SubstHeader(env, target, source): + return env.SubstGeneric(target, + source, + SUBST_PATTERN=_SubstHeader_pattern, + SUBST_REPLACE=_SubstHeader_replace) + + +# Create builders +############################################################################## +def TOOL_SUBST(env): + # The generic builder + subst = SCons.Action.Action(_subst_action, _subst_message) + env['BUILDERS']['SubstGeneric'] = Builder(action=subst, + emitter=_subst_emitter) + + # Additional ones + env.AddMethod(SubstFile) + env.AddMethod(SubstHeader) diff --git a/tools/SConscript b/tools/SConscript index 9d2df2e25..27fc4bdce 100644 --- a/tools/SConscript +++ b/tools/SConscript @@ -28,3 +28,38 @@ installTargets.extend( # Copy man pages inst = localenv.Install('$ct_mandir', mglob(localenv, 'man', '*')) installTargets.extend(inst) + +### Generate customized scripts ### + +# 'setup_cantera' +v = sys.version_info +localenv['python_module_loc'] = pjoin(localenv['prefix'], 'lib', 'python%i.%i' % v[:2], 'site-packages') +target = localenv.SubstFile('setup_cantera', 'setup_cantera.in') +buildTargets.extend(target) +inst = localenv.Install('$ct_bindir','setup_cantera') +installTargets.append(inst) + +# 'mixmaster' +if env['BUILD_PYTHON'] >= 2: + target = localenv.SubstFile('mixmaster', 'mixmaster.in') + buildTargets.extend(target) + inst = localenv.Install('$ct_bindir', 'mixmaster') + localenv.AddPostAction(inst, Chmod('$TARGET', 0755)) + installTargets.extend(inst) + +# 'ctnew' +target = localenv.SubstFile('ctnew', 'ctnew.in') +buildTargets.extend(target) +inst = localenv.Install('$ct_bindir', 'ctnew') +localenv.AddPostAction(inst, Chmod('$TARGET', 0755)) +installTargets.extend(inst) + +# 'ctpath.m', 'cantera_demos.m' +if env['matlab_toolbox'] == 'y': + target = localenv.SubstFile('ctpath.m', 'ctpath.m.in') + buildTargets.extend(target) + inst = localenv.Install(pjoin('$prefix','matlab'), target) + installTargets.extend(inst) + + inst = localenv.Install(pjoin('$prefix','matlab'), 'cantera_demos.m') + installTargets.extend(inst) diff --git a/tools/cantera_demos.m b/tools/cantera_demos.m new file mode 100644 index 000000000..749688a97 --- /dev/null +++ b/tools/cantera_demos.m @@ -0,0 +1,4 @@ +ctpath; +cd ../demos/matlab; +run_examples; + diff --git a/tools/ctnew.in b/tools/ctnew.in new file mode 100644 index 000000000..de2be4a17 --- /dev/null +++ b/tools/ctnew.in @@ -0,0 +1,9 @@ +#!/bin/sh + +if test "x$1" = "x-f77"; then + cp @ct_templdir@/f77/*.* . +elif test "x$1" = "x-f90"; then + cp @ct_templdir@/f90/*.* . +else + cp @ct_templdir@/cxx/*.* . +fi diff --git a/tools/ctpath.m.in b/tools/ctpath.m.in new file mode 100644 index 000000000..a8c213f25 --- /dev/null +++ b/tools/ctpath.m.in @@ -0,0 +1,4 @@ +path('@ct_matlab_dir@', path) +path('@ct_matlab_dir@/1D', path) +setenv('PYTHON_CMD', '@python_cmd@') +setenv('PYTHONPATH', [getenv('PYTHONPATH'), ':@python_module_loc@']) diff --git a/tools/mixmaster.in b/tools/mixmaster.in new file mode 100644 index 000000000..ab6e5ab69 --- /dev/null +++ b/tools/mixmaster.in @@ -0,0 +1,4 @@ +#!@python_cmd@ + +from MixMaster import MixMaster +MixMaster() diff --git a/tools/setup_cantera.in b/tools/setup_cantera.in new file mode 100644 index 000000000..a87689a80 --- /dev/null +++ b/tools/setup_cantera.in @@ -0,0 +1,47 @@ +#!/bin/sh + +if [ -z $LD_LIBRARY_PATH ]; then + LD_LIBRARY_PATH=@ct_libdir@ +else + LD_LIBRARY_PATH=@ct_libdir@:$LD_LIBRARY_PATH +fi +export LD_LIBRARY_PATH + +PYTHON_CMD=@python_cmd@ +export PYTHON_CMD + +PATH=@ct_bindir@:$PATH +export PATH + +if [ "@python_cmd@" != `which python` ]; then + alias ctpython=@python_cmd@ +fi + +if [ "@matlab_toolbox@" = "y" ]; then + if [ -z $MATLAB_PATH ]; then + MATLABPATH=@ct_matlab_dir@:@ct_matlab_dir@/1D + else + MATLABPATH=$MATLABPATH:@ct_matlab_dir@:@ct_matlab_dir@/1D + fi + export MATLABPATH +fi + +if [ -z $PYTHONPATH ]; then + PYTHONPATH=@python_module_loc@ +else + PYTHONPATH=@python_module_loc@:$PYTHONPATH +fi + +if [ "@python_array_home@" != "" ]; then + PYTHONPATH=@python_array_home@:$PYTHONPATH +fi + +export PYTHONPATH + +## Uncomment this if you want to specify the tmp dir location where +## Cantera writes temporary files. The default is: +## (1) getenv("TMP") +## (2) getenv("TEMP") +## (3) "." +# CANTERA_TMPDIR=/fill/in +# export CANTERA_TMPDIR