From 44db81d3543b6dd584d7f6fa9171e4349ecb9259 Mon Sep 17 00:00:00 2001 From: Tim Jenness Date: Tue, 28 Oct 2014 09:06:11 -0700 Subject: [PATCH] hds: Two helper python scripts 1. Generate a versioned include file from a standard hds.h include file. This can be used by the v4 and v5 HDS libraries to allow them to publicize a version specific API. 2. Script to generate hds_select.c which provides basic version selection code to switch between APIs based on the locator. --- libraries/hds/helper/mkhdsversions.py | 65 ++++++++++ libraries/hds/helper/mkhdswrapper.py | 173 ++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 libraries/hds/helper/mkhdsversions.py create mode 100644 libraries/hds/helper/mkhdswrapper.py diff --git a/libraries/hds/helper/mkhdsversions.py b/libraries/hds/helper/mkhdsversions.py new file mode 100644 index 00000000000..e7e538f959c --- /dev/null +++ b/libraries/hds/helper/mkhdsversions.py @@ -0,0 +1,65 @@ +#!python + +""" +Given the HDS include file, generate a new include file +for either v4 or v5 API. We can not do lazy evaluation of +macros as we want v5 and v4 and the public API to exist +in code at the same time. + +We need to be able to + + #include "hds.h" + #include "hds_v5.h" + #include "hds_v4.h" + +and get all the APIs defined: datPublic, datPublic_v4 +and datPublic_v4 with no redefinitions. + +Internally in the v5 and v4 code, we want to be able to +build the entire HDS standalone with the public API and +build it with the versioned API (we do not want to have +to go through the old HDS code and change all the +datName usage to datName_v4) when used as a wrapper. + +This means that hds.h in v4 has to be able to load declaring +public or versioned API. + +This routine therefore generates + + - standalone copy of hds.h that can be included by the wrapper (hds_vx.h) + - include file of redefinitions that can be included in + internal build of wrapped library. (hds_vx_map.h) + +""" + +import codecs +import re + +# Pattern match to find a function +hfunc_re = re.compile(r"^((dat|hds)[A-Z][A-Za-z0-9]+)\(") + +# First define the versioned API +for ver in ["_v4", "_v5"]: + outfile = "hds" + ver + ".h" + outfh = codecs.open( outfile,"w", "utf8") + for line in codecs.open("hds.h", "r", "utf8"): + line = line.strip() + func_match = hfunc_re.search(line) + if func_match: + line = line.replace("(", ver+"(") + elif line.find("STAR_HDS_H") != -1: + line = line.replace("STAR_HDS_H", "STAR_HDS"+ver.upper()+"_H") + outfh.write(line+"\n") + outfh.close() + +# Now the translation file +for ver in ["_v4", "_v5"]: + outfile = "hds" + ver + "_map.h" + outfh = codecs.open( outfile,"w", "utf8") + for line in codecs.open("hds.h", "r", "utf8"): + line = line.strip() + func_match = hfunc_re.search(line) + if func_match: + hds_function = func_match.group(1) + outfh.write("#define {0} {0}{1}\n".format(hds_function, ver)) + outfh.close() diff --git a/libraries/hds/helper/mkhdswrapper.py b/libraries/hds/helper/mkhdswrapper.py new file mode 100644 index 00000000000..fc5eb541263 --- /dev/null +++ b/libraries/hds/helper/mkhdswrapper.py @@ -0,0 +1,173 @@ +#!python + +""" +Given the HDS include file, generate a wrapper that calls +the v4 or v5 APIs depending on the type of the input +locator. + +Reads through hds.h and for every API definition, generates +a wrapper routine. + +Notes: + +- datCcopy is only routine that takes two locators and returns a third. +- Routines taking two input locators: + datCopy, datMove + +All other routines take one locator and may or may not return a +locator. That locator will be of the correct type. + +The copy/move routines will need special code to be able to handle +locators from different implementations. + +Routines that don't take a locator at all: + datCctyp - call v5 + datChscn - call v5 + datErmsg - Use wrapper implementation that uses largest error table (default to v5) + hdsEwild - Wrapper implementation + hdsFlush - Call both (only error if both fail) + hdsGtune - call both (only error if both fail or both different) + hdsShow - call one or both depending on whether we have used any v5 or v4 locators. + hdsState - call both (error if different) + hdsStop - call both + hdsTune - call both routines + +Routines that open a file: + + hdsOpen - Try v5 and if that fails try v4 + hdsWild - presumably will use hdsOpen internally. + This routine has to be implemented in the wrapper + so that the wrapped hdsOpen can be called. + +Creating a file: + + hdsNew - always creates v5 unless environment variable + indicates to only open v4. + datTemp - uses same logic as for hdsNew + +Ideally v5 files would have a different file ending to v4 +but that will require lots of code changes in other packages +that are assuming just one file extension. + +Will not be run repeatedly as there will eventually be special +code for these notable routines. File is retained for historical +interest. + +""" + +import re + +# Pattern match to find a function +hfunc_re = re.compile(r"^((dat|hds)[A-Z][A-Za-z0-9]+)\(") + +def version_names(line): + v4 = line.replace("(", "_v4(") + v5 = line.replace("(", "_v5(") + return (v4,v5) + +# Code for the different type of functions +def func_simple(line): + (v4,v5) = version_names(line) + # are we dealing with locator or locator1? + locvar = "locator" + if line.find("locator1") >= 0: + locvar = "locator1" + elif line.startswith("datAnnul") or line.startswith("datPrmry") or line.startswith("hdsErase") or line.startswith("hdsClose"): + locvar = "*locator" + print(""" + if (ISHDSv5({0})) return {1} + return {2} +""".format(locvar, v5, v4)) + + +def func_special(line): + print(" /* Requires special code */") + print(' printf("Aborting. Special code required in: %s\\n", "{0}");'.format(line)) + print(" abort();") + if line.find("status") > -1: + print(" return *status;") + +def func_both(line): + v4 = line.replace("(", "_v4(") + print(" "+v4) + v5 = line.replace("(", "_v5(") + print(" return "+v5) + +def func_v5(line): + v5 = line.replace("(", "_v5(") + print(" return "+v5) + + +# Dictionary indicating special cases +special = dict({ + "datCcopy": "special", + "datCctyp": "v5", + "datChscn": "v5", + "datCopy": "special", + "datErmsg": "v5", + "datMove": "special", + "datTemp": "v5", + "hdsEwild": "special", + "hdsFlush": "both", + "hdsGtune": "both", + "hdsNew": "v5", + "hdsOpen": "both", + "hdsShow": "special", + "hdsState": "both", + "hdsStop": "both", + "hdsTune": "both", + "hdsWild": "special" +}) + +inserted_includes = 0 +for line in open("hds.h"): + line = line.strip() + func_match = hfunc_re.search(line) + if func_match: + hds_function = func_match.group(1) + print( line[:-1] + " {") # Without the semi-colon + # Now we have to convert the prototype to a function call + # ie datXxx( type1 var1, type2 var2); to datXxx(var1,var2); + openparen = line.find("(") + closeparen = line.find(")") + argsin = line[openparen:closeparen].split(",") + argsout = [] + for a in argsin: + # Get rid of array [] specifiers + arraypos = a.find("[") + if arraypos > -1: + a = a[:arraypos] + parts = a.split() + varname = parts[-1] + # Remember to drop pointer derefs + argsout.append( varname.replace("*", "") ) + # put the line back together + line = hds_function + "(" + ", ".join(argsout) + ");" + if hds_function in special: + mode = special[hds_function] + if mode == "both": + func_both(line) + elif mode == "special": + func_special(line) + elif mode == "v5": + func_v5(line) + else: + raise ValueError("Unrecognized mode for function {0}".format(hds_function)) + else: + func_simple(line) + print("}") + else: + if not inserted_includes and line.startswith("/*=="): + print('#include ') # For abort() + print('#include ') # For printf() + print('#include "hds.h"') + print('#include "star/hds_v4.h"') + print('#include "star/hds_v5.h"') + print('#define ISHDSv5(loc) ((loc) && (loc)->hds_version >= 5)') + print("") + inserted_includes=1 + elif line.find("dat_par.h") != -1: + print('#include "hds1_types.h"') + print("#define HDS_INTERNAL_INCLUDES 1") + print(line) +