haiku/build/jam/MiscRules
Ingo Weinhold 014eed80e2 Change repository directory layout
* Under the base URL there are supposed to be the repository files and a
  subdirectory "packages".
* Fix the repository URL related confusion introduced earlier. The URL
in
  the repository info (and thus in the repository file) is supposed to
  be the base URL for the repository. It is not a (potentially)
  different base URL for the package files. Package and repository
  files were supposed to live in the same directory. Now, by requiring
  the package files to live in a subdirectory -- which can also be a
  symlink -- we gain some flexibility.
  The URL in the repository config is usually the same as the in the
  repository info, unless it refers to a mirror site. This allows for
  mirrors to copy the original repository verbatim.
* Remove the PackageURL rule and introduce a DownloadPackage rule
  instead. The URL for a package file cannot be computed in the jam
  parsing phase anymore, as it contains the hash value of the package
  list.
* BRepositoryConfig: Add PackagesURL() for convenience.
2013-07-06 18:06:02 +02:00

756 lines
22 KiB
Plaintext

rule SetupObjectsDir
{
# SetupObjectsDir
#
# Internal rule used to set up the *{LOCATE,SEARCH}*_{TARGET,SOURCE}
# variables for the current directory.
local relPath = [ FDirName $(SUBDIR_TOKENS[2-]) ] ;
if $(relPath) = . {
relPath = ;
}
COMMON_PLATFORM_LOCATE_TARGET =
[ FDirName $(HAIKU_COMMON_PLATFORM_OBJECT_DIR) $(relPath) ] ;
local var ;
for var in COMMON_ARCH COMMON_DEBUG DEBUG_$(HAIKU_DEBUG_LEVELS) {
HOST_$(var)_LOCATE_TARGET
= [ FDirName $(HOST_$(var)_OBJECT_DIR) $(relPath) ] ;
TARGET_$(var)_LOCATE_TARGET
= [ FDirName $(TARGET_$(var)_OBJECT_DIR) $(relPath) ] ;
}
LOCATE_TARGET = $(COMMON_PLATFORM_LOCATE_TARGET) ;
LOCATE_SOURCE = $(LOCATE_TARGET) ;
SEARCH_SOURCE = $(SUBDIR) $(LOCATE_SOURCE)
$(HOST_COMMON_DEBUG_LOCATE_TARGET) # Also add the standard output
$(TARGET_COMMON_DEBUG_LOCATE_TARGET) # dirs for generated sources.
;
}
rule SetupFeatureObjectsDir feature
{
# SetupFeatureObjectsDir <feature>
#
# Updates the *{LOCATE,SEARCH}*_{TARGET,SOURCE} variables for the current
# directory appending a <feature> to each of them. Note that it resets
# the LOCATE_TARGET, LOCATE_SOURCE, SEARCH_SOURCE (!) variables. I.e. it
# should be invoked before customizing these variables further (e.g. like
# adding additional source directories to SEARCH_SOURCE).
COMMON_PLATFORM_LOCATE_TARGET
= [ FDirName $(COMMON_PLATFORM_LOCATE_TARGET) $(feature) ] ;
local var ;
for var in COMMON_ARCH COMMON_DEBUG DEBUG_$(HAIKU_DEBUG_LEVELS) {
HOST_$(var)_LOCATE_TARGET
= [ FDirName $(HOST_$(var)_LOCATE_TARGET) $(feature) ] ;
TARGET_$(var)_LOCATE_TARGET
= [ FDirName $(TARGET_$(var)_LOCATE_TARGET) $(feature) ] ;
}
LOCATE_TARGET = [ FDirName $(LOCATE_TARGET) $(feature) ] ;
LOCATE_SOURCE = $(LOCATE_TARGET) ;
SEARCH_SOURCE = $(SUBDIR) $(LOCATE_SOURCE)
$(HOST_COMMON_DEBUG_LOCATE_TARGET) # Also add the standard output
$(TARGET_COMMON_DEBUG_LOCATE_TARGET) # dirs for generated sources.
;
}
rule SubIncludeGPL
{
# SubInclude rule that can be used to conditionally include GPL licensed
# add-ons
if $(HAIKU_INCLUDE_GPL_ADDONS) = 1 {
SubInclude $(1) ;
}
}
# pragma mark - MakeLocate variants
rule MakeLocateCommonPlatform
{
# The file is shared between all target platforms.
MakeLocate $(1) : $(COMMON_PLATFORM_LOCATE_TARGET) ;
}
rule MakeLocatePlatform
{
# The file is specific for the target platform, but
# architecture independent. Usually the right rule for generated
# sources, though sometimes sources can be architecture specific.
local files = $(1) ;
local file ;
for file in $(files) {
if [ on $(file) return $(PLATFORM) ] = host {
MakeLocate $(file) : $(HOST_COMMON_ARCH_LOCATE_TARGET) ;
} else {
MakeLocate $(file) : $(TARGET_COMMON_ARCH_LOCATE_TARGET) ;
}
}
}
rule MakeLocateArch
{
# The file is platform+architecture specific, but is debug
# level independent. This is usually the right rule for generated
# architecture specific data or source files.
local files = $(1) ;
local file ;
for file in $(files) {
if [ on $(file) return $(PLATFORM) ] = host {
MakeLocate $(file) : $(HOST_COMMON_DEBUG_LOCATE_TARGET) ;
} else {
MakeLocate $(file) : $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
}
}
}
rule MakeLocateDebug
{
# The file is platform+architecture+debug level specific.
# That's what should be used for compiled code.
local files = $(1) ;
local file ;
for file in $(files) {
on $(file) {
if $(PLATFORM) = host {
MakeLocate $(file) : $(HOST_DEBUG_$(DEBUG)_LOCATE_TARGET) ;
} else {
MakeLocate $(file) : $(TARGET_DEBUG_$(DEBUG)_LOCATE_TARGET) ;
}
}
}
}
# pragma mark - Deferred SubIncludes
# The variable used to collect the deferred SubIncludes.
HAIKU_DEFERRED_SUB_INCLUDES = ;
rule DeferredSubInclude params : jamfile : scope
{
# DeferredSubInclude <subdir tokens> [ : <jamfile name> [ : <scope> ] ] ;
#
# Takes the same directory tokens parameter as SubInclude plus an optional
# alternative Jamfile name. The the subdirectory referred to by
# <subdir tokens> will be included when ExecuteDeferredSubIncludes is
# invoked, i.e. at the end of the root Jamfile. The <jamfile name> parameter
# specifies the name of the Jamfile to include. By default it is "Jamfile".
# The <scope> parameter can be "global" (default) or "local", specifying
# whether the alternative Jamfile name shall also be used for subdirectories.
HAIKU_DEFERRED_SUB_INCLUDES += "/" $(params) ;
if $(jamfile) {
SetConfigVar JAMFILE : $(params) : $(jamfile) : $(scope) ;
}
}
rule ExecuteDeferredSubIncludes
{
# ExecuteDeferredSubIncludes ;
#
# Performs the deferred SubIncludes scheduled by DeferredSubInclude.
local tokensList = $(HAIKU_DEFERRED_SUB_INCLUDES) ;
while $(tokensList) {
# chop off leading "/"
tokensList = $(tokensList[2-]) ;
# get the tokens for the next include
local tokens ;
while $(tokensList) && $(tokensList[1]) != "/" {
tokens += $(tokensList[1]) ;
tokensList = $(tokensList[2-]) ;
}
# perform the include
if $(tokens) {
SubInclude $(tokens) ;
}
}
}
rule HaikuSubInclude tokens
{
# HaikuSubInclude <tokens> ;
#
# Current subdir relative SubInclude.
# <tokens> - subdir tokens specifying the subdirectory to be include
# (relative to the current subdir)
if $(tokens) {
SubInclude HAIKU_TOP $(SUBDIR_TOKENS) $(tokens) ;
}
}
# pragma mark - Unique IDs/targets
# private to NextID; incremented with each NextID invocation
HAIKU_NEXT_ID = 0 ;
rule NextID
{
# NextID ;
local result = $(HAIKU_NEXT_ID:J=) ;
HAIKU_NEXT_ID = [ AddNumAbs $(HAIKU_NEXT_ID) : 1 ] ;
return $(result) ;
}
rule NewUniqueTarget basename
{
# NewUniqueTarget [ basename ] ;
local id = [ NextID ] ;
return $(basename[1]:E=_target:G=unique!target)_$(id) ;
}
# pragma mark - RunCommandLine
rule RunCommandLine commandLine
{
# RunCommandLine <commandLine>
#
# Creates a pseudo target that, when made by jam, causes the supplied shell
# command line to be executed. Elements of <commandLine> with the prefix ":"
# are replaced by the rule. After stripping the prefix such a string specifies
# a build system target and the finally executed command line will contain
# a path to the target instead.
# The pseudo target will depend on all targets thus identified. Each
# invocation of this rule creates a different pseudo target, which is
# returned to the caller.
# collect the targets in the command line and replace them by $targetX*
# variables
local substitutedCommandLine ;
local targets ;
local targetVarName = target ;
local i ;
for i in $(commandLine) {
# targets are marked by the ":" prefix
local target = [ Match ^:(.*) : $(i) ] ;
if $(target) {
targets += $(target) ;
targetVarName = $(targetVarName)X ;
i = "$"$(targetVarName) ;
}
substitutedCommandLine += $(i) ;
}
# define the "run" target
local run = [ NewUniqueTarget run ] ;
COMMAND_LINE on $(run) = $(substitutedCommandLine) ;
NotFile $(run) ;
Always $(run) ;
Depends $(run) : $(targets) ;
RunCommandLine1 $(run) : $(targets) ;
return $(run) ;
}
actions RunCommandLine1 {
target=target;
for t in $(2) ; do
target=${target}X
eval "${target}=${t}"
done
$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)
"$(COMMAND_LINE)"
}
#pragma mark - DefineBuildProfile
rule DefineBuildProfile name : type : path {
# DefineBuildProfile <name> : <type> [ : <path> ]
#
# Makes a build profile known. Build profiles can be used to define
# different sets of settings for Haiku images/installations. For each
# profile the default actions "build", "update", and "mount" (the latter
# only for disks or image types) will be available (i.e. can be specified
# as second parameter on the jam command line). They will build an image
# or installation, update only given targets, respectively just mount the
# image or disk using the bfs_shell.
#
# <name> - The name of the build profile.
# <type> - The type of the build profile. Must be one of "image" (plain
# disk image), "anyboot-image" (custom disk image that can be
# written to CD or disk device), "cd-image" (ISO CD image),
# "vmware-image" (VMware disk image), "disk" (actual partition
# or hard disk device), "install" (installation in a directory),
# or "custom" (user-defined).
# <path> - The path associated with the profile. Depending on the profile
# type, this is the path to the disk image/VMware image, hard
# disk/partition device, or the installation directory. If the
# parameter is omitted, the value of the HAIKU[_VMWARE]_IMAGE_NAME,
# HAIKU_IMAGE_DIR, respectively HAIKU_INSTALL_DIR or their default
# values will be used instead.
if [ on $(name) return $(HAIKU_BUILD_PROFILE_SPECIFIED) ] {
Exit "ERROR: Build profile \"$(name)\" defined twice!" ;
}
HAIKU_BUILD_PROFILE_SPECIFIED on $(name) = 1 ;
if ! $(HAIKU_BUILD_PROFILE) || $(HAIKU_BUILD_PROFILE) != $(name) {
return ;
}
HAIKU_BUILD_PROFILE_DEFINED = 1 ;
# split path into directory path and name
local targetDir = $(path:D) ;
local targetName = $(path:BS) ;
# Jam's path splitting produces an empty string, if a component doesn't
# exist. That's a little unhandy for checks.
if $(targetDir) = "" {
targetDir = ;
}
if $(targetName) = "" {
targetName = ;
}
targetDir ?= $(HAIKU_IMAGE_DIR) ;
targetDir ?= $(HAIKU_DEFAULT_IMAGE_DIR) ;
# "disk" is "image" with HAIKU_DONT_CLEAR_IMAGE
if $(type) = "disk" {
type = "image" ;
HAIKU_DONT_CLEAR_IMAGE = 1 ;
}
local buildTarget ;
local startOffset ;
switch $(type) {
case "anyboot-image" : {
targetName ?= $(HAIKU_ANYBOOT_NAME) ;
targetName ?= $(HAIKU_DEFAULT_ANYBOOT_NAME) ;
HAIKU_ANYBOOT_DIR = $(targetDir) ;
HAIKU_ANYBOOT_NAME = $(targetName) ;
buildTarget = haiku-anyboot-image ;
}
case "cd-image" : {
targetName ?= $(HAIKU_CD_NAME) ;
targetName ?= $(HAIKU_DEFAULT_CD_NAME) ;
HAIKU_CD_DIR = $(targetDir) ;
HAIKU_CD_NAME = $(targetName) ;
buildTarget = haiku-cd ;
}
case "image" : {
targetName ?= $(HAIKU_IMAGE_NAME) ;
targetName ?= $(HAIKU_DEFAULT_IMAGE_NAME) ;
HAIKU_IMAGE_DIR = $(targetDir) ;
HAIKU_IMAGE_NAME = $(targetName) ;
buildTarget = haiku-image ;
}
case "vmware-image" : {
targetName ?= $(HAIKU_VMWARE_IMAGE_NAME) ;
targetName ?= $(HAIKU_DEFAULT_VMWARE_IMAGE_NAME) ;
HAIKU_IMAGE_DIR = $(targetDir) ;
HAIKU_VMWARE_IMAGE_NAME = $(targetName) ;
buildTarget = haiku-vmware-image ;
startOffset = --start-offset 65536 ;
}
case "install" : {
path ?= $(HAIKU_INSTALL_DIR) ;
path ?= $(HAIKU_DEFAULT_INSTALL_DIR) ;
HAIKU_INSTALL_DIR = $(path) ;
buildTarget = install-haiku ;
}
case "custom" : {
# user-defined -- don't do anything
return 1 ;
}
case * : {
Exit "Unsupported build profile type: " $(type) ;
}
}
switch $(HAIKU_BUILD_PROFILE_ACTION) {
case "build" : {
JAM_TARGETS = $(buildTarget) ;
}
case "update" : {
JAM_TARGETS = $(buildTarget) ;
SetUpdateHaikuImageOnly 1 ;
HAIKU_PACKAGES_UPDATE_ONLY = 1 ;
HAIKU_INCLUDE_IN_IMAGE on $(HAIKU_BUILD_PROFILE_PARAMETERS) = 1 ;
HAIKU_INCLUDE_IN_PACKAGES on $(HAIKU_BUILD_PROFILE_PARAMETERS) = 1 ;
}
case "update-all" : {
JAM_TARGETS = $(buildTarget) ;
SetUpdateHaikuImageOnly 1 ;
HAIKU_PACKAGES_UPDATE_ONLY = 1 ;
HAIKU_INCLUDE_IN_IMAGE = 1 ;
HAIKU_INCLUDE_IN_PACKAGES = 1 ;
}
case "update-packages" : {
JAM_TARGETS = $(buildTarget) ;
SetUpdateHaikuImageOnly 1 ;
HAIKU_UPDATE_ALL_PACKAGES = 1 ;
}
case "mount" : {
if $(type) in "install" "cd-image" {
Exit "Build action \"mount\" not supported for profile type"
"\"$(type)\"." ;
}
local commandLine = :<build>bfs_shell $(startOffset)
\"$(targetName:D=$(targetDir))\" ;
JAM_TARGETS = [ RunCommandLine $(commandLine) ] ;
}
}
return 1 ;
}
# pragma mark - Build Features
rule FIsBuildFeatureEnabled feature
{
# FIsBuildFeatureEnabled <feature> ;
# Returns whether the given build feature <feature> is enabled (if so: "1",
# if not: empty list).
#
# <feature> - The name of the build feature (all lower case).
if $(feature) in $(HAIKU_BUILD_FEATURES) {
return 1 ;
}
return ;
}
rule FMatchesBuildFeatures specification
{
# FMatchesBuildFeatures <specification> ;
# Returns whether the given build feature specification <specification>
# holds. <specification> consists of positive and negative build feature
# conditions. Conditions can be individual list elements and multiple
# conditions can be joined, separated by ",", in a single list element. The
# effect is the same; they are considered as single set of conditions.
# A positive condition does not start with a "!". It holds when the build
# feature named by the element is enabled.
# A negative condition starts with a "!". It holds when the build feature
# named by the string resulting from removing the leading "!" is not
# enabled.
# <specification> holds when it is not empty and
# * none of the negative conditions it contains hold and
# * if it contains any positive conditions, at least one one of them holds.
#
# <specification> - The build feature specification. A list of individual
# conditions or conditions joined by ",".
local splitSpecification ;
local element ;
for element in $(specification) {
splitSpecification += [ FSplitString $(element) : "," ] ;
}
return [ FSetConditionsHold $(splitSpecification)
: $(HAIKU_BUILD_FEATURES) ] ;
}
rule FFilterByBuildFeatures list
{
# FFilterByBuildFeatures <list> ;
# Filters the list annotated by build feature specifications and returns the
# resulting list. The list can be annotated in two different ways:
# * A single list element can be annotated by appending "@<specification>"
# to it, with <specification> being a build feature specification (a
# single comma-separated string). The element appears in the resulting
# list, only if <specification> holds.
# * A sublist can be annotated by enclosing it in "<specification> @{" (two
# separate list elements) and "}@", with <specification> being a build
# feature specification (a single comma-separated string). The enclosed
# sublist appears in the resulting list, only if <specification> holds.
# The sublist annotations can be nested. The annotations themselves don't
# appear in the resulting list.
#
# <list> - A list annotated with build feature specifications.
local filteredList ;
# Since we must look ahead one element to be able to decide whether an
# element is a regular list element or a features specification for a
# subsequent "@{". Hence we always process an element other than "@{" and
# "}@" in the next iteration. We append a dummy element to the list so we
# don't need special handling for the last element.
local evaluationStack = 1 ;
local previousElement ;
local element ;
for element in $(list) dummy {
local stackTop = $(evaluationStack[1]) ;
local processElement = $(previousElement) ;
switch $(element) {
case }@ :
{
# Pop the topmost specification off the stack.
evaluationStack = $(evaluationStack[2-]) ;
if ! $(evaluationStack) {
Exit "FFilterByBuildFeatures: Unbalanced @( in: " $(list) ;
}
processElement = $(previousElement) ;
previousElement = ;
}
case @{ :
{
if ! $(previousElement) {
Exit "FFilterByBuildFeatures: No feature specification"
"after )@ in: " $(list) ;
}
if $(evaluationStack[1]) = 1
&& [ FMatchesBuildFeatures $(previousElement) ] {
evaluationStack = 1 $(evaluationStack) ;
} else {
evaluationStack = 0 $(evaluationStack) ;
}
processElement = ;
previousElement = ;
}
case * :
{
processElement = $(previousElement) ;
previousElement = $(element) ;
}
}
if $(processElement) && $(stackTop) = 1 {
local splitElement = [ Match "(.*)@([^@]*)" : $(processElement) ] ;
if $(splitElement) {
if [ FMatchesBuildFeatures $(splitElement[2]) ] {
filteredList += $(splitElement[1]) ;
}
} else {
filteredList += $(processElement) ;
}
}
}
if $(evaluationStack[2-]) {
Exit "FFilterByBuildFeatures: Unbalanced )@ in: " $(list) ;
}
return $(filteredList) ;
}
rule EnableBuildFeatures features : specification
{
# EnableBuildFeatures <features> : <specification> ;
# Enables the build features <features>, if the build features specification
# <specification> holds. If <specification> is omitted, the features are
# enabled unconditionally.
# The rule enables a build feature by adding its given lower case name to
# the build variable HAIKU_BUILD_FEATURES and defining a build variable
# HAIKU_BUILD_FEATURE_<FEATURE>_ENABLED (<FEATURE> being the upper case name
# of the build feature) to "1".
#
# <features> - A list of build feature names (lower case).
# <specification> - An optional build features specification (cf.
# FMatchesBuildFeatures).
if ! $(specification)
|| [ FMatchesBuildFeatures $(specification)
: $(HAIKU_BUILD_FEATURES) ] {
local feature ;
for feature in $(features) {
HAIKU_BUILD_FEATURES += $(feature) ;
HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED = 1 ;
}
}
}
rule BuildFeatureObject feature
{
# BuildFeatureObject <feature> ;
# Returns a unique object for the given build feature. It is used for
# attaching attribute values to it.
local featureObject = $(HAIKU_BUILD_FEATURE_$(feature:U)) ;
if ! $(featureObject) {
featureObject = [ NewUniqueTarget ] ;
HAIKU_BUILD_FEATURE_$(feature:U) = $(featureObject) ;
}
return $(featureObject) ;
}
rule SetBuildFeatureAttribute feature : attribute : values : package
{
# SetBuildFeatureAttribute <feature> : <attribute> : <values>
# [ : <package> ] ;
# Sets attribute <attribute> of a build feature <feature> to value <values>.
# If <package> is specified, it names the package the attribute belongs to.
local featureObject = [ BuildFeatureObject $(feature) ] ;
HAIKU_ATTRIBUTE_$(attribute) on $(featureObject) = $(values) ;
if $(package) {
HAIKU_ATTRIBUTE_$(attribute):package on $(featureObject) = $(package) ;
}
}
rule BuildFeatureAttribute feature : attribute : flags
{
# BuildFeatureAttribute <feature> : <attribute> [ : <flags> ] ;
# Returns the value of attribute <attribute> of a build feature <feature>.
# Flags can be a list of flags which influence the returned value. Currently
# only flag "path" is defined, which will convert the attribute value --
# which is assumed to be a list of (gristed) targets with a path relative to
# the extraction directory of the build feature archive files -- to paths.
# A typical example is the "headers" attribute, whose value can be used as
# dependency, but which must be converted to a path to be a valid headers
# search path.
local featureObject = [ BuildFeatureObject $(feature) ] ;
local values
= [ on $(featureObject) return $(HAIKU_ATTRIBUTE_$(attribute)) ] ;
if path in $(flags) {
# get the attribute's package and the corresponding extraction dir
local package
= [ BuildFeatureAttribute $(feature) : $(attribute):package ] ;
local directory ;
if $(package) {
directory = [ BuildFeatureAttribute $(feature)
: $(package):directory ] ;
}
# translate the values
local paths ;
local value ;
for value in $(values:G=) {
paths += [ FDirName $(directory) $(value) ] ;
}
values = $(paths) ;
}
return $(values) ;
}
rule ExtractBuildFeatureArchives feature : list
{
# ExtractBuildFeatureArchives <feature> : <list> ;
# Downloads and extracts one or more archives for build feature <feature>
# and sets attributes for the build feature to extracted entries. The syntax
# for <list> is:
# "file:" <packageName> <url>
# <attribute>: <value> ...
# ...
# "file:" <packageName2> <url2>
# ...
#
# <packageName> specifies a short name for the archive (e.g. "base" for the
# base package, "devel" for the development package, etc.), <url> the URL
# from which to download the file. <attribute> and be any name and <value>
# any relative path in the extraction directory.
#
# The attribute with the name "depends" will be handled specially. Its
# <value> specifies the name of a package the current package depends on
# (e.g. "devel" typically depends on "base"). If such a dependency is
# specified the current package will be extracted to the same directory as
# the package it depends on. The "depends" attribute must precede any other
# attribute for the package.
#
# The rule also sets the build feature attribute "<packageName>:directory"
# to the extraction directory for each package.
while $(list) {
if $(list[1]) != file: {
Exit "ExtractBuildFeatureArchives: Expected \"file: ...\", got:"
$(list) ;
}
local package = $(list[2]) ;
local file = [ DownloadPackage $(list[3]) ] ;
local fileName = $(file:BS) ;
list = $(list[4-]) ;
local directory = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR)
$(fileName:B) ] ;
directory = $(directory:G=$(package)) ;
while $(list) {
local attribute = [ Match "(.*):" : $(list[1]) ] ;
if ! $(attribute) {
Exit "ExtractBuildFeatureArchives: Expected attribute, got:"
$(list) ;
}
if $(attribute) = file {
break ;
}
list = $(list[2-]) ;
local values ;
while $(list) {
switch $(list[1]) {
case *: :
{
break ;
}
case * :
{
values += $(list[1]) ;
list = $(list[2-]) ;
}
}
}
if $(attribute) = depends {
# Inherit the extraction directory (with a different grist) and
# depend on the extraction directory of the base package.
local basePackage = $(values[1]) ;
local baseDirectory = [ BuildFeatureAttribute $(feature)
: $(basePackage):directory ] ;
directory = $(baseDirectory:G=$(package)) ;
Depends $(directory) : $(directory:G=$(basePackage)) ;
} else {
SetBuildFeatureAttribute $(feature) : $(attribute)
: [ ExtractArchive $(directory)
: $(values) : $(file)
: extracted-$(feature)-$(package) ] ;
SetBuildFeatureAttribute $(feature) : $(attribute):package
: $(package) ;
}
}
SetBuildFeatureAttribute $(feature) : $(package):directory
: $(directory:G=) ;
}
}