📄 gen_base.py
字号:
#
# gen_base.py -- infrastructure for generating makefiles, dependencies, etc.
#
import os
import sys
import string
import glob
import re
import fileinput
import ConfigParser
import getversion
class GeneratorBase:
#
# Derived classes should define a class attribute named _extension_map.
# This attribute should be a dictionary of the form:
# { (target-type, file-type): file-extension ...}
#
# where: target-type is 'exe', 'lib', ...
# file-type is 'target', 'object', ...
#
def __init__(self, fname, verfname, options=None):
parser = ConfigParser.ConfigParser()
parser.read(fname)
self.cfg = Config()
self.cfg.swig_lang = string.split(parser.get('options', 'swig-languages'))
# Version comes from a header file since it is used in the code.
try:
vsn_parser = getversion.Parser()
vsn_parser.search('SVN_VER_MAJOR', 'libver')
self.cfg.version = vsn_parser.parse(verfname).libver
except:
raise GenError('Unable to extract version.')
self.sections = { }
self.graph = DependencyGraph()
if not hasattr(self, 'skip_sections'):
self.skip_sections = { }
# PASS 1: collect the targets and some basic info
for section_name in _filter_sections(parser.sections()):
if self.skip_sections.has_key(section_name):
continue
options = {}
for option in parser.options(section_name):
options[option] = parser.get(section_name, option)
type = options.get('type')
target_class = _build_types.get(type)
if not target_class:
raise GenError('ERROR: unknown build type for ' + section_name)
section = target_class.Section(options, target_class)
self.sections[section_name] = section
section.create_targets(self.graph, section_name, self.cfg,
self._extension_map)
# compute intra-library dependencies
for section in self.sections.values():
dep_types = ((DT_LINK, section.options.get('libs')),
(DT_NONLIB, section.options.get('nonlibs')))
for dt_type, deps_list in dep_types:
if deps_list:
for dep_section in self.find_sections(deps_list):
for target in section.get_targets():
self.graph.bulk_add(dt_type, target.name,
dep_section.get_dep_targets(target))
# collect various files
self.includes = _collect_paths(parser.get('options', 'includes'))
self.apache_files = _collect_paths(parser.get('static-apache', 'paths'))
# collect all the test scripts
self.scripts = _collect_paths(parser.get('test-scripts', 'paths'))
self.bdb_scripts = _collect_paths(parser.get('bdb-test-scripts', 'paths'))
self.swig_dirs = string.split(parser.get('swig-dirs', 'paths'))
def find_sections(self, section_list):
"""Return a list of section objects from a string of section names."""
sections = [ ]
for section_name in string.split(section_list):
if not self.skip_sections.has_key(section_name):
sections.append(self.sections[section_name])
return sections
def compute_hdr_deps(self):
#
# Find all the available headers and what they depend upon. the
# include_deps is a dictionary mapping a short header name to a tuple
# of the full path to the header and a dictionary of dependent header
# names (short) mapping to None.
#
# Example:
# { 'short.h' : ('/path/to/short.h',
# { 'other.h' : None, 'foo.h' : None }) }
#
# Note that this structure does not allow for similarly named headers
# in per-project directories. SVN doesn't have this at this time, so
# this structure works quite fine. (the alternative would be to use
# the full pathname for the key, but that is actually a bit harder to
# work with since we only see short names when scanning, and keeping
# a second variable around for mapping the short to long names is more
# than I cared to do right now)
#
include_deps = _create_include_deps(map(native_path, self.includes))
for d in unique(self.graph.get_sources(DT_LIST, LT_TARGET_DIRS)):
hdrs = glob.glob(os.path.join(native_path(d), '*.h'))
if hdrs:
more_deps = _create_include_deps(hdrs, include_deps)
include_deps.update(more_deps)
for objname, sources in self.graph.get_deps(DT_OBJECT):
assert len(sources) == 1
if isinstance(objname, SWIGObject):
for ifile in self.graph.get_sources(DT_SWIG_C, sources[0]):
if isinstance(ifile, SWIGSource):
for short in _find_includes(native_path(ifile.filename),
include_deps):
self.graph.add(DT_SWIG_C, sources[0],
build_path(include_deps[short][0]))
continue
if isinstance(sources[0], ObjectFile) or \
isinstance(sources[0], HeaderFile):
if sources[0].source_generated == 1:
continue
filename = native_path(sources[0].filename)
if not os.path.isfile(filename):
continue
hdrs = [ ]
for short in _find_includes(filename, include_deps):
self.graph.add(DT_OBJECT, objname,
build_path(include_deps[short][0]))
class DependencyGraph:
"""Record dependencies between build items.
See the DT_* values for the different dependency types. For each type,
the target and source objects recorded will be different. They could
be file names, Target objects, install types, etc.
"""
def __init__(self):
self.deps = { } # type -> { target -> [ source ... ] }
for dt in dep_types:
self.deps[dt] = { }
def add(self, type, target, source):
if self.deps[type].has_key(target):
self.deps[type][target].append(source)
else:
self.deps[type][target] = [ source ]
def bulk_add(self, type, target, sources):
if self.deps[type].has_key(target):
self.deps[type][target].extend(sources)
else:
self.deps[type][target] = sources[:]
def get_sources(self, type, target, cls=None):
sources = self.deps[type].get(target, [ ])
if not cls:
return sources
filtered = [ ]
for src in sources:
if isinstance(src, cls):
filtered.append(src)
return filtered
def get_all_sources(self, type):
sources = [ ]
for group in self.deps[type].values():
sources.extend(group)
return sources
def get_deps(self, type):
return self.deps[type].items()
# dependency types
dep_types = [
'DT_INSTALL', # install areas. e.g. 'lib', 'base-lib'
'DT_OBJECT', # an object filename, depending upon .c filenames
'DT_SWIG_C', # a swig-generated .c file, depending upon .i filename(s)
'DT_LINK', # a libtool-linked filename, depending upon object fnames
'DT_INCLUDE', # filename includes (depends) on sources (all basenames)
'DT_NONLIB', # filename depends on object fnames, but isn't linked to them
'DT_LIST', # arbitrary listS of values, see list_types below
]
list_types = [
'LT_PROJECT', # Visual C++ projects (TargetSpecial instances)
'LT_TEST_DEPS', # Test programs to build
'LT_TEST_PROGS', # Test programs to run (subset of LT_TEST_DEPS)
'LT_BDB_TEST_DEPS', # File system test programs to build
'LT_BDB_TEST_PROGS', # File system test programs to run
'LT_TARGET_DIRS', # directories where files are built
'LT_MANPAGES', # manpages
]
# create some variables for these
for _dt in dep_types + list_types:
# e.g. DT_INSTALL = 'DT_INSTALL'
globals()[_dt] = _dt
class DependencyNode:
def __init__(self, filename):
self.filename = filename
def __str__(self):
return self.filename
class ObjectFile(DependencyNode):
def __init__(self, filename, compile_cmd = None):
DependencyNode.__init__(self, filename)
self.compile_cmd = compile_cmd
self.source_generated = 0
class SWIGObject(ObjectFile):
def __init__(self, filename, lang):
ObjectFile.__init__(self, filename)
self.lang = lang
self.lang_abbrev = lang_abbrev[lang]
### hmm. this is Makefile-specific
self.compile_cmd = '$(COMPILE_%s_WRAPPER)' % string.upper(self.lang_abbrev)
self.source_generated = 1
class HeaderFile(DependencyNode):
def __init__(self, filename, classname = None, compile_cmd = None):
DependencyNode.__init__(self, filename)
self.classname = classname
self.compile_cmd = compile_cmd
class SourceFile(DependencyNode):
def __init__(self, filename, reldir):
DependencyNode.__init__(self, filename)
self.reldir = reldir
class SWIGSource(SourceFile):
def __init__(self, filename):
SourceFile.__init__(self, filename, build_path_dirname(filename))
pass
lang_abbrev = {
'python' : 'py',
'java' : 'java',
'perl' : 'pl',
'ruby' : 'rb',
'tcl' : 'tcl',
### what others?
}
lang_full_name = {
'python' : 'Python',
'java' : 'Java',
'perl' : 'Perl',
'ruby' : 'Ruby',
'tcl' : 'TCL',
### what others?
}
lang_utillib_suffix = {
'python' : 'py',
'java' : 'java',
'perl' : 'perl',
'ruby' : 'ruby',
'tcl' : 'tcl',
### what others?
}
class Target(DependencyNode):
"A build target is a node in our dependency graph."
def __init__(self, name, options, cfg, extmap):
self.name = name
self.desc = options.get('description')
self.path = options.get('path', '')
self.add_deps = options.get('add-deps', '')
self.add_install_deps = options.get('add-install-deps', '')
self.msvc_name = options.get('msvc-name') # override project name
def add_dependencies(self, graph, cfg, extmap):
# subclasses should override to provide behavior, as appropriate
raise NotImplementedError
class Section:
"""Represents an individual section of build.conf
The Section class is sort of a factory class which is responsible for
creating and keeping track of Target instances associated with a section
of the configuration file. By default it only allows one Target per
section, but subclasses may create multiple Targets.
"""
def __init__(self, options, target_class):
self.options = options
self.target_class = target_class
def create_targets(self, graph, name, cfg, extmap):
"""Create target instances"""
self.target = self.target_class(name, self.options, cfg, extmap)
self.target.add_dependencies(graph, cfg, extmap)
def get_targets(self):
"""Return list of target instances associated with this section"""
return [self.target]
def get_dep_targets(self, target):
"""Return list of targets from this section that "target" depends on"""
return [self.target]
class TargetLinked(Target):
"The target is linked (by libtool) against other libraries."
def __init__(self, name, options, cfg, extmap):
Target.__init__(self, name, options, cfg, extmap)
self.install = options.get('install')
self.compile_cmd = options.get('compile-cmd')
self.sources = options.get('sources', '*.c')
self.link_cmd = options.get('link-cmd', '$(LINK)')
self.external_lib = options.get('external-lib')
self.external_project = options.get('external-project')
self.msvc_libs = string.split(options.get('msvc-libs', ''))
def add_dependencies(self, graph, cfg, extmap):
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -