import os, sys, re, shutil """ 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 modes: - 'cmt2waf' (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 = 'cmt2waf', base = 0): self.mode = mode self.base = base 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_base_name (self, i): return 'Pack%03d' % i #--------------------------------------------------------- def package_name (self, i): return os.path.join (self.prefix (i), self.package_base_name (i)) #--------------------------------------------------------- def prefix (self, i): pre = '' if (i - self.base) > (self.packages/2): pre = os.path.normpath (os.path.join ('Pre', 'Fix')) return pre #--------------------------------------------------------- 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) [mode=%s]' % (self.project, self.mode) os.mkdir (os.path.join (self.project, 'cmt')) if self.mode == 'cmt2cmake': f = 'project.cmt' else: f = 'requirements' print '> create %s/cmt/%s' % (self.project, f) text = '' if sys.platform == 'win32': text += '''#-----------------\n project %s macro CXXFLAGS /EHsc\n ''' % self.project print 'base=%d packages=%d' % ( self.base, self.packages ) for p in range(self.base, self.packages + self.base): pre = re.sub (r'[\\]', '/', self.prefix (p)) if self.mode == 'cmt2cmake': text += 'has %s %s\n' % (self.package_base_name (p), pre) else: text += 'use %s %s\n' % (self.package_base_name (p), pre) text += '#-----------------\n' self.write_text (os.path.join ('cmt', f), 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 - self.base) 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): pre = self.prefix (i) used = self.get_used (i) print '> create %s/%s/%s/src/Lib%03d.hxx used=%s all=%s' % (self.project, pre, 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 (pre, name, 'src', 'Lib%03d.hxx' % i), text) #--------------------------------------------------------- def set_package_sources (self, name, i): pre = self.prefix (i) used = self.get_used (i) print '> create %s/%s/%s/src/Lib%03d.cxx' % (self.project, pre, 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 (pre, name, 'src', 'Lib%03d.cxx' % i), text) #--------------------------------------------------------- def set_package_test (self, name, i): pre = self.prefix (i) used = self.get_used (i) print '> create %s/%s/%s/src/test%03d.cxx' % (self.project, pre, name, i) text = '''// -------------------------------------- #include #include int main () { C%(p)d o; o.f (); } // -------------------------------------- ''' % {"p":i} self.write_text (os.path.join (pre, name, 'src', 'test%03d.cxx' % i), text) #--------------------------------------------------------- def set_package_requirements (self, name, i): pre = self.prefix (i) used = self.get_used (i) print '> mkdir %s/%s/%s/cmt' % (self.project, pre, name) os.mkdir (os.path.join (self.project, pre, name, 'cmt')) print '> create %s/%s/%s/cmt/requirements' % (self.project, pre, name) text = '#-----------------\n' if len(used) > 0: for u in used: pu = self.prefix (u) text += 'use %s %s\n' % (self.package_base_name (u), pu) 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} self.write_text (os.path.join (pre, name, 'cmt', 'requirements'), text) #--------------------------------------------------------- def set_package (self, i): import random # # we select some used packages from [0 .. i-1] # used = [] ii = i - self.base if ii > 1: k = (ii-1)/3 if k > 1: used = random.sample (range (self.base, ii - 1 + self.base), k) pre = self.prefix (i) self.used[i] = used print 'used = %s' % used name = self.package_base_name (i) if pre != '': def create_dirs (base, pre): head, tail = os.path.split (pre) if head != '': create_dirs (base, head) try: d = os.path.normpath (os.path.join(base, head, tail)) print '> mkdir %s' % (d) os.mkdir (d) except: pass create_dirs (self.project, os.path.normpath (pre)) print '> mkdir %s/%s/%s' % (self.project, pre, name) os.mkdir (os.path.join (self.project, pre, name)) print '> mkdir %s/%s/%s/src' % (self.project, pre, name) os.mkdir (os.path.join (self.project, pre, 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.base, self.packages + self.base): self.set_package (i) #--------------------------------------------------------- def set_cmt2waf_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) #--------------------------------------------------------- def shox (ctx): cmt.show (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_base_name (i): return 'Pack%%03d' %% i #--------------------------------------------------------- def prefix (i): pre = '' if i > (%(packages)d/2): pre = os.path.normpath (os.path.join ('Pre', 'Fix')) return pre #--------------------------------------------------------- def package_name (i): return os.path.join (prefix (i), package_base_name (i)) #--------------------------------------------------------- def build (ctx): if sys.platform == 'win32': ctx.env.append_value ('CXXFLAGS','/EHsc') """ % { "packages":self.packages } for i in range(self.base, self.packages + self.base): 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_cmt2cmake_script (self): text = ''' project(CMT) cmake_minimum_required(VERSION 2.8) set (CMAKE_VERBOSE_MAKEFILE TRUE) set(ENV{CMTROOT} "C:/Arnault/CMT2") include("$ENV{CMTROOT}/cmake/CMTLib.cmake") ''' self.write_text ('CMakeLists.txt', text) #--------------------------------------------------------- def set_cmake_script (self): text = ''' project(CMT) cmake_minimum_required(VERSION 2.8) set (CMAKE_VERBOSE_MAKEFILE TRUE) ''' for i in range(self.base, self.packages + self.base): used = self.get_all_used (i) shused = used[1:] path = os.path.join (self.prefix(i), 'Pack%03d' % i) path = re.sub (r'[\\]', '/', path) text += """ add_library(Lib%(p)03d SHARED %(path)s/src/Lib%(p)03d.cxx) target_link_libraries(Lib%(p)03d %(use)s) include_directories(%(path)s/src) add_executable(test%(p)03d %(path)s/src/test%(p)03d.cxx) target_link_libraries(test%(p)03d Lib%(p)03d %(use)s) """ % {"p":i, "path":path, "use":' '.join (['Lib%03d' % u for u in shused]) } self.write_text ('CMakeLists.txt', text) #--------------------------------------------------------- def set_configuration_script (self): if self.mode == 'cmt2waf': print '> create %s/wscript for CMT' % (self.project) self.set_cmt2waf_script () elif self.mode == 'waf': print '> create %s/wscript' % (self.project) self.set_waf_script () elif self.mode == 'cmt2cmake': print '> create %s/CMakeList.txt' % (self.project) self.set_cmt2cmake_script () elif self.mode == 'cmake': print '> create %s/CMakeList.txt' % (self.project) self.set_cmake_script () #--------------------------------------------------------- def generate (self, project, packages): self.project = project self.packages = packages self.cleanup_project () self.set_project_requirements () self.set_packages () self.set_configuration_script () #------------------------- # Main class holding command interface # class CMTInterface: #--------------------------------------------------------- def generate_project (self, project, packages, mode = 'cmake', base = 0): here = os.getcwd () generator = CMTProjectGenerator (mode, base) generator.generate (project, packages) Interface = CMTInterface () #---------------------------------------------------------------------------------------------------------------------- # Interface to waflib #---------------------------------------------------------------------------------------------------------------------- if __name__ == "__main__": project = 'A' mode = 'cmake' n = 0 base = 0 if len(sys.argv) > 1: for arg in sys.argv: if re.match ('cmt2waf=(\d+)', arg): m = re.match ('cmt2waf=(\d+)', arg) n = int(m.group(1)) mode = 'cmt2waf' elif re.match ('waf=(\d+)', arg): m = re.match ('waf=(\d+)', arg) n = int(m.group(1)) mode = 'waf' elif re.match ('cmt2cmake=(\d+)', arg): m = re.match ('cmt2cmake=(\d+)', arg) n = int(m.group(1)) mode = 'cmt2cmake' elif re.match ('cmake=(\d+)', arg): m = re.match ('cmake=(\d+)', arg) n = int(m.group(1)) mode = 'cmake' elif re.match ('project=(.+)', arg): m = re.match ('project=(.+)', arg) project = m.group(1) elif re.match ('base=(\d+)', arg): m = re.match ('base=(\d+)', arg) base = int(m.group(1)) else: print """ generator.py [cmt2waf|waf|cmt2cmake|cmake|project|base] cmt2waf= waf= cmt2cmake= cmake= project= base= """ print 'mode=%s project=%s n=%d base=%d' % ( mode, project, n, base) if n > 0: Interface.generate_project (project, n, mode, base)