mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
293 lines
10 KiB
Python
293 lines
10 KiB
Python
# Status: ported.
|
|
# Base revision: 45462.
|
|
|
|
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
|
# distribute this software is granted provided this copyright notice appears in
|
|
# all copies. This software is provided "as is" without express or implied
|
|
# warranty, and with no claim as to its suitability for any purpose.
|
|
|
|
|
|
|
|
import re
|
|
import os
|
|
import os.path
|
|
from b2.util.utility import replace_grist, os_name
|
|
from b2.exceptions import *
|
|
from b2.build import feature, property, scanner
|
|
|
|
__re_hyphen = re.compile ('-')
|
|
|
|
def __register_features ():
|
|
""" Register features need by this module.
|
|
"""
|
|
# The feature is optional so that it is never implicitly added.
|
|
# It's used only for internal purposes, and in all cases we
|
|
# want to explicitly use it.
|
|
feature.feature ('target-type', [], ['composite', 'optional'])
|
|
feature.feature ('main-target-type', [], ['optional', 'incidental'])
|
|
feature.feature ('base-target-type', [], ['composite', 'optional', 'free'])
|
|
|
|
def reset ():
|
|
""" Clear the module state. This is mainly for testing purposes.
|
|
Note that this must be called _after_ resetting the module 'feature'.
|
|
"""
|
|
global __prefixes_suffixes, __suffixes_to_types, __types, __rule_names_to_types, __target_suffixes_cache
|
|
|
|
__register_features ()
|
|
|
|
# Stores suffixes for generated targets.
|
|
__prefixes_suffixes = [property.PropertyMap(), property.PropertyMap()]
|
|
|
|
# Maps suffixes to types
|
|
__suffixes_to_types = {}
|
|
|
|
# A map with all the registered types, indexed by the type name
|
|
# Each entry is a dictionary with following values:
|
|
# 'base': the name of base type or None if type has no base
|
|
# 'derived': a list of names of type which derive from this one
|
|
# 'scanner': the scanner class registered for this type, if any
|
|
__types = {}
|
|
|
|
# Caches suffixes for targets with certain properties.
|
|
__target_suffixes_cache = {}
|
|
|
|
reset ()
|
|
|
|
|
|
def register (type, suffixes = [], base_type = None):
|
|
""" Registers a target type, possibly derived from a 'base-type'.
|
|
If 'suffixes' are provided, they list all the suffixes that mean a file is of 'type'.
|
|
Also, the first element gives the suffix to be used when constructing and object of
|
|
'type'.
|
|
type: a string
|
|
suffixes: None or a sequence of strings
|
|
base_type: None or a string
|
|
"""
|
|
# Type names cannot contain hyphens, because when used as
|
|
# feature-values they will be interpreted as composite features
|
|
# which need to be decomposed.
|
|
if __re_hyphen.search (type):
|
|
raise BaseException ('type name "%s" contains a hyphen' % type)
|
|
|
|
if __types.has_key (type):
|
|
raise BaseException ('Type "%s" is already registered.' % type)
|
|
|
|
entry = {}
|
|
entry ['base'] = base_type
|
|
entry ['derived'] = []
|
|
entry ['scanner'] = None
|
|
__types [type] = entry
|
|
|
|
if base_type:
|
|
__types [base_type]['derived'].append (type)
|
|
|
|
if len (suffixes) > 0:
|
|
# Generated targets of 'type' will use the first of 'suffixes'
|
|
# (this may be overriden)
|
|
set_generated_target_suffix (type, [], suffixes [0])
|
|
|
|
# Specify mapping from suffixes to type
|
|
register_suffixes (suffixes, type)
|
|
|
|
feature.extend('target-type', [type])
|
|
feature.extend('main-target-type', [type])
|
|
feature.extend('base-target-type', [type])
|
|
|
|
if base_type:
|
|
feature.compose ('<target-type>' + type, replace_grist (base_type, '<base-target-type>'))
|
|
feature.compose ('<base-target-type>' + type, '<base-target-type>' + base_type)
|
|
|
|
# FIXME: resolving recursive dependency.
|
|
from b2.manager import get_manager
|
|
get_manager().projects().project_rules().add_rule_for_type(type)
|
|
|
|
def register_suffixes (suffixes, type):
|
|
""" Specifies that targets with suffix from 'suffixes' have the type 'type'.
|
|
If a different type is already specified for any of syffixes, issues an error.
|
|
"""
|
|
for s in suffixes:
|
|
if __suffixes_to_types.has_key (s):
|
|
old_type = __suffixes_to_types [s]
|
|
if old_type != type:
|
|
raise BaseException ('Attempting to specify type for suffix "%s"\nOld type: "%s", New type "%s"' % (s, old_type, type))
|
|
else:
|
|
__suffixes_to_types [s] = type
|
|
|
|
def registered (type):
|
|
""" Returns true iff type has been registered.
|
|
"""
|
|
return __types.has_key (type)
|
|
|
|
def validate (type):
|
|
""" Issues an error if 'type' is unknown.
|
|
"""
|
|
if not registered (type):
|
|
raise BaseException ("Unknown target type '%s'" % type)
|
|
|
|
def set_scanner (type, scanner):
|
|
""" Sets a scanner class that will be used for this 'type'.
|
|
"""
|
|
validate (type)
|
|
__types [type]['scanner'] = scanner
|
|
|
|
def get_scanner (type, prop_set):
|
|
""" Returns a scanner instance appropriate to 'type' and 'property_set'.
|
|
"""
|
|
if registered (type):
|
|
scanner_type = __types [type]['scanner']
|
|
if scanner_type:
|
|
return scanner.get (scanner_type, prop_set.raw ())
|
|
pass
|
|
|
|
return None
|
|
|
|
def all_bases (type):
|
|
""" Returns type and all of its bases, in the order of their distance from type.
|
|
"""
|
|
result = []
|
|
while type:
|
|
result.append (type)
|
|
type = __types [type]['base']
|
|
|
|
return result
|
|
|
|
def all_derived (type):
|
|
""" Returns type and all classes that derive from it, in the order of their distance from type.
|
|
"""
|
|
result = [type]
|
|
for d in __types [type]['derived']:
|
|
result.extend (all_derived (d))
|
|
|
|
return result
|
|
|
|
def is_derived (type, base):
|
|
""" Returns true if 'type' is 'base' or has 'base' as its direct or indirect base.
|
|
"""
|
|
# TODO: this isn't very efficient, especially for bases close to type
|
|
if base in all_bases (type):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def is_subtype (type, base):
|
|
""" Same as is_derived. Should be removed.
|
|
"""
|
|
# TODO: remove this method
|
|
return is_derived (type, base)
|
|
|
|
def set_generated_target_suffix (type, properties, suffix):
|
|
""" Sets a target suffix that should be used when generating target
|
|
of 'type' with the specified properties. Can be called with
|
|
empty properties if no suffix for 'type' was specified yet.
|
|
This does not automatically specify that files 'suffix' have
|
|
'type' --- two different types can use the same suffix for
|
|
generating, but only one type should be auto-detected for
|
|
a file with that suffix. User should explicitly specify which
|
|
one.
|
|
|
|
The 'suffix' parameter can be empty string ("") to indicate that
|
|
no suffix should be used.
|
|
"""
|
|
set_generated_target_ps(1, type, properties, suffix)
|
|
|
|
|
|
|
|
def change_generated_target_suffix (type, properties, suffix):
|
|
""" Change the suffix previously registered for this type/properties
|
|
combination. If suffix is not yet specified, sets it.
|
|
"""
|
|
change_generated_target_ps(1, type, properties, suffix)
|
|
|
|
def generated_target_suffix(type, properties):
|
|
return generated_target_ps(1, type, properties)
|
|
|
|
# Sets a target prefix that should be used when generating targets of 'type'
|
|
# with the specified properties. Can be called with empty properties if no
|
|
# prefix for 'type' has been specified yet.
|
|
#
|
|
# The 'prefix' parameter can be empty string ("") to indicate that no prefix
|
|
# should be used.
|
|
#
|
|
# Usage example: library names use the "lib" prefix on unix.
|
|
def set_generated_target_prefix(type, properties, prefix):
|
|
set_generated_target_ps(0, type, properties, prefix)
|
|
|
|
# Change the prefix previously registered for this type/properties combination.
|
|
# If prefix is not yet specified, sets it.
|
|
def change_generated_target_prefix(type, properties, prefix):
|
|
change_generated_target_ps(0, type, properties, prefix)
|
|
|
|
def generated_target_prefix(type, properties):
|
|
return generated_target_ps(0, type, properties)
|
|
|
|
def set_generated_target_ps(is_suffix, type, properties, val):
|
|
properties.append ('<target-type>' + type)
|
|
__prefixes_suffixes[is_suffix].insert (properties, val)
|
|
|
|
def change_generated_target_ps(is_suffix, type, properties, val):
|
|
properties.append ('<target-type>' + type)
|
|
prev = __prefixes_suffixes[is_suffix].find_replace(properties, val)
|
|
if not prev:
|
|
set_generated_target_ps(is_suffix, type, properties, val)
|
|
|
|
# Returns either prefix or suffix (as indicated by 'is_suffix') that should be used
|
|
# when generating a target of 'type' with the specified properties.
|
|
# If no prefix/suffix is specified for 'type', returns prefix/suffix for
|
|
# base type, if any.
|
|
def generated_target_ps_real(is_suffix, type, properties):
|
|
|
|
result = ''
|
|
found = False
|
|
while type and not found:
|
|
result = __prefixes_suffixes[is_suffix].find (['<target-type>' + type] + properties)
|
|
|
|
# Note that if the string is empty (""), but not null, we consider
|
|
# suffix found. Setting prefix or suffix to empty string is fine.
|
|
if result:
|
|
found = True
|
|
|
|
type = __types [type]['base']
|
|
|
|
if not result:
|
|
result = ''
|
|
return result
|
|
|
|
def generated_target_ps(is_suffix, type, prop_set):
|
|
""" Returns suffix that should be used when generating target of 'type',
|
|
with the specified properties. If not suffix were specified for
|
|
'type', returns suffix for base type, if any.
|
|
"""
|
|
key = str(is_suffix) + type + str(prop_set)
|
|
v = __target_suffixes_cache.get (key, None)
|
|
|
|
if not v:
|
|
v = generated_target_ps_real(is_suffix, type, prop_set.raw())
|
|
__target_suffixes_cache [key] = v
|
|
|
|
return v
|
|
|
|
def type(filename):
|
|
""" Returns file type given it's name. If there are several dots in filename,
|
|
tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and
|
|
"so" will be tried.
|
|
"""
|
|
while 1:
|
|
filename, suffix = os.path.splitext (filename)
|
|
if not suffix: return None
|
|
suffix = suffix[1:]
|
|
|
|
if __suffixes_to_types.has_key(suffix):
|
|
return __suffixes_to_types[suffix]
|
|
|
|
# NOTE: moved from tools/types/register
|
|
def register_type (type, suffixes, base_type = None, os = []):
|
|
""" Register the given type on the specified OSes, or on remaining OSes
|
|
if os is not specified. This rule is injected into each of the type
|
|
modules for the sake of convenience.
|
|
"""
|
|
if registered (type):
|
|
return
|
|
|
|
if not os or os_name () in os:
|
|
register (type, suffixes, base_type)
|