Commit d91a37ac681741634b01d8b2a616c490e3ffd73e

Authored by Jürgen Knödlseder
1 parent 3930d753

Add ctool code generation script

Showing 1 changed file with 393 additions and 0 deletions
dev/codegen.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# ctools code generator
  4 +#
  5 +# Copyright (C) 2018 Juergen Knoedlseder
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import os
  22 +import sys
  23 +import glob
  24 +import commands
  25 +import fileinput
  26 +from datetime import date
  27 +import gammalib
  28 +
  29 +
  30 +# ================= #
  31 +# Confirm something #
  32 +# ================= #
  33 +def confirm(text):
  34 + """
  35 + Confirm something
  36 +
  37 + Parameters
  38 + ----------
  39 + text : str
  40 + Thing to confirm
  41 +
  42 + Returns
  43 + -------
  44 + flag : bool
  45 + True if something is confirmed
  46 + """
  47 + # Confirmation loop
  48 + waiting = True
  49 + while waiting:
  50 + confirmation = str(raw_input(text+' (y/n): '))
  51 + if confirmation == 'q':
  52 + sys.exit()
  53 + elif confirmation == 'y':
  54 + flag = True
  55 + waiting = False
  56 + elif confirmation == 'n':
  57 + flag = False
  58 + waiting = False
  59 +
  60 + # Return confirmation flag
  61 + return flag
  62 +
  63 +
  64 +# ============ #
  65 +# Get response #
  66 +# ============ #
  67 +def response(text, confirm_response=False):
  68 + """
  69 + Get response
  70 +
  71 + Parameters
  72 + ----------
  73 + text : str
  74 + Thing we want to get a response to
  75 + confirm_response : bool
  76 + True if response should be confirmed
  77 +
  78 + Returns
  79 + -------
  80 + response : str
  81 + Reponse
  82 + """
  83 + # Looping until response is accepted
  84 + looping = True
  85 + while looping:
  86 +
  87 + # Get response from input
  88 + waiting = True
  89 + while waiting:
  90 + response = str(raw_input('%s: ' % text))
  91 + if response == 'q':
  92 + sys.exit()
  93 + else:
  94 + waiting = False
  95 +
  96 + # If the version number change is confirmed then do the change
  97 + if confirm_response:
  98 + if confirm('Is "%s" ok?' % response):
  99 + looping = False
  100 + else:
  101 + looping = False
  102 +
  103 + # Return response
  104 + return response
  105 +
  106 +
  107 +# ======== #
  108 +# Add file #
  109 +# ======== #
  110 +def add_file(infile, outfile, tokens):
  111 + """
  112 + Add a file, replacing all tokens
  113 +
  114 + Parameters
  115 + ----------
  116 + infile : str
  117 + Input filename
  118 + outfile : str
  119 + Output filename
  120 + tokens : list of dict
  121 + Token list
  122 + """
  123 + # Create output file
  124 + file = open(outfile, "w")
  125 +
  126 + # Initialise number of header lines
  127 + n_header = 0
  128 +
  129 + # Loop over input file
  130 + for line in open(infile, 'r'):
  131 +
  132 + # Signal if this is a header line
  133 + is_header_ast = False
  134 + is_header_hash = False
  135 + if len(line) > 75:
  136 + is_header_ast = line[1] == '*' and line[75] == '*'
  137 + is_header_hash = line[0] == '#' and line[1] == ' ' and line[76] == '#'
  138 + n_header += 1
  139 +
  140 + # Replace tokens in line
  141 + for token in tokens:
  142 + line = line.replace(token['pattern'], token['string'])
  143 +
  144 + # If we have a header line than format the line
  145 + if is_header_ast:
  146 + text = line.strip(' *\n')
  147 + if n_header == 2:
  148 + line = ' *'+gammalib.centre(text,73)+'*\n'
  149 + elif n_header == 4:
  150 + line = ' *'+gammalib.left(' '+text,73)+'*\n'
  151 + if is_header_hash:
  152 + text = line.strip(' #\n')
  153 + line = '# '+gammalib.left(text,74)+'#\n'
  154 +
  155 + # Write out line
  156 + file.write(line)
  157 +
  158 + # Close file
  159 + file.close()
  160 +
  161 + # Return
  162 + return
  163 +
  164 +
  165 +# ========== #
  166 +# Set tokens #
  167 +# ========== #
  168 +def set_tokens(classname, author, what):
  169 + """
  170 + Set replacement tokens for a ctool or cscript
  171 + """
  172 + # Get current year
  173 + year = str(date.today().year)
  174 +
  175 + # Set tokens
  176 + tokens = [{'pattern': 'xxx', 'string': classname},
  177 + {'pattern': 'XXX', 'string': classname.upper()},
  178 + {'pattern': '[WHAT]', 'string': what},
  179 + {'pattern': '[what]', 'string': what.lower()},
  180 + {'pattern': '[AUTHOR]', 'string': author},
  181 + {'pattern': '[YEAR]', 'string': year}]
  182 +
  183 + # Return tokens
  184 + return tokens
  185 +
  186 +
  187 +# ========= #
  188 +# Add ctool #
  189 +# ========= #
  190 +def add_ctool(name, tokens, baseclass):
  191 + """
  192 + Add an instrument class
  193 +
  194 + Parameters
  195 + ----------
  196 + name : str
  197 + ctool or script name
  198 + tokens : str
  199 + Tokens for replacement
  200 + baseclass : str
  201 + Baseclass
  202 + """
  203 + # Create directory structure
  204 + if not os.path.isdir('src/%s' % name):
  205 + os.mkdir('src/%s' % name)
  206 +
  207 + # Set template file names
  208 + inctemp = 'src/template/ctool_%s.hpp' % (baseclass)
  209 + srctemp = 'src/template/ctool_%s.cpp' % (baseclass)
  210 + partemp = 'src/template/ctool_%s.par' % (baseclass)
  211 + maintemp = 'src/template/main_%s.cpp' % (baseclass)
  212 + maketemp = 'src/template/ctool_Makefile.am'
  213 + pytemp = 'src/template/ctool_%s.i' % (baseclass)
  214 + testtemp = 'src/template/ctool_test.py'
  215 +
  216 + # Set destination file names
  217 + incfile = 'src/%s/%s.hpp' % (name, name)
  218 + srcfile = 'src/%s/%s.cpp' % (name, name)
  219 + parfile = 'src/%s/%s.par' % (name, name)
  220 + mainfile = 'src/%s/main.cpp' % (name)
  221 + makefile = 'src/%s/Makefile.am' % (name)
  222 + pyfile = 'pyext/%s.i' % (name)
  223 + testfile = 'test/test_%s.py' % (name)
  224 +
  225 + # Add files
  226 + add_file(inctemp, incfile, tokens)
  227 + add_file(srctemp, srcfile, tokens)
  228 + add_file(partemp, parfile, tokens)
  229 + add_file(maintemp, mainfile, tokens)
  230 + add_file(maketemp, makefile, tokens)
  231 + add_file(pytemp, pyfile, tokens)
  232 + add_file(testtemp, testfile, tokens)
  233 +
  234 + # Update src/Makefile.am
  235 + filename = 'src/Makefile.am'
  236 + insertline1 = ' %s ' % (name)
  237 + insertline2 = ' %s/lib%s.la ' % (name, name)
  238 + for line in fileinput.FileInput(filename,inplace=1):
  239 + if 'SUBDIRS' in line.replace(' ', ''):
  240 + last = line[-2]
  241 + line = line.replace(line, line+insertline1+last+'\n')
  242 + if 'libctools_la_LIBADD' in line.replace(' ', ''):
  243 + last = line[-2]
  244 + line = line.replace(line, line+insertline2+last+'\n')
  245 + print line,
  246 +
  247 + # Update Python module
  248 + for line in fileinput.FileInput('pyext/ctools/tools.i',inplace=1):
  249 + if '%include "ctbin.i"' in line:
  250 + print('%%include "%s.i"' % (name))
  251 + print line,
  252 +
  253 + # Update unit test
  254 + for line in fileinput.FileInput('test/test_python_ctools.py',inplace=1):
  255 + if 'import test_ctbin' in line:
  256 + print('import test_%s' % (name))
  257 + elif 'test_ctbin.Test()' in line:
  258 + print(' test_%s.Test(),' % (name))
  259 + print line,
  260 +
  261 + # Update export of unit test
  262 + for line in fileinput.FileInput('pyext/Makefile.am',inplace=1):
  263 + if '$(top_srcdir)/test/test_ctbin.py' in line:
  264 + print(' $(top_srcdir)/test/test_%s.py \\' % (name))
  265 + print line,
  266 +
  267 + # Update configure.ac
  268 + for line in fileinput.FileInput('configure.ac',inplace=1):
  269 + if 'src/ctbin/Makefile' in line:
  270 + print(' src/%s/Makefile' % (name))
  271 + print line,
  272 +
  273 + # Return
  274 + return
  275 +
  276 +
  277 +# ============ #
  278 +# Manage ctool #
  279 +# ============ #
  280 +def ctool_menu():
  281 + """
  282 + Manage ctool
  283 + """
  284 + # Annonce actions
  285 + print("")
  286 + print("Add ctool")
  287 + print("---------")
  288 +
  289 + # Stay in loop until there is a final confirmation
  290 + while True:
  291 +
  292 + # Enter information
  293 + ctoolname = response('Please enter a ctool name (e.g. "ctpntsim")')
  294 + what = response('Please say what the tool is for (e.g. "Pointing simulation")')
  295 + author = response('Please enter your name (e.g. "Joe Public")')
  296 +
  297 + # Enter baseclass
  298 + print('From which baseclass should the ctool derive?')
  299 + print('[1] ctool')
  300 + print('[2] ctobservation')
  301 + print('[3] ctlikelihood')
  302 + waiting = True
  303 + choice = 1
  304 + while waiting:
  305 + choice = str(raw_input('Enter your choice: '))
  306 + if choice == '1' or choice == '2' or choice == '3':
  307 + waiting = False
  308 + if choice == '1':
  309 + basename = 'base'
  310 + baseclass = 'ctool'
  311 + elif choice == '2':
  312 + basename = 'obs'
  313 + baseclass = 'ctobservation'
  314 + elif choice == '3':
  315 + basename = 'like'
  316 + baseclass = 'ctlikelihood'
  317 +
  318 + # Ask to confirm module summary
  319 + print('\nAll right. Have now:')
  320 + print('ctool name ......: "%s"' % ctoolname)
  321 + print('Tools descriptor : "%s"' % what)
  322 + print('Your name .......: "%s"' % author)
  323 + print('Base class ......: "%s"' % baseclass)
  324 + if confirm('Is this correct?'):
  325 + break
  326 +
  327 + # Set tokens
  328 + tokens = set_tokens(ctoolname, author, what)
  329 +
  330 + # Add ctool
  331 + add_ctool(ctoolname, tokens, basename)
  332 +
  333 + # Return
  334 + return
  335 +
  336 +
  337 +# ================ #
  338 +# Manage main menu #
  339 +# ================ #
  340 +def main_menu():
  341 + """
  342 + Manage main menu
  343 + """
  344 + # Print main menu
  345 + print('[1] Add ctool')
  346 + print('[2] Add cscript')
  347 + print('[q] Quit')
  348 +
  349 + # Wait for the input
  350 + waiting = True
  351 + while waiting:
  352 + choice = str(raw_input('Enter your choice: '))
  353 + if choice == '1' or choice == '2' or choice == 'q':
  354 + waiting = False
  355 +
  356 + # Return choice
  357 + return choice
  358 +
  359 +
  360 +# ======================== #
  361 +# Main routine entry point #
  362 +# ======================== #
  363 +if __name__ == '__main__':
  364 +
  365 + # Check if script has been started from ctools root directory
  366 + if not os.path.isfile('ctools.pc.in'):
  367 + print('"codgen.py" script needs to be started from ctools source code '
  368 + 'root directory, you are somewhere else. Quit now.')
  369 + sys.exit()
  370 +
  371 + # Clear console
  372 + os.system('clear')
  373 +
  374 + # Print header
  375 + print('ctools code generator')
  376 + print('=====================')
  377 + print('')
  378 +
  379 + # Enter endless loop
  380 + while True:
  381 +
  382 + # Show main menu
  383 + choice = main_menu()
  384 +
  385 + # Dispatch according to choice
  386 + if choice == '1':
  387 + ctool_menu()
  388 + print('')
  389 + elif choice == '2':
  390 + cscript_menu()
  391 + print('')
  392 + elif choice == 'q':
  393 + break
... ...