colinux64/bin/comake/target.py
2025-02-13 19:44:47 -07:00

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)