Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:13:09

0001 #!/usr/bin/env python3
0002 #-*- coding: utf-8 -*-
0003 #pylint: disable-msg=W0122,R0914,R0912
0004 
0005 """
0006 File       : pkg.py
0007 Author     : Valentin Kuznetsov <vkuznet@gmail.com>
0008 Description: AbstractGenerator class provides basic functionality
0009 to generate CMSSW class from given template
0010 """
0011 from __future__ import print_function
0012 
0013 # system modules
0014 import os
0015 import sys
0016 import time
0017 import pprint
0018 
0019 # package modules
0020 from FWCore.Skeletons.utils import parse_word, functor, user_info, tree, template_directory
0021 
0022 class AbstractPkg(object):
0023     """
0024     AbstractPkg takes care how to generate code from template/PKG
0025     package area. The PKG can be any directory which may include
0026     any types of files, e.g. C++ (.cc), python (.py), etc.
0027     This class relies on specific logic which we outline here:
0028 
0029         - each template may use tags defined with double underscores
0030           enclosure, e.g. __class__, __record__, etc.
0031         - each template may have example tags, such tags should
0032           start with @example_. While processing template user may
0033           choose to strip them off or keep the code behind those tags
0034         - in addition user may specify pure python code which can
0035           operate with user defined tags. This code snipped should
0036           be enclosed with #python_begin and #python_end lines
0037           which declares start and end of python block
0038     """
0039     def __init__(self, config=None):
0040         super(AbstractPkg, self).__init__()
0041         if  not config:
0042             self.config = {}
0043         else:
0044             self.config = config
0045         self.pname  = self.config.get('pname', None)
0046         self.tmpl   = self.config.get('tmpl', None)
0047         self.debug  = self.config.get('debug', 0)
0048         self.tdir   = template_directory()
0049         self.author = user_info(self.config.get('author', None))
0050         self.date   = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
0051         self.not_in_dir = self.config.get('not_in_dir', [])
0052         self.working_dir = self.config.get('working_dir')
0053 
0054     def tmpl_etags(self):
0055         "Scan template files and return example tags"
0056         keys = []
0057         sdir = '%s/%s' % (self.tdir, self.tmpl)
0058         for name in os.listdir(sdir):
0059             if  name[-1] == '~':
0060                 continue
0061             if  name == 'CVS':
0062                 continue
0063             fname = os.path.join(sdir, name)
0064             with open(fname, 'r') as stream:
0065                 for line in stream.readlines():
0066                     if  line.find('@example_') != -1: # possible tag
0067                         keys += [k for k in line.split() if \
0068                                     k.find('@example_') != -1]
0069         return set(keys)
0070 
0071     def print_etags(self):
0072         "Print out template example tags"
0073         for key in self.tmpl_etags():
0074             print(key)
0075 
0076     def tmpl_tags(self):
0077         "Scan template files and return template tags"
0078         keys = []
0079         sdir = '%s/%s' % (self.tdir, self.tmpl)
0080         for name in os.listdir(sdir):
0081             if  name[-1] == '~':
0082                 continue
0083             if  name == 'CVS':
0084                 continue
0085             fname = os.path.join(sdir, name)
0086             with open(fname, 'r') as stream:
0087                 for line in stream.readlines():
0088                     if  line.find('__') != -1: # possible key
0089                         keys += [k for k in parse_word(line)]
0090         return set(keys)
0091 
0092     def print_tags(self):
0093         "Print out template keys"
0094         for key in self.tmpl_tags():
0095             print(key)
0096 
0097     def parse_etags(self, line):
0098         """
0099         Determine either skip or keep given line based on class tags
0100         meta-strings
0101         """
0102         tmpl_etags = self.tmpl_etags()
0103         keep_etags = self.config.get('tmpl_etags', [])
0104         for tag in tmpl_etags:
0105             for valid_tag in keep_etags:
0106               if  line.find(valid_tag) != -1:
0107                 line = line.replace(valid_tag, '')
0108                 return line
0109             if  line.find(tag) != -1:
0110               line = ''
0111               return line
0112         if len(keep_etags) == 0:
0113             return line.replace('@default', '')
0114         if '@default' in line:
0115             return ''
0116         return line
0117 
0118     def write(self, fname, tmpl_name, kwds):
0119         "Create new file from given template name and set of arguments"
0120         code = ""
0121         read_code = False
0122         if os.path.exists(fname):
0123             return
0124         with open(fname, 'w') as stream:
0125             for line in open(tmpl_name, 'r').readlines():
0126                 line = self.parse_etags(line)
0127                 if  not line:
0128                     continue
0129                 if  line.find('#python_begin') != -1:
0130                     read_code = True
0131                     continue
0132                 if  line.find('#python_end') != -1:
0133                     read_code = False
0134                 if  read_code:
0135                     code += line
0136                 if  code and not read_code:
0137                     res   = functor(code, kwds, self.debug)
0138                     stream.write(res)
0139                     code  = ""
0140                     continue
0141                 if  not read_code:
0142                     for key, val in kwds.items():
0143                         if  isinstance(val, str):
0144                             line = line.replace(key, val)
0145                     stream.write(line)
0146 
0147     def get_kwds(self):
0148         "Return keyword arguments to be used in methods"
0149         kwds  = {'__pkgname__': self.config.get('pkgname', 'Package'),
0150                  '__author__': self.author,
0151                  '__date__': self.date,
0152                  '__class__': self.pname,
0153                  '__class_lowercase__': self.pname.lower(),
0154                  '__class_space__': " "*len(self.pname),
0155                  '__name__': self.pname,
0156                  '__subsys__': self.config.get('subsystem', 'Subsystem')}
0157         args = self.config.get('args', None)
0158         kwds.update(args)
0159         if  self.debug:
0160             print("Template tags:")
0161             pprint.pprint(kwds)
0162         return kwds
0163 
0164     def generate(self):
0165         "Generate package templates in a given directory"
0166 
0167         # keep current location, since generate will switch directories
0168         cdir = os.getcwd()
0169 
0170         # read from configutation which template files to create
0171         tmpl_files = self.config.get('tmpl_files', 'all')
0172 
0173         # setup keyword arguments which we'll pass to write method
0174         kwds = self.get_kwds()
0175 
0176         # create template package dir and cd into it
0177         if  tmpl_files == 'all' and self.tmpl not in self.not_in_dir:
0178             if  os.path.isdir(self.pname):
0179                 msg  = "Can't create package '%s'\n" % self.pname
0180                 msg += "Directory %s is already exists" % self.pname
0181                 print(msg)
0182                 sys.exit(1)
0183             os.makedirs(self.pname)
0184             os.chdir(self.pname)
0185 
0186         # read directory driver information and create file list to generate
0187         sdir    = os.path.join(self.tdir, self.tmpl)
0188         sources = [s for s in os.listdir(sdir) \
0189                 if s != 'Driver.dir' and s.find('~') == -1]
0190         driver  = os.path.join(sdir, 'Driver.dir')
0191         if  os.path.isfile(driver):
0192             sources = [s.replace('\n', '') for s in open(driver, 'r').readlines()]
0193         if  'CVS' in sources:
0194             sources.remove('CVS')
0195 
0196         # special case of Skeleton, which requires to generate only given
0197         # file type if self.pname has extension of that type
0198         names = set([s.split('.')[0] for s in sources])
0199         if  names == set(['Skeleton']):
0200             if  self.pname.find('.') != -1:
0201                 _, ext = os.path.splitext(self.pname)
0202                 sources = [s for s in sources if s.rfind(ext) != -1]
0203                 self.pname = self.pname.replace(ext, '')
0204                 kwds = self.get_kwds()
0205                 if  not sources:
0206                     msg = 'Unable to find skeleton for extension "%s"' % ext
0207                     print(msg)
0208                     sys.exit(1)
0209             bdir = os.environ.get('CMSSW_BASE', '')
0210             dirs = os.getcwd().replace(bdir, '').split('/')
0211             ldir = os.getcwd().split('/')[-1]
0212             idir = ''
0213             subsys  = kwds['__subsys__']
0214             pkgname = kwds['__pkgname__']
0215             if  sources == ['Skeleton.cc', 'Skeleton.h']:
0216                 if  ldir == 'interface' and os.getcwd().find(bdir) != -1:
0217                     idir = '%s/%s/interface/' % (subsys, pkgname)
0218             # run within some directory of the Sybsystem/Pkg area
0219             # and only for mkskel <file>.cc
0220             elif sources == ['Skeleton.cc'] and \
0221                 len(dirs) == 5 and dirs[0] == ''  and dirs[1] == 'src':
0222                 idir = '%s/%s/interface/' % (subsys, pkgname)
0223             elif sources == ['Skeleton.h'] and ldir == 'interface' and \
0224                 len(dirs) == 5 and dirs[0] == ''  and dirs[1] == 'src':
0225                 idir = '%s/%s/interface/' % (subsys, pkgname)
0226             kwds.update({'__incdir__': idir})
0227 
0228         # loop over source files, create dirs as necessary and generate files
0229         # names for writing templates
0230         gen_files = []
0231         for src in sources:
0232             if  tmpl_files != 'all':
0233                 fname, ext = os.path.splitext(src)
0234                 if  tmpl_files != ext:
0235                     continue
0236                 #also reject if this is the wrong directory
0237                 if self.working_dir and src.split('/')[-2] != self.working_dir:
0238                     continue
0239                 src = src.split('/')[-1]
0240             if  self.debug:
0241                 print("Read", src)
0242             items = src.split('/')
0243             if  items[-1] == '/':
0244                 items = items[:-1]
0245             tname     = items[-1] # template file name
0246             tmpl_name = os.path.join(sdir, items[-1]) # full tmpl file name
0247             if  os.path.isfile(tmpl_name):
0248                 ftype = 'file'
0249             else:
0250                 ftype = 'dir'
0251             name2gen  = src # new file we'll create
0252             if items[-1] == 'testBuildFile.xml':
0253               name2gen = '/'.join(src.split('/')[:-1])+'/BuildFile.xml'
0254             if  -1 !=tname.split('.')[0].find(self.tmpl): # need to substitute
0255                 name2gen  = name2gen.replace(self.tmpl, self.pname)
0256             name2gen  = os.path.join(os.getcwd(), name2gen)
0257             if  self.debug:
0258                 print("Create", name2gen)
0259             if  ftype == 'dir':
0260                 if  not os.path.isdir(name2gen):
0261                     os.makedirs(name2gen)
0262                 continue # we're done with dir
0263             fdir = os.path.dirname(name2gen)
0264             if  not os.path.isdir(fdir):
0265                 os.makedirs(fdir)
0266             self.write(name2gen, tmpl_name, kwds)
0267             gen_files.append(name2gen.split('/')[-1])
0268         if  tmpl_files == 'all' and self.tmpl not in self.not_in_dir:
0269             msg  = 'New package "%s" of %s type is successfully generated' \
0270                     % (self.pname, self.tmpl)
0271         else:
0272             msg = 'Generated %s file' % ', '.join(gen_files)
0273             if  len(gen_files) > 1:
0274                 msg += 's'
0275         print(msg)
0276         # return back where we started
0277         os.chdir(cdir)
0278         if  msg.find('New package') != -1:
0279             tree(self.pname)