haiku/build/jam/MiscRules

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=) ;
}
}