import os, sys, re, shutil #------------------------- # Data model # class CMTDtb: #--------------------------------------------------------- def __init__ (self): self.paths = [] self.packages = {} self.libraries = {} self.programs = {} self.macros = {} #--------------------------------------------------------- def scan_packages (self): for name in self.packages: p = self.packages[name] p.done = False #--------------------------------------------------------- def add_package (self, name, package): if not name in self.packages: self.packages[name] = package #--------------------------------------------------------- def add_library (self, ctx, use, name, sources): topsrc = os.path.join (use, 'src') print 'add_library> use=%s name=%s topsrc=%s' % (use, name, topsrc) fsources = [] for s in sources: lst = ctx.path.ant_glob (os.path.join (topsrc, s)) for f in lst: p = f.path_from (ctx.path) fsources.append (p) self.libraries[name] = fsources #--------------------------------------------------------- def add_program (self, ctx, use, name, sources, uses = ''): topsrc = os.path.join (use, 'src') fsources = [] for s in sources: lst = ctx.path.ant_glob (os.path.join (topsrc, s)) for f in lst: p = f.path_from (ctx.path) fsources.append (p) self.programs[name] = [fsources, uses] #--------------------------------------------------------- def add_macro (self, ctx, use, name, value): self.macros[name] = value #--------------------------------------------------------- def add_cmt_path (self, path): self.paths.append (path) #--------------------------------------------------------- def show_macros (self): for macro, value in self.macros.iteritems(): print '> macro: ' + macro + ' ' + str(value) #--------------------------------------------------------- def show_libraries (self): for lib, sources in self.libraries.iteritems(): print '> library: ' + lib #--------------------------------------------------------- def show_programs (self): for lib, sources in self.programs.iteritems(): print '> programs: ' + lib dtb = CMTDtb () #------------------------- # Package configuration # class CMTMacro: #--------------------------------------------------------- def __init__ (self, name = ''): if name == '': return self.name = name #------------------------- # Package configuration # class CMTPackage: #--------------------------------------------------------- def __init__ (self, ctx, name = '', path = ''): if name == '': return self.name = name self.path = path self.uses = [] dtb.add_package (name, self) self.read_requirements (ctx) self.done = False #--------------------------------------------------------- def add_use (self, ctx, name): found = False self.uses.append (name) if not name in dtb.packages: for path in dtb.paths: #print 'CMTDtb.add_use> path=' + path p = os.path.join (path, name) if os.path.exists (p): p = os.path.join (p, 'cmt') #print 'CMTPackage.add_use> p=' + p if os.path.exists (p): package = CMTPackage (ctx, name, path) found = True else: found = True if not found: print 'package ' + name + 'not found in cmtpaths' #--------------------------------------------------------- def show_uses (self, pre = ''): print pre + self.name + ' ' + self.path if self.done: return self.done = True #print pre + 'uses=' + ','.join(self.uses) for name in self.uses: package = dtb.packages[name] package.show_uses (pre + ' ') #--------------------------------------------------------- def read_requirements (self, ctx): path = os.path.join (self.path, self.name, 'cmt', 'requirements') #print 'CMTPackage.read_requirements> path=' + path try: f = open (path, 'r') for line in f: words = re.split ('\s+', line) if len(words[-1]) == 0: words = words[:-1] #print 'ws=[' + str(words) + '] last word=[' + str(len(words[-1])) + ']' verb = words[0] if verb == 'use': #print 'from package ' + self.name + '>use ' + words[1] self.add_use (ctx=ctx, name=words[1]) elif verb == 'library': #print self.name + '>library ' + words[1] + ' ' + str(words[2:]) dtb.add_library (ctx=ctx, use=self.name, name=words[1], sources=words[2:]) elif verb == 'program': #print self.name + '>program ' + words[1] + ' ' + str(words[2:]) dtb.add_program (ctx=ctx, use=self.name, name=words[1], sources=words[2:]) elif verb == 'macro': #print self.name + '>macro ' + words[1] + ' ' + str(words[2:]) dtb.add_macro (ctx=ctx, use=self.name, name=words[1], value=words[2:]) finally: f.close () #--------------------------------------------------------- def build (self, ctx): src = os.path.join (self.path, self.name, 'src') print 'CMTPackage.build> srcnode=' + str(ctx.srcnode) + ' here=' + os.getcwd () includes = src libraries = '' # we collect include dirs from used packages for use in self.uses: inc = os.path.join (use, 'src') includes += ' ' + inc # we declare the build for all libraries for lib, sources in dtb.libraries.iteritems(): print self.name + '> build library ' + lib + ' sources=' + str(sources) + ' includes=' + str(includes) linkopts = '' if lib + '_linkopts' in dtb.macros: linkopts = dtb.macros[lib + '_linkopts'] link = ' '.join (linkopts) ctx.shlib ( source=sources, includes=includes, use=link, target=lib) # we declare the build for all programs for pgm, attrs in dtb.programs.iteritems(): sources = attrs[0] linkopts = '' if pgm + '_linkopts' in dtb.macros: linkopts = dtb.macros[pgm + '_linkopts'] link = ' '.join (linkopts) print self.name + '> build program ' + pgm + ' sources=' + str(sources) + ' link=' + str(link) uses = attrs[1] ctx.program ( source=sources, includes=includes, use=link, target=pgm) """ ProjectGenerator Construct a CMT project with a hierarchy of packages each containing - sources of a library (with one C++ class) - sources of a test program (which instantiates one object of the class) - a cmt requirements file Packages may use other packages. For each used package, the package's class includes the used classes, and instantiates one object for each the used classe Two modes: - 'cmt' (the default) only one generic wscript is generated. This script makes use of the CMT module which interprets the requirements files to construct the WAF build actions. - 'waf' one complete WAF script is generated. This script is a pure waf script independant of CMT. """ class CMTProjectGenerator: #--------------------------------------------------------- def __init__ (self, mode = 'cmt'): self.mode = mode self.used = {} def write_text (self, name, text): try: f = open(os.path.join (self.project, name), 'w') f.write (text) finally: f.close () def package_name (self, i): return 'Pack%03d' % i #--------------------------------------------------------- def cleanup_project (self): print '> rmdir (%s)' % self.project if os.path.exists(self.project): shutil.rmtree(self.project) #--------------------------------------------------------- def set_project_requirements (self): print '> mkdir (%s)' % self.project os.mkdir (self.project) print '> mkdir (%s/cmt)' % self.project os.mkdir (os.path.join (self.project, 'cmt')) print '> create %s/cmt/requirements' % self.project text = '' if sys.platform == 'win32': text += '''#-----------------\n macro CXXFLAGS /EHsc\n ''' for p in range(self.packages): text += 'use %s\n' % self.package_name (p) text += '#-----------------\n' self.write_text (os.path.join ('cmt', 'requirements'), text) #--------------------------------------------------------- def get_used (self, p): used = set () if p in self.used: used = set (self.used[p]) return used #--------------------------------------------------------- def get_all_used (self, p): def recurse (p, all = []): u = self.get_used (p) if not p in range (self.packages): return [] t = [p] for sub in u: if not sub in t + all: t += recurse (sub, t + all) return t all = recurse (p, []) return all #--------------------------------------------------------- def set_package_headers (self, name, i): used = self.get_used (i) print '> create %s/%s/src/Lib%03d.hxx used=%s all=%s' % (self.project, name, i, used, all) text = ''' #ifndef __Lib%(p)03d_hxx__ #define __Lib%(p)03d_hxx__ // -------------------------------------- ''' % {"p":i} if len(used) > 0: for u in used: text += '#include \n' % u text += ''' #ifdef _MSC_VER #define DllExport __declspec( dllexport ) #else #define DllExport #endif class DllExport C%(p)i { public: C%(p)i (); ~C%(p)i (); void f(); private:\n''' % {"p":i} if len(used) > 0: for u in used: text += ' C%(p)d o%(p)d;\n' % {"p":u} text += '''}; // -------------------------------------- #endif ''' self.write_text (os.path.join (name, 'src', 'Lib%03d.hxx' % i), text) #--------------------------------------------------------- def set_package_sources (self, name, i): used = self.get_used (i) print '> create %s/%s/src/Lib%03d.cxx' % (self.project, name, i) text = '''// -------------------------------------- #include #include C%(p)d::C%(p)d () { std::cout << "Constructor C%(p)d" << std::endl; } C%(p)d::~C%(p)d () { std::cout << "Destructor C%(p)d" << std::endl; } void C%(p)d::f () { std::cout << "C%(p)d.f" << std::endl;\n''' % {"p":i} if len(used) > 0: for k in used: text += ' o%d.f();\n' % (k) text += '''} // -------------------------------------- ''' self.write_text (os.path.join (name, 'src', 'Lib%03d.cxx' % i), text) #--------------------------------------------------------- def set_package_test (self, name, i): used = self.get_used (i) print '> create %s/%s/src/test%03d.cxx' % (self.project, name, i) text = '''// -------------------------------------- #include #include int main () { C%(p)d o; o.f (); } // -------------------------------------- ''' % {"p":i} self.write_text (os.path.join (name, 'src', 'test%03d.cxx' % i), text) #--------------------------------------------------------- def set_package_requirements (self, name, i): used = self.get_used (i) print '> mkdir %s/%s/cmt' % (self.project, name) os.mkdir (os.path.join (self.project, name, 'cmt')) print '> create %s/%s/cmt/requirements' % (self.project, name) text = '#-----------------\n' if len(used) > 0: for u in used: text += 'use %s\n' % self.package_name (u) all_used = self.get_all_used (i) text += 'macro Lib%(p)03d_linkopts %(libs)s\n' % {"p":i, "libs":' '.join (['Lib%03d' % u for u in all_used[1:]])} text += 'library Lib%(p)03d Lib%(p)03d.cxx\n' % {"p":i} text += 'macro test%(p)03d_linkopts %(libs)s\n' % {"p":i, "libs":' '.join (['Lib%03d' % u for u in all_used])} text += '''program test%(p)03d test%(p)03d.cxx #-----------------''' % {"p":i} print '> create %s/%s/cmt/requirements' % (self.project, name) self.write_text (os.path.join (name, 'cmt', 'requirements'), text) #--------------------------------------------------------- def set_package (self, i): import random # # we select some used packages from [0 .. i-1] # used = [] if i > 1: k = (i-1)/3 if k > 1: used = random.sample (range (i-1), k) self.used[i] = used print 'used = %s' % used name = self.package_name (i) print '> mkdir %s/%s' % (self.project, name) os.mkdir (os.path.join (self.project, name)) print '> mkdir %s/%s/src' % (self.project, name) os.mkdir (os.path.join (self.project, name, 'src')) self.set_package_headers (name, i) self.set_package_sources (name, i) self.set_package_test (name, i) self.set_package_requirements (name, i) #--------------------------------------------------------- def set_packages (self): for i in range(self.packages): self.set_package (i) #--------------------------------------------------------- def set_cmt_script (self): text = ''' import cmt top, out = cmt.init () #--------------------------------------------------------- def options (ctx): cmt.options (ctx) #--------------------------------------------------------- def configure (ctx): cmt.configure (ctx) #--------------------------------------------------------- def build (ctx): cmt.build (ctx) ''' self.write_text ('wscript', text) #--------------------------------------------------------- def set_waf_script (self): text = """ import os, sys top = '' out = sys.platform #--------------------------------------------------------- def options (ctx): ctx.load ('compiler_cxx') here = os.getcwd () default_prefix = os.path.join (here, 'installarea') try: print 'options' ctx.add_option('--prefix', help = "installation prefix (configuration only) [Default: '%s']" % default_prefix, default = default_prefix, dest = 'prefix') except: print 'options error' pass #--------------------------------------------------------- def configure (ctx): ctx.load ('compiler_cxx') #--------------------------------------------------------- def package_name (i): return 'Pack%03d' % i #--------------------------------------------------------- def build (ctx): if sys.platform == 'win32': ctx.env.append_value ('CXXFLAGS','/EHsc') """ for i in range(self.packages): used = self.get_all_used (i) shused = used[1:] includes = "includes=[os.path.join (package_name(u), 'src') for u in %s]" % used text += """ ctx.shlib(features = ['cxx','cxxshlib'], source=os.path.join(package_name(%(p)d), 'src', 'Lib%(p)03d.cxx'), %(includes)s, use='%(shuse)s', target='Lib%(p)03d') ctx.program(source=os.path.join(package_name(%(p)d), 'src', 'test%(p)03d.cxx'), %(includes)s, use='%(use)s', target='test%(p)03d') """ % {"p":i, "shuse":' '.join (['Lib%03d' % u for u in shused]), "use":' '.join (['Lib%03d' % u for u in used]), "includes":includes} self.write_text ('wscript', text) #--------------------------------------------------------- def set_wscript (self): print '> create %s/wscript' % (self.project) if self.mode == 'cmt': self.set_cmt_script () elif self.mode == 'waf': self.set_waf_script () #--------------------------------------------------------- def generate (self, project, packages): self.project = project self.packages = packages self.cleanup_project () self.set_project_requirements () self.set_packages () self.set_wscript () #------------------------- # Main class holding command interface # class CMTInterface: #--------------------------------------------------------- def show_uses (self): dtb.scan_packages () dtb.project.show_uses () #--------------------------------------------------------- def show_libraries (self): dtb.show_libraries () #--------------------------------------------------------- def show_programs (self): dtb.show_programs () #--------------------------------------------------------- def show_macros (self): dtb.show_macros () #--------------------------------------------------------- def generate_project (self, project, packages, mode = 'cmt'): here = os.getcwd () # we may select mode='waf' or mode='cmt' (default is 'cmt') generator = CMTProjectGenerator (mode) generator.generate (project, packages) Interface = CMTInterface () #---------------------------------------------------------------------------------------------------------------------- # Interface to waflib #---------------------------------------------------------------------------------------------------------------------- #------------------------- # Generic init action # def init (): # # discovering the local contexte # here = os.getcwd () print 'here=' + here # # who am I (in terms of Project) # me = os.path.basename (here) print 'me=' + me p = os.path.dirname (here) print 'p=' + p # # the current package produces its own entry in the CMTPATH # ... as well its own use entry # dtb.add_cmt_path (here) build = 'any' if sys.platform == 'win32': build = 'win32' top = here out = os.path.join (top, build) return (top, out) #------------------------- # Generic options action # def options (ctx): ctx.load ('compiler_cxx') here = os.getcwd () default_prefix = os.path.join (here, 'installarea') try: print 'options' ctx.add_option('--prefix', help = "installation prefix (configuration only) [Default: '%s']" % default_prefix, default = default_prefix, dest = 'prefix') except: print 'options error' pass #------------------------- # Generic configure action # def configure (ctx): here = os.getcwd () me = os.path.basename (here) p = os.path.dirname (here) dtb.project = CMTPackage (ctx, me, p) print 'cmt> configure in ' + ctx.path.abspath () ctx.load ('compiler_cxx') for path in dtb.paths: print 'path: ' + path print '------------ show:' Interface.show_uses () Interface.show_libraries () Interface.show_programs () Interface.show_macros () print '------------' #------------------------- # Construct all build actions from all declared constituents from all used package # def build (ctx): if 'CXXFLAGS' in dtb.macros: ctx.env.append_value ('CXXFLAGS', dtb.macros['CXXFLAGS']) print 'cmt> project build in ' + ctx.path.abspath () u = dtb.project u.build (ctx) if __name__ == "__main__": print '----------init--------------' top, out = init () print '----------------------------' if len(sys.argv) > 1: if re.match ('gencmt=(\d+)', sys.argv[1]): m = re.match ('gencmt=(\d+)', sys.argv[1]) Interface.generate_project ('A', int(m.group(1)), 'cmt') elif re.match ('genwaf=(\d+)', sys.argv[1]): m = re.match ('genwaf=(\d+)', sys.argv[1]) Interface.generate_project ('A', int(m.group(1)), 'waf') elif re.match ('os', sys.argv[1]): print 'os=%s' % (os.name) elif re.match ('platform', sys.argv[1]): print 'platform=%s' % (sys.platform) else: print """ cmt.py [gencmt|genwaf|os|platform] gencmt= genwaf= os platform """