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 # # Updates the *{LOCATE,SEARCH}*_{TARGET,SOURCE} variables for the current # directory appending a 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 [ : [ : ] ] ; # # Takes the same directory tokens parameter as SubInclude plus an optional # alternative Jamfile name. The the subdirectory referred to by # will be included when ExecuteDeferredSubIncludes is # invoked, i.e. at the end of the root Jamfile. The parameter # specifies the name of the Jamfile to include. By default it is "Jamfile". # The 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 ; # # Current subdir relative SubInclude. # - 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 # # Creates a pseudo target that, when made by jam, causes the supplied shell # command line to be executed. Elements of 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 : [ : ] # # 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. # # - The name of the build profile. # - 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). # - 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 = :bfs_shell $(startOffset) \"$(targetName:D=$(targetDir))\" ; JAM_TARGETS = [ RunCommandLine $(commandLine) ] ; } } return 1 ; } # pragma mark - Build Features rule FIsBuildFeatureEnabled feature { # FIsBuildFeatureEnabled ; # Returns whether the given build feature is enabled (if so: "1", # if not: empty list). # # - The name of the build feature (all lower case). if $(feature) in $(HAIKU_BUILD_FEATURES) { return 1 ; } return ; } rule FMatchesBuildFeatures specification { # FMatchesBuildFeatures ; # Returns whether the given build feature specification # holds. 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. # 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. # # - 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 ; # 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 "@" # to it, with being a build feature specification (a # single comma-separated string). The element appears in the resulting # list, only if holds. # * A sublist can be annotated by enclosing it in " @{" (two # separate list elements) and "}@", with being a build # feature specification (a single comma-separated string). The enclosed # sublist appears in the resulting list, only if holds. # The sublist annotations can be nested. The annotations themselves don't # appear in the resulting 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 : ; # Enables the build features , if the build features specification # holds. If 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__ENABLED ( being the upper case name # of the build feature) to "1". # # - A list of build feature names (lower case). # - 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 ; # 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 : : # [ : ] ; # Sets attribute of a build feature to value . # If 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 : [ : ] ; # Returns the value of attribute of a build 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 : ; # Downloads and extracts one or more archives for build feature # and sets attributes for the build feature to extracted entries. The syntax # for is: # "file:" # : ... # ... # "file:" # ... # # specifies a short name for the archive (e.g. "base" for the # base package, "devel" for the development package, etc.), the URL # from which to download the file. and be any name and # any relative path in the extraction directory. # # The attribute with the name "depends" will be handled specially. Its # 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 ":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 url = $(list[3]) ; local fileName = $(url:BS) ; list = $(list[4-]) ; local file = [ DownloadFile $(fileName) : $(url) ] ; 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=) ; } }