373 lines
12 KiB
Python
Executable File
373 lines
12 KiB
Python
Executable File
import os, copy, sys
|
|
|
|
if sys.version_info[1]>=5:
|
|
# md5 is deprecated since Python version 2.5
|
|
# this will not work in version 3.x of Python but there's some time until then
|
|
import hashlib
|
|
use_hashlib = True
|
|
else:
|
|
# for compatability reasons for those still using ver <= Python 2.4
|
|
import md5
|
|
use_hashlib = False
|
|
|
|
from lib import normal_path
|
|
|
|
class RawTarget(object):
|
|
def __init__(self, inputs=None, tool=None, options=None, mono_options=None, settings_options=None):
|
|
self.inputs = inputs
|
|
if inputs is None:
|
|
self.inputs = []
|
|
self.tool = tool
|
|
self.options = options
|
|
self.mono_options = mono_options
|
|
self.settings_options = settings_options
|
|
|
|
def __repr__(self):
|
|
return 'RawTarget(inputs=%r)' % (self.inputs, )
|
|
|
|
class RawOptions(object):
|
|
def __init__(self, overriders=None, appenders=None):
|
|
self.overriders = overriders
|
|
self.appenders = appenders
|
|
if not self.overriders:
|
|
self.overriders = {}
|
|
if not self.appenders:
|
|
self.appenders = {}
|
|
|
|
def affect(self, options):
|
|
for key, value in self.overriders.iteritems():
|
|
options[key] = value
|
|
for key, value in self.appenders.iteritems():
|
|
if isinstance(value, list):
|
|
options[key] = options.get(key, []) + value
|
|
elif isinstance(value, str):
|
|
options[key] = options.get(key, '') + value
|
|
elif isinstance(value, dict):
|
|
odict = options.get(key, {})
|
|
odict.update(value)
|
|
options[key] = odict
|
|
else:
|
|
raise NotImplementedError()
|
|
|
|
class RawInput(object):
|
|
def __init__(self, name=None, only_build_dep=False, root_relative=False):
|
|
self.name = name
|
|
self.only_build_dep = only_build_dep
|
|
self.root_relative = root_relative
|
|
|
|
def __repr__(self):
|
|
return 'RawInput(%s)' % (self.name, )
|
|
|
|
class Statistics(object):
|
|
def __init__(self):
|
|
self.targets = 0
|
|
self.made_targets = 0
|
|
|
|
statistics = Statistics()
|
|
|
|
target_cache = {}
|
|
|
|
class Target(object):
|
|
def __init__(self, pathname, raw_target, inputs, original_tinput, options):
|
|
self.pathname = pathname
|
|
self.raw_target = raw_target
|
|
self.inputs = inputs
|
|
self.as_input = original_tinput
|
|
self.tool = self.raw_target.tool
|
|
self.hash = None
|
|
self.built = False
|
|
self.options = options
|
|
|
|
if not self.tool:
|
|
from defaults import get_default_tool
|
|
self.tool = get_default_tool(self)
|
|
if not self.tool:
|
|
from tools import Empty
|
|
self.tool = Empty()
|
|
|
|
def cache(self):
|
|
if use_hashlib:
|
|
hasho = hashlib.md5()
|
|
else:
|
|
hasho = md5.md5()
|
|
for inputo in self.inputs:
|
|
hasho.update(inputo.hash)
|
|
hasho.update(self.tool.cache(self))
|
|
hasho.update(self.pathname)
|
|
self.hash = hasho.digest()
|
|
if self.hash in target_cache:
|
|
return target_cache[self.hash]
|
|
target_cache[self.hash] = self
|
|
return self
|
|
|
|
def exists(self):
|
|
return os.path.exists(self.pathname)
|
|
|
|
def mtime(self):
|
|
return os.stat(self.pathname).st_mtime
|
|
|
|
def filenames(self):
|
|
from comake import COMAKE_OUTPUT_DIRECTORY
|
|
comake_shortname = os.path.basename(self.pathname) + '-' + self.hash.encode('hex')
|
|
comake_outdir = os.path.join(os.path.dirname(self.pathname), COMAKE_OUTPUT_DIRECTORY)
|
|
comake_pathname = os.path.join(comake_outdir, comake_shortname)
|
|
comake_relname = os.path.join(COMAKE_OUTPUT_DIRECTORY, comake_shortname)
|
|
return (comake_shortname, comake_outdir, comake_pathname, comake_relname)
|
|
|
|
def _make(self, reporter):
|
|
if os.path.islink(self.pathname):
|
|
os.unlink(self.pathname)
|
|
|
|
self.tool.make(self, reporter)
|
|
if self.tool.EMPTY:
|
|
return
|
|
|
|
statistics.made_targets += 1
|
|
|
|
comake_shortname, comake_outdir, comake_pathname, comake_relname = self.filenames()
|
|
if not os.path.exists(comake_outdir):
|
|
os.mkdir(comake_outdir)
|
|
os.rename(self.pathname, comake_pathname)
|
|
os.symlink(comake_relname, self.pathname)
|
|
|
|
def get_ext(self):
|
|
return os.path.splitext(self.pathname)[1]
|
|
|
|
def get_actual_inputs(self):
|
|
actual_inputs = []
|
|
for tinput in self.inputs:
|
|
if not tinput.as_input.only_build_dep:
|
|
actual_inputs.append(tinput)
|
|
return actual_inputs
|
|
|
|
def build(self, reporter=None):
|
|
if self.built:
|
|
return 0
|
|
|
|
if not self.tool.EMPTY:
|
|
statistics.targets += 1
|
|
|
|
if reporter is None:
|
|
from report import Report
|
|
reporter = Report()
|
|
|
|
reporter.title(self.pathname)
|
|
|
|
rebuild_target = False
|
|
rebuild_reason = None
|
|
|
|
builds = 0
|
|
for tinput in self.inputs:
|
|
builds += tinput.build(reporter.sub())
|
|
|
|
if os.path.islink(self.pathname):
|
|
link = os.readlink(self.pathname)
|
|
comake_shortname, comake_outdir, comake_pathname, comake_relname = self.filenames()
|
|
if comake_relname != link:
|
|
os.unlink(self.pathname)
|
|
os.symlink(comake_relname, self.pathname)
|
|
|
|
if not os.path.exists(self.pathname):
|
|
rebuild_target = True
|
|
rebuild_reason = "target requires creation"
|
|
else:
|
|
my_mtime = self.mtime()
|
|
others_mtime = [tinput.mtime() for tinput in self.inputs]
|
|
if others_mtime:
|
|
max_others_mtime = max(others_mtime)
|
|
if max_others_mtime > my_mtime:
|
|
rebuild_target = True
|
|
rebuild_reason = "target is older than dependencies"
|
|
|
|
if not rebuild_target:
|
|
if builds:
|
|
rebuild_target = True
|
|
rebuild_reason = "dependencies were rebuilt"
|
|
|
|
if not rebuild_target:
|
|
if self.tool.rebuild_needed(self):
|
|
rebuild_target = True
|
|
rebuild_reason = "rebuilding required by tool"
|
|
|
|
if rebuild_target:
|
|
if not self.tool.EMPTY:
|
|
reporter.print_title()
|
|
reporter.print_text("Building: %s" % (rebuild_reason, ))
|
|
self._make(reporter)
|
|
builds += 1
|
|
|
|
self.built = True
|
|
return builds
|
|
|
|
def dump(self, indent=0):
|
|
print indent*" " + self.pathname + " { "
|
|
print indent*" " + " Built: %r" % (self.built, )
|
|
print indent*" " + " %x" % (id(self), )
|
|
print indent*" " + " " + self.hash.encode('hex')
|
|
print indent*" " + " %r" % (self.tool.cache(self), )
|
|
count = 1
|
|
if len(self.inputs) != 0:
|
|
print indent*" " + " Inputs#: %d" % (len(self.inputs), )
|
|
if not self.built:
|
|
for tinput in self.inputs:
|
|
count += tinput.dump(indent+1)
|
|
else:
|
|
print indent*" " + " [..]"
|
|
|
|
print indent*" " + " %d" % (count, )
|
|
print indent*" " + "} "
|
|
self.built = True
|
|
return count
|
|
|
|
_per_directory_comake_file = {}
|
|
|
|
class COMakeFile(object):
|
|
def __init__(self):
|
|
self.targets = {}
|
|
|
|
def __repr__(self):
|
|
return 'COMakeFile(%s)' % (repr(self.targets), )
|
|
|
|
_globals_dict = None
|
|
|
|
class BuildCancelError(Exception):
|
|
pass
|
|
|
|
def get_global_dict():
|
|
global _globals_dict
|
|
if _globals_dict is None:
|
|
_globals_dict = {}
|
|
_globals_dict['Target'] = RawTarget
|
|
_globals_dict['Input'] = RawInput
|
|
_globals_dict['Options'] = RawOptions
|
|
_globals_dict['pathjoin'] = os.path.join
|
|
_globals_dict['getenv'] = os.getenv
|
|
_globals_dict['BuildCancelError'] = BuildCancelError
|
|
from comake.tools import exported_tools
|
|
for tool in exported_tools:
|
|
_globals_dict[tool.__name__] = tool
|
|
return _globals_dict
|
|
|
|
def get_per_directory_comake_file(dirname):
|
|
comake_file = _per_directory_comake_file.get(dirname)
|
|
if comake_file:
|
|
return comake_file
|
|
comake_file = COMakeFile()
|
|
from comake import DEFAULT_BUILD_NAME
|
|
build_name = os.path.join(dirname, DEFAULT_BUILD_NAME)
|
|
if not os.path.exists(build_name):
|
|
return comake_file
|
|
|
|
globals_dict = dict(get_global_dict())
|
|
def input_list(ext, new_ext):
|
|
lst = []
|
|
for filename in os.listdir(dirname):
|
|
base_part, ext_part = os.path.splitext(filename)
|
|
if ext_part == ext:
|
|
lst.append(RawInput(base_part + new_ext))
|
|
return lst
|
|
|
|
def target_pathname(filename):
|
|
return os.path.join(dirname, filename)
|
|
|
|
def deftarget(filename):
|
|
from comake.defaults import get_default_raw_target
|
|
return get_default_raw_target(target_pathname(filename))
|
|
|
|
globals_dict['input_list'] = input_list
|
|
globals_dict['target_pathname'] = target_pathname
|
|
globals_dict['deftarget'] = deftarget
|
|
globals_dict['current_dirname'] = dirname
|
|
execfile(build_name, globals_dict, vars(comake_file))
|
|
_per_directory_comake_file[dirname] = comake_file
|
|
return comake_file
|
|
|
|
def get_raw_target(pathname):
|
|
dirname = os.path.dirname(pathname)
|
|
basename = os.path.basename(pathname)
|
|
comake_file = get_per_directory_comake_file(dirname)
|
|
raw_target = comake_file.targets.get(basename)
|
|
if not raw_target:
|
|
from comake.defaults import get_default_raw_target
|
|
raw_target = get_default_raw_target(pathname)
|
|
if not raw_target:
|
|
if os.path.exists(pathname):
|
|
if not os.path.islink(pathname):
|
|
return RawTarget()
|
|
else:
|
|
print os.readlink(pathname)
|
|
return raw_target
|
|
|
|
class TargetNotFoundError(Exception):
|
|
pass
|
|
|
|
def create_target_tree(pathname):
|
|
def _recur(pathname, original_tinput, options):
|
|
raw_target = get_raw_target(pathname)
|
|
if not raw_target:
|
|
print "Error, target %s not found" % (pathname, )
|
|
raise TargetNotFoundError()
|
|
|
|
options = copy.deepcopy(options)
|
|
if raw_target.options:
|
|
raw_target.options.affect(options)
|
|
|
|
if raw_target.settings_options:
|
|
from settings import settings
|
|
raw_target.settings_options.affect(vars(settings))
|
|
|
|
inputs = []
|
|
for index, tinput in enumerate(raw_target.inputs):
|
|
if tinput.root_relative:
|
|
abs_name = tinput.name
|
|
else:
|
|
abs_name = os.path.join(os.path.dirname(pathname), tinput.name)
|
|
abs_name = normal_path(abs_name)
|
|
try:
|
|
inputs.append(_recur(abs_name, tinput, options))
|
|
except TargetNotFoundError:
|
|
print "Included from: %s [%d]" % (pathname, index+1)
|
|
raise
|
|
|
|
if raw_target.mono_options:
|
|
raw_target.mono_options.affect(options)
|
|
|
|
target = Target(pathname, raw_target, inputs, original_tinput, options)
|
|
target = target.cache()
|
|
return target
|
|
|
|
return _recur(pathname, None, {})
|
|
|
|
def clean():
|
|
from comake import COMAKE_OUTPUT_DIRECTORY
|
|
from comake import build_root
|
|
|
|
def unlink(pathname):
|
|
pathname_display = pathname[len(build_root)+1:]
|
|
print "removing file %s" % (pathname_display, )
|
|
os.unlink(pathname)
|
|
|
|
def rmdir(pathname):
|
|
pathname_display = pathname[len(build_root)+1:]
|
|
print "removing dir %s" % (pathname_display, )
|
|
os.rmdir(pathname)
|
|
|
|
def _recur(pathname):
|
|
comake_dir = os.path.basename(pathname) == COMAKE_OUTPUT_DIRECTORY
|
|
for filename in os.listdir(pathname):
|
|
fullname = os.path.join(pathname, filename)
|
|
if os.path.islink(fullname):
|
|
link = os.readlink(fullname)
|
|
if link.startswith(COMAKE_OUTPUT_DIRECTORY):
|
|
unlink(fullname)
|
|
|
|
if os.path.isdir(fullname):
|
|
_recur(fullname)
|
|
if comake_dir:
|
|
unlink(fullname)
|
|
|
|
if comake_dir:
|
|
rmdir(pathname)
|
|
|
|
return _recur(build_root)
|