# Jam build system for Genode:
# This is a (very partial) re-implementation of the gmake-style
# Genode build system, using Jam/MR instead. The reason for re-inventing
# this particular wheel is to speed up HoG builds and make their configuration
# more flexible and refactored. This is mainly of use for the "haiku-on-gendode"
# project, though the below is somewhat generic, in that the HoG specific stuff
# is mainly in jamrules-qemu-hog instead of this file.



# We maintain a unique/common instance of the "boot/grub/" folder and
# symlink to it from the actual run build folders, saves a few hundred KBs:
GenodeGrubTemplate = $(GenodeBuild)/var/run/_common_/boot/grub ;



#pragma mark - back-end -


##################
# Back-end rules # (see bottom for front-end rules)
##################


# !
	newline = "
" ;
# ~!


# silence jam...
actions MakeGrubFolder
{
}

rule MakeGrubFolder  where : isofile : scenario
{
#xx needs an MkDir-style "cheesy gate"..? copies are performed twice, when initializing the GRUB folder template..
	# eltorito
	#
	Depends $(isofile) : eltorito.img ;
	MakeLocate eltorito.img : $(where)/i386-pc ;
	File eltorito.img : $(GenodeRepos)/../contrib/$(Contrib_grub2)/boot/grub2/eltorito.img ;
	
	# grub mods
	#
	local grub_mods = [ GLOB $(GenodeRepos)/../contrib/$(Contrib_grub2)/boot/grub2 : *.mod ] ;
	local
		file target
		;
	for file in $(grub_mods)
	{
		target = $(file:D=) ;
		
		Depends $(isofile) : $(target) ;
		MakeLocate $(target) : $(where)/i386-pc ;
		File $(target) : $(file) ;
	}
	
	# grub2 config file
	#
	
	Depends $(isofile) : grub.cfg ;
	MakeLocate grub.cfg : $(where) ;
	GrubConfig grub.cfg ;
}



# e.g. bin/hypervisor => var/run/hog-demos/genode/hypervisor => var/run/hog-demos/boot/hypervisor
actions Objcopy
{
	$(GccToolChain)/genode-x86-objcopy -O elf32-i386 $(2) $(1)
}

rule Objcopy  target : source
{
	Depends $(target) : $(source) ;
}


# If debugging is needed, add "cat $(1)" before "xmllint.." to display the contents of "config"
actions CheckXmlSyntax
{
	xmllint --noout $(1)
}


#///later: Genode ~23.02 has this leading line (is it used by EFI boot?) : "loadfont /boot/font.pf2" , can we do without ?

# (-: item "menuentry" could even say "Haiku on Genode on Nova" :-)
# item 'intel_hwp_balanced' can be set to 'intel_hwp_performance' (but arguably does more harm than good)
actions GrubConfig
{
	cat > "$(1)"  << --JAM-EOF--
set timeout=0
set gfxpayload=auto
menuentry 'Genode on NOVA' {
 insmod multiboot2
 insmod gzio
 multiboot2 /boot/bender  serial intel_hwp_balanced
 module2 /boot/hypervisor hypervisor iommu_intel iommu_amd serial
 module2 /boot/image.elf.gz image.elf
}
--JAM-EOF--
}


# write $(CONFIG) to file $(1), pre-pended with an appropriate header
# (180 capabilities are necessary to run e.g. Mandelbrot,
#  210 for Pulse if you open both the About box and the Settings window)
#
# -> use  180 caps when in qemu, to reveal resources leaks that may occur
# -> use 1180 (!) caps for running on bare metal/"production"
#
actions ConfigFile
{
	cat > "$(1)"  << --JAM-EOF--
<config verbose="no" prio_levels="4">
	<parent-provides>
		<!-- We're the root (top) init, so these are provided by core: -->
		<service name="ROM"/>
		<service name="RAM"/>
		<service name="IRQ"/>
		<service name="IO_MEM"/>
		<service name="IO_PORT"/>
		<service name="CAP"/>
		<service name="PD"/>
		<service name="RM"/>
		<service name="CPU"/>
		<service name="LOG"/>
		<service name="SIGNAL" />
		<service name="TRACE"/>
	</parent-provides>
	<default-route>
		<!--		<service name="Audio_out"> <child name="mixer"/> </service> -->
		<!-- Re. the above: actually, it makes no sense to have each and every component get some routing
			to Audio_out/Capture/etc. It's plenty enough to insert the below by default, i.e. provide the
			"standard" parent/any-child routing in those components whose config lacks a "route" section:
		 -->
		<any-service> <parent/> <any-child/> </any-service>
	</default-route>
	<default caps="210"/>

$(CONFIG)

</config>
--JAM-EOF--
}

rule ConfigFile  textfile
{
	#echo will create $(textfile) with: $(CONFIG) ;
	
	CheckXmlSyntax $(textfile) ;
#xxx Problem with the above: the config file is _removed_ in case of syntax error..:
#+++ use File to make a copy of "scenario.config" to "config", to keep a copy around ?
}


actions ImageCore
{
	# e.g. $(1) => var/run/hog-demos.boot_modules.o
	cd $(GenodeBuild) && \
		$(GccToolChain)/genode-x86-gcc -m64 -mcmodel=large  -c -x assembler \
			-o $(1) - << --JAM-EOF--
$(PAYLOAD)
--JAM-EOF--
}

rule ImageCore  target : binaries
{
	# call :B once here, and a second time
	# down there -- e.g. "hog-demos.boot_modules.o" => "hog-demos.boot_modules" => "hog-demos" (or we could just get the value passed as "scenario" argument?)
	local halfbasename = $(target:B) ;
	
	# jam by itself is unable to increment an integer from 1 to 100, sigh
	local counter = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 _EXHAUSTED_ ;
	
#+++	Depends $(target) : binaries!...  ;  #$(source) ;
	
	local out ;
	local address_type = ".quad" ;  # for x64 (for 32bit, would be ".long" instead)
	
	out += ".set MIN_PAGE_SIZE_LOG2, 12" $(newline) ;
	out += ".set DATA_ACCESS_ALIGNM_LOG2, 3" $(newline) ;
	out += $(newline) ;
	out += ".section .data" $(newline) ;
	out += $(newline) ;
	out += ".p2align DATA_ACCESS_ALIGNM_LOG2" $(newline) ;
	out += ".global _boot_modules_headers_begin" $(newline) ;
	out += "_boot_modules_headers_begin:" $(newline) ;
	out += $(newline) ;
	
#	for module in $(binaries)
	for i in $(counter)
	{
		if $(i) = _EXHAUSTED_
		{
			Echo "**** Exhausted counters **** (in ImageCore rule)" ;
			EXIT "**** Exhausted counters -- Edit jamrules-qemu, type up additional 'local counter' items, and try again ****" ;
		}
		
		# module = $(binaries[1]), [2] ..etc
		local module = $(binaries[$(i)]:D=) ;
		if ! $(module)
		{
			break ;
		}
		
		out += $(address_type) "_boot_module_"$(i)"_name" $(newline) ;
		out += $(address_type) "_boot_module_"$(i)"_begin" $(newline) ;
		out += $(address_type) "_boot_module_"$(i)"_end - _boot_module_"$(i)"_begin" $(newline) ;
		out += $(newline) ;
	}
	
	out += ".global _boot_modules_headers_end" $(newline) ;
	out += "_boot_modules_headers_end:" $(newline) ;
	out += $(newline) ;
	
	for i in $(counter)
	{
		local module = $(binaries[$(i)]:D=) ;
		if ! $(module)
		{
			break ;
		}
		
		out += ".p2align DATA_ACCESS_ALIGNM_LOG2" $(newline) ;
		out += "_boot_module_"$(i)"_name:" $(newline) ;
		out += ".string \""$(module)"\"" $(newline) ;
		out += ".byte 0" $(newline) ;
		out += $(newline) ;
	}
	
	out += ".section .data.boot_modules_binaries" $(newline) ;
	out += $(newline) ;
	out += ".global _boot_modules_binaries_begin" $(newline) ;
	out += "_boot_modules_binaries_begin:" $(newline) ;
	out += $(newline) ;
	
	for i in $(counter)
	{
		local module = $(binaries[$(i)]:D=) ;
		if ! $(module)
		{
			break ;
		}
		
		out += ".p2align MIN_PAGE_SIZE_LOG2" $(newline) ;
		out += "_boot_module_"$(i)"_begin:" $(newline) ;
		local path
			= "var/run/$(halfbasename:B)_genode" ;  # e.g. var/run/hog-demos/
		out += ".incbin \""$(path)/$(module)"\"" $(newline) ;
		out += "_boot_module_"$(i)"_end:" $(newline) ;
		out += $(newline) ;
	}
	
	out += ".p2align MIN_PAGE_SIZE_LOG2" $(newline) ;
	out += ".global _boot_modules_binaries_end" $(newline) ;
	out += "_boot_modules_binaries_end:" $(newline) ;
	
	#echo ----- $(out) ;
	
	PAYLOAD on $(1) = $(out) ;
}



# backported from Jam 2.6.1
rule GetTargetVar
{
	on $(<) { return $($(>)) ; }
}



# create image.elf at $(1) from target-specific list $(BOOTMODULESOBJ)
# and (hardcoded) "core-nova.a", lifted directly from the bin/ folder
	# ///ToDo: ++++ why not lift other binaries directly from bin, instead of using a copy (or symlink) of them in scenario-genode/ folder ? Indeed bin/ has the symlinks-to-stripped, libcache has the .abi.so files, no need to collect an interim indirection layer...
actions ElfImage
{
	# e.g. $(1) => var/run/hog-demos/image.elf
	$(GccToolChain)/genode-x86-g++ -m64 -mcmodel=large  -nostdlib \
		-Wl,-T -Wl,$(GenodeRepos)/base/src/ld/genode.ld \
		-Wl,$(GenodeRepos)/base-nova/src/core/core-bss.ld \
		-Wl,-z -Wl,max-page-size=0x1000  -Wl,-Ttext=0x100000 -Wl,-gc-sections  -Wl,-nostdlib \
		-Wl,--whole-archive -Wl,--start-group \
			$(GenodeBuild)/bin/core-nova.a \
			$(BOOTMODULESOBJ) \
		-Wl,--no-whole-archive  -Wl,--end-group \
		-o $(1) \
	&& \
		$(GccToolChain)/genode-x86-strip $(1)
#not needed ? Building an ISO image works just the same without this...:
#$(GccToolChain)/../lib/gcc/x86_64-pc-elf/$(VersionLibGCC)/64/libgcc.a \
}


rule ElfImage  image : scenario
{
	# Params:
	# - image: "image.elf", gristed
	# - scenario: e.g. "hog-demos"
	# - (implicit/on-target): AllBinaries
	# - (implicit/on-target): BulkBinaries
	
	# locate the raw image OUT of the boot folder, or it would be included in the final ISO image, bloating it massively
	MakeLocate $(image) : /tmp/jamrules-qemu_$(scenario) ;
	
	# bulk binaries
	local bulk_binaries =
		[ GetTargetVar $(image) : BulkBinaries ]
		;
	
	# special-cases: the copy for these 3 must be named differently from the original file
	# (rather than a grist, let's try a full path -- yup, works fine):
	local configfile = $(GenodeBuild)/var/run/$(scenario)_genode/config ;
	local timer = $(GenodeBuild)/var/run/$(scenario)_genode/timer ;
	local ldlib = $(GenodeBuild)/var/run/$(scenario)_genode/ld.lib.so ;
	
	# create Boot Image core from *all* binaries:
	local core = $(scenario).boot_modules.o ;
	Depends $(image) : $(core) ;
	MakeLocate $(core) : $(GenodeBuild)/var/run ;
	ImageCore $(core) :
		[ GetTargetVar $(image) : AllBinaries ]  # same list as $(BulkBinaries), plus timer & configfile & ld.lib.so
		;
	BOOTMODULESOBJ on $(image) = $(GenodeBuild)/var/run/$(core) ;  # for "actions ElfImage"
	
	local _targets = $(bulk_binaries:G=$(scenario)) ;
#echo ......... $(_targets) ;
	local file ;
	
	#+++ use rule RmTemps to remove bulky temps files like uncompressed image.elf ?
	#+++ Bulk:
	#	Bulk $(GenodeBuild)/var/run/$(scenario)/genode : $(_targets:G=) ;
	#	Depends $(image) : $(GenodeBuild)/var/run/$(scenario)/genode/vfs ; #$(_targets) ;
	#	SEARCH on $(_targets) += $(GenodeBuild)/bin ;
	SEARCH_SOURCE += $(GenodeRepos)/base/board/pc ;  # file "devices", for the ps/2 driver
	
	#///ToDo: maybe we don't need these 2 lines any more ?:
	# specials (xx Why won't it work when this statement is moved to jamrules-qemu-tts ?)
	SEARCH on VeraMono.ttf += $(GenodeRepos)/../contrib/ttf-bitstream-vera-cd3684816b73c4361e11236f9e63302f99b9b1ff/ttf/bitstream-vera ;
	SEARCH on libc.lib.so += $(GenodeBuild)/bin ;
	
	local target ;
	for target in $(_targets)
	{
		local _s = $(target:G=) ;
		
		Depends $(core) : $(target) ;
		MakeLocate $(target) : $(GenodeBuild)/var/run/$(scenario)_genode ;  # locate outside of the Xorriso build folder (don't pollute/bloat the ISO image)
		
		# We used to duplicate the files, with "File $(target) : $(_s)", but now do
		# symlinks instead, to save on disk space.
		# Careful to use *stripped* binaries to save space:
		# most SoftLink's are to bin/, which re-link to "stripped"; but there
		# are a couple exceptions, e.g. jam-built binaries, which are picked
		# in situ instead, where we have to manually select the .stripped variant:
		local exceptions =
			bfs_fuse_fs
			;
		#local exceptions2 =
		#	avcodec.lib.so
		#	;
		
		if $(_s) in $(exceptions)
		{
			EXIT bfs-fuse-symlink-not-implemented ;
#?			SoftLink $(target) : some_bfs_driver_related_path_here_maybe/$(_s).stripped ;
		}
		else
		{
			# T0
			SoftLink $(target) : $(_s) ;  # e.g.:   SoftLink  <hog-demos>Mandelbrot : Mandelbrot
		}
		
		# T+: we *clobber* the SEARCH value, with operator= (and not operator +=) to be deterministic:
		# -- indeed the value set from SEARCH_SOURCE is polluted with too many entries, including the ABI folder, which might occur earlier than the LIB folder, resulting in including the wrong (abi.so) libc etc, instead of the correct (lib.so) one !

		#SEARCH on $(_s) =
		SEARCH on $(_s) +=
			$(GenodeBuild)/bin  # for all (stripped) exes and some of the libs
			$(HoG_TOP)/haiku-on-genode/system/kinda-kernel  # haiku.lib.so
			;
		
		#echo ...SEARCH $(_s) .. [ GetTargetVar $(_s) : SEARCH ] ;
	}
	
	# special cases: these 3 files are hardcoded, and get *renamed* as
	# they are copied, so we cannot use "rule Bulk":
	SoftLink $(ldlib) : ld-nova.lib.so ;
	SoftLink $(timer) : nova_timer ;  # was: "nova_timer_drv"
#	File $(configfile) : $(scenario).config ;
ConfigFile $(configfile) ;  # uses target-local "CONFIG" variable for text payload
Always $(configfile) ;  # xx We could dodge this "always": detect changes in $(CONFIG) vs file contents ? Won't be trivial...
	SEARCH on ld-nova.lib.so += $(GenodeBuild)/bin ;
	SEARCH on nova_timer	+= $(GenodeBuild)/bin ;
#	SEARCH on $(scenario).config  += $(GenodeBuild)/var/run ;
	Depends $(core) : $(ldlib) $(timer) $(configfile) ;
	MakeLocate $(timer) $(ldlib) $(configfile) : $(GenodeBuild)/var/run/$(scenario)_genode ;
}


#rule SoftLink
#{
#echo +++++++SoftLink $(<) ---- $(>) ;
#	Depends files : $(<) ;
#	Depends $(<) : $(>) ;
#	SEARCH on $(>) = $(SEARCH_SOURCE) ;
#	Clean clean : $(<) ;
#}

#actions SoftLink
#{
#	# hack: make the link ABSOLUTE (prefix with $PWD) ; or maybe it's a proper fix? :
#	#	$(RM) $(<) && $(LN) -s "$PWD/$(>)" $(<)
#	#sometimes it's ALREADY absolute :-(
#	
#	$(RM) $(<) && $(LN) -s "$(>)" $(<)
#}



actions GzippedImage
{
#///FIXME: add "-n" switch to have constant timestamps (#4956) for reproducible builds
	gzip --fast --stdout $(2) > $(1) ;  # e.g. var/run/hog-demos/boot/image.elf.gz
	# with "--fast" we lose a MB or two but gain a few seconds
	#///ToDo: add a 'switch' that allows to turn on strong GZip compression when making "production" builds? First check how efficient it is on tts-prod builds...
}

rule GzippedImage gzipped : image : scenario
{
	# Make image.elf.gz from image.elf
	
	MakeLocate $(gzipped) : $(GenodeBuild)/var/run/$(scenario)/boot ;
	
	Depends $(gzipped) : $(image) ;  # e.g. Depends <hog-demos>image.elf.gz : <hog-demos>image.elf
	ElfImage $(image) : $(scenario) ;
}



actions Xorriso
{
	$(RM) $(<)  &&  xorriso -out_charset utf-8 -report_about HINT \
		-as mkisofs -f -l -R -hide-rr-moved -graft-points --modification-date=2017101816570300 \
		-b boot/grub/i386-pc/eltorito.img -no-emul-boot -boot-load-size 4 -boot-info-table \
		--embedded-boot $(GenodeRepos)/../contrib/$(Contrib_grub2)/boot/grub2/embedded.img \
		--protective-msdos-label \
		-o $(<) \
		-r $(<:S=) \
		--sort-weight 0 / --sort-weight 1 /boot
}

rule Xorriso  isofile : scenario
{
	# Make e.g. hog-demos.iso from image.elf, the grub folder(template), hypervisor ..etc
	
	#echo creating ISO image for $(isofile) for scenario $(scenario) ;
	
	MakeLocate $(isofile) : $(GenodeBuild)/var/run ;
	
	## boot folder ##
	
	# bender
	Depends $(isofile) : <$(scenario)>bender ;
	MakeLocate <$(scenario)>bender : $(GenodeBuild)/var/run/$(scenario)/boot ;
	File <$(scenario)>bender : $(GenodeRepos)/../tool/boot/bender ;
	
	# hypervisor
	Depends $(isofile) : <$(scenario)>hypervisor ;
	MakeLocate <$(scenario)>hypervisor : $(GenodeBuild)/var/run/$(scenario)/boot ;
#///FIXME: replace this Objcopy step with a plain "File" (copy) call... (ticket # ??)
	Objcopy <$(scenario)>hypervisor : $(GenodeBuild)/bin/hypervisor ;
	
	# image.elf.gz
	local img = image.elf ;
	local gz  = image.elf.gz ;
	local img_gristed = $(img:G=$(scenario)) ;
	local gz_gristed  = $(gz:G=$(scenario)) ;
	Depends $(isofile) : $(gz_gristed) ;
	GzippedImage $(gz_gristed) : $(img_gristed) : $(scenario) ;
	
	
	## boot/grub folder : IsoBootloader ##
	
	# Make the folder /before/ the SoftLink-to-folder, otherwise the below SoftLink creates an empty link
	# grub (actual/singleton)
	MakeGrubFolder $(GenodeGrubTemplate) : $(isofile) : $(scenario) ;
	
	# grub (symlink)
	Depends $(isofile) : $(GenodeBuild)/var/run/$(scenario)/boot/grub ;
	SoftLink
		$(GenodeBuild)/var/run/$(scenario)/boot/grub
		:
		$(GenodeGrubTemplate)
		;
}



rule FGristedImage  runfile
{
	# e.g. "hog-demos.run" => "<hog-demos>image.elf"
	
	local scenario    = $(runfile:B) ;
	local img         = image.elf ;
	local img_gristed = $(img:G=$(scenario)) ;
	
	return $(img_gristed) ;
}


rule CopyStaticFile  tmp_aliased_name : source_file
{
	MakeLocate $(tmp_aliased_name) : /tmp/genode_mirrored_staticfiles ;
	SoftLink   $(tmp_aliased_name) : $(source_file) ;
	
	#///later: seems $(tmp_aliased_name) does *not* get rebuilt, if $(source_file) is modified ? SoftLink does call "Depends", odd...
}

# Make a "copy" (as a 'symlink' node, if not an actual file node) of Genode repo files,
# so that in case of failure of e.g. CheckExternalFile, jam will delete the copy and not the original (!)
# call these here so as to be called *once* (if we were calling this from within another rule, we'd be wasting time copying several times)
CopyStaticFile  drivers.config		: $(HoG_TOP)/ge-services/repos-gems-sculpt-drivers/pc ;  # was formerly found over at $(GenodeRepos)/gems/sculpt/drivers/pc
CopyStaticFile  event_filter.config	: $(GenodeRepos)/os/recipes/raw/drivers_interactive-pc/event_filter.config ;

# event_filter.config must be 'patched' to provide support to vnc_server even in USE_DRV_MANAGER builds
#///later-2: nice to now have proper verification of (event-config, decorator-config) files, but maybe even "bring them in", make them part of the repo ?


rule CheckExternalFile  filepath : regexp
{
	NotFile $(regexp) ;
}

# we could also have a rule "CheckFileIsPatched" with args <filepath : md5sum> and actions { echo "$(2) $(1)" | md5sum --check } but that's much more convoluted for a lesser result

actions CheckExternalFile
{
	grep --quiet  "$(2)"  "$(1)"
}


# Assert files in the Genode repo (i.e. outside of our 'Fossil' version control) are patched.
# Note: these refer to *symlinks* to the external files -- in case the check
# fails to 'pass', jam will then delete the symlink rather than the original, phew
#
CheckExternalFile  event_filter.config : "<input name=\\\"vnc\\\"/>" ;
CheckExternalFile  event_filter.config : "<policy label=\\\"vnc\\\" input=\\\"vnc\\\"/>" ;



#pragma mark - kinda-front-end -


rule AddMinimumComponents  runfile
{
	# Adds "init", "timer", and other absolute minimum components needed
	# before even thinking of adding interaction components and apps.
	
	# config
	
	local LogOutput_Terminal_or_Serial = "<child name=\"terminal_log\"/> <parent/>" ;
	
	
	# not "components" per se
	
	AddRawComponent $(runfile) : config ;  # copying is special case, do not copy here
	AddRawComponent $(runfile) : ld.lib.so ;  # copying is special case, do not copy here
	
	AddRawComponent $(runfile) : init : init ;  # this one gets copied as-is
	
	
	# actual components, which get an XML <start> node
	
	AddRawComponent $(runfile) : timer :   :  # copying is special case, do not copy here and let Jamrules take care of it
		"
	\	<start name=\"timer\">
	\		<resource name=\"RAM\" quantum=\"2M\"/>
	\		<provides> <service name=\"Timer\"/> </provides>
	\	</start>
		"
		;
	
	if $(USE_DRV_MANAGER)
	{
		# drv manager
		AddBootModule $(runfile) : driver_manager ;
#+///ToDo: clean-up jam/* re. "drivers_init" blocks ? Maybe do "Jamrules" cleanup too.. and AddMediaStack cleanup
		AddBootModule $(runfile) : drivers.config ;  # repos/gems/sculpt/drivers/pc, aka drivers.config
		
		# hal
		AddBootModule $(runfile) : acpi ;  # was: "acpi_drv"
		AddBootModule $(runfile) : pci_decode ;
		AddBootModule $(runfile) : pc_platform ;  # was: "pc_platform_drv"
		
		# drivers
		AddBootModule $(runfile) : vesa_fb ;
		AddBootModule $(runfile) : pc_intel_fb ;
		AddBootModule $(runfile) : intel_gpu ;  # intel_fb cannot work without intel_gpu (gpu serves as "Platform" service for fb)
		AddBootModule $(runfile) : boot_fb ;    # driver_manager might select this one on some devices, if Platform mandates it
		AddBootModule $(runfile) : ahci ;
		AddBootModule $(runfile) : pc_usb_host ;
		AddBootModule $(runfile) : usb_hid ;
		AddBootModule $(runfile) : ps2 ;
		
		# glue
		AddBootModule $(runfile) : event_filter ;
		AddBootModule $(runfile) : event_filter.config ;
		AddBootModule $(runfile) : rom_reporter ;
		
		#/// maybe also: priority="-1" (like in sculpt.run) ?
		AddComponent  $(runfile) : init : "
		\	<start name=\"drivers_init\" caps=\"4850\" managing_system=\"yes\" >
		\		<resource name=\"RAM\"     quantum=\"230M\"   />
		\		<binary name=\"init\" />
		\		<provides>
		\			<service name=\"Block\"/>
		\			<service name=\"Platform\"/>  <!-- For acpica, driver/nic... -->
	#	\			<service name=\"Gpu\"/>
	#	\			<service name=\"Usb\"/>
		\			<service name=\"Event\"/>  <!-- In HoG we also allow accessing event_filter (required by vnc_server, in 'run_depot' sibling) -->
		\		</provides>
			"
				# -- split string to dodge Jam crash --
			"
		\		<route>
		\			<service name=\"ROM\" label=\"config\"> <parent label=\"drivers.config\"/> </service>
		\			<service name=\"ROM\" label_last=\"managed/event_filter\">
		\				<parent label=\"event_filter.config\"/> </service>
		\			<service name=\"ROM\" label_last=\"numlock_remap\">
		\				<parent label=\"numlock_remap.config\"/> </service>
		\			<service name=\"ROM\" label=\"capslock\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"ROM\" label=\"numlock\">   <child name=\"report_rom\"/> </service>
		\			<service name=\"ROM\" label_last=\"system\">
		\						<child name=\"platform_report_rom\" /> </service>
			#	\				<child name=\"config_fs_rom\" label=\"managed/system\"/> </service>
		\			<service name=\"ROM\" label_last=\"usb_policy\">
		\				<child name=\"rom_filter\"/> </service>
		\			<service name=\"ROM\" label_last=\"vesa.config\">
		\				<child name=\"config_rom\"/> </service>
		\			<service name=\"ROM\" label_last=\"fb_drv.config\">
		\				<child name=\"config_rom\"/> </service>
		\			<service name=\"ROM\" label_last=\"gpu_drv.config\">
		\				<child name=\"config_rom\"/> </service>
			"
				# -- split string to dodge Jam crash --
			"
		\			<service name=\"Timer\">   <child name=\"timer\"/> </service>
		\			<service name=\"Capture\"> <child name=\"nitpicker\"/> </service>
		\			<service name=\"Event\">   <child name=\"nitpicker\"/> </service>
		\			<service name=\"Report\" label=\"state\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"acpi\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"iommu\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"block_devices\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"pci_devices\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"usb_active_config\">  <child name=\"report_rom\"/> </service>
			# needed in 'static drivers' mode(?):
		\			<service name=\"Report\" label=\"usb_devices\">  <child name=\"report_rom\"/> </service>
		\			<service name=\"LOG\" label_suffix=\"fb\">  <parent/> </service>  <!-- was: fb_drv -->
		\			<service name=\"LOG\" label_suffix=\"gpu\"> <parent/> </service>  <!-- was: gpu_drv -->
		\			<service name=\"LOG\" label_suffix=\"usb\"> <parent/> </service>  <!-- was: usb_drv -->
		\			<service name=\"LOG\" label_suffix=\"usb_hid\"> <parent/> </service>
		\			<service name=\"LOG\"> $(LogOutput_Terminal_or_Serial) </service>
		\			<service name=\"Report\" label=\"devices\">       <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"smbios_table\">   <child name=\"report_rom\"/> </service>
		\			<service name=\"Report\" label=\"dynamic -> state\">  <child name=\"report_rom\"/> </service>
		\			<any-service> <parent/> </any-service>
		\		</route>
		\	</start>
			"
			;
		AddComponentService $(runfile) : 4M : report_rom : Report ROM : "
		\		<config verbose=\"no\">
		\			<policy label=\"foo -> state\"          report=\"drivers_init -> state\"/>
		\			<policy label=\"foo -> usb_active_config\"          report=\"drivers_init -> usb_active_config\"/>
			#+ acpi iommu block_devices pci_devices usb_devices devices smbios_table dynamic_state
			#
			#///ToDo-2: create a "display_reportrom" for these: --------
		\			<policy label=\"status_bar -> focus\"          report=\"nitpicker -> focus\"/>
		\			<policy label=\"global_keys_handler -> hover\" report=\"nitpicker -> hover\"/>
		\			<policy label=\"drivers_init -> capslock\"          report=\"foo -> capslock\"/>
		\			<policy label=\"drivers_init -> numlock\"          report=\"foo -> numlock\"/>
		\		</config>
			"
			;
		
		#///ToDo-2: move "usb_policy" to config_rom, next to Vesa config, and get rid of this rom_filter hack?
		AddComponentService  $(runfile) : 1M : rom_filter : ROM : "
		\	<config buffer=\"32K\">
		\		<output node=\"usb_policy\">
		\			<inline> <usb /> </inline>
		\		</output>
		\	</config>
			"
			;
		
		# Configs for Vesa FB and Intel FB/GPU
		#
		
		AddBootModule $(runfile) : vfs_import.lib.so ;
		
		AddComponent $(runfile) : vfs : "
		\	<start name=\"config_fs\"  priority=\"-1\" >
		\		<binary name=\"vfs\"/>
		\		<resource name=\"RAM\" quantum=\"1M\"/>
		\		<provides> <service name=\"File_system\"/> </provides>
		\		<config>
		\			<vfs>
		\				<ram/>
		\				<import>
		\					<inline name=\"gpu_drv.config\">
			<config ld_verbose=\"yes\" max_framebuffer_memory=\"64M\">
				<device vendor=\"0x8086\" device=\"0x1606\" generation=\"8\" platform=\"broadwell\" description=\"HD Graphics (BDW GT1 ULT)\"/>
				<device vendor=\"0x8086\" device=\"0x1616\" generation=\"8\" platform=\"broadwell\" description=\"HD Graphics 5500 (BDW GT2 ULT)\"/>
				<device vendor=\"0x8086\" device=\"0x1622\" generation=\"8\" platform=\"broadwell\" description=\"Iris Pro Graphics 6200 (BDW GT3e)\"/>
				<device vendor=\"0x8086\" device=\"0x1916\" generation=\"9\" platform=\"skylake\"  description=\"HD Graphics 520 (Skylake, Gen9)\"/>
				<device vendor=\"0x8086\" device=\"0x191b\" generation=\"9\" platform=\"skylake\"  description=\"HD Graphics 530 (Skylake, Gen9)\"/>
				<device vendor=\"0x8086\" device=\"0x5916\" generation=\"9\" platform=\"kabylake\" description=\"HD Graphics 620 (Kaby Lake, Gen9p5)\"/>
				<device vendor=\"0x8086\" device=\"0x5917\" generation=\"9\" platform=\"kabylake\" description=\"UHD Graphics 620 (Kaby Lake, Gen9p5)\"/>
				<device vendor=\"0x8086\" device=\"0x591b\" generation=\"9\" platform=\"kabylake\" description=\"HD Graphics 630 (Kaby Lake, Gen9p5)\"/>
				<device vendor=\"0x8086\" device=\"0x3ea0\" generation=\"9\" platform=\"whiskeylake\" description=\"UHD Graphics 620 (Whiskey Lake, Gen9p5)\"/>
				<device vendor=\"0x8086\" device=\"0x9a49\" generation=\"12\" platform=\"tigerlake\" description=\"Iris Xe Graphics (Tiger Lake, Xe)\"/>
			</config>
		\					</inline>
			# We cannot use the same "fb_drv.config" for both Vesa and Intel : vesa gets silently stuck
			#UPD: fb_drv.config does not upset vesa_fb, when spawned by driver_manager....
			# So back to commenting this out:
#		\					<inline name=\"vesa.config\">
#<config ld_verbose=\"yes\"    width=\"1024\" height=\"768\"   buffered=\"yes\"  >
#</config>
#		\					</inline>
			# BEWARE: one CANNOT configure "report connectors=NO"... it does not work... One MUST enable connectors, and configure a report_rom to accept them !
			#intel: add: max_width=2560 max_height=1504/1440
			# 3) though some mention "force_" : <config force_width=\"1024\" force_height=\"768\">
		\					<inline name=\"fb_drv.config\">
<config ld_verbose=\"yes\"    apply_on_hotplug=\"yes\"  >
	<report connectors=\"yes\"/>
</config>
		\					</inline>
		\				</import>
		\			</vfs>
		\			<policy label_prefix=\"config_rom\" root=\"/\"/>
		\		</config>
		\	</start>
			"
			;
		AddComponent $(runfile) : fs_rom : "
		\	<start name=\"config_rom\"  priority=\"-1\" >
		\		<binary name=\"fs_rom\"/>
		\		<resource name=\"RAM\" quantum=\"4M\"/>
		\		<provides> <service name=\"ROM\"/> </provides>
		\		<route>
		\			<service name=\"File_system\"> <child name=\"config_fs\" /> </service>
		\			<any-service> <parent/> <any-child/> </any-service>
		\		</route>
		\	</start>
			"
			;
	}
	else  # do not USE_DRV_MANAGER:
{
	AddComponent $(runfile) : acpi : "
	\	<start name=\"acpi\"  caps=\"400\" >
	\		<resource name=\"RAM\" quantum=\"4M\"/>
	\		<config report=\"yes\" />  <!-- To report on S3 level support... -->
	\		<route>
#	\			<service name=\"LOG\"> $(LogOutput_Terminal_or_Serial) </service>
#	\			<service name=\"LOG\">    <parent/> </service>
	\			<service name=\"LOG\"> <child name=\"term_app_log\"/> <parent/> </service>
	\			<service name=\"IO_MEM\"> <parent/> </service>
	\			<service name=\"PD\">     <parent/> </service>
	\			<service name=\"RM\">     <parent/> </service>
	\			<service name=\"CPU\">    <parent/> </service>
	\			<service name=\"ROM\">    <parent/> </service>
#tweaked these 2 lines... (for the benefit of acpica, or USB-HID ?)
#	\			<service name=\"Report\" label=\"acpi\"> <child name=\"report_rom\"/> </service>
#	\			<service name=\"Report\" label=\"smbios_table\"> <child name=\"report_rom\"/> </service>
#xx this could be "drivers_reports" instead of "report_rom":
	\			<service name=\"Report\"> <child name=\"report_rom\"/> </service>
#let's enable again:
#	\			<service name=\"ROM\" label=\"platform_info\"> <parent/> </service>
	\		</route>
	\	</start>
	"
		;
	
	AddComponent $(runfile) : pci_decode : "
	\	<start name=\"pci_decode\"  caps=\"350\" >
	\		<resource name=\"RAM\" quantum=\"1M\"/>
	\		<route>
	\			<service name=\"LOG\"> <child name=\"term_app_log\"/> <parent/> </service>
	\			<service name=\"Report\">               <child name=\"report_rom\"/> </service>
	\			<service name=\"ROM\" label=\"system\"> <child name=\"report_rom\"/> </service>  <!-- pci_decode seems to require 'system', an alias for 'acpi(_drv) -> acpi', not to be confused with the 'system=reset/poweroff' ROM... -->
	\			<any-service> <parent/> <any-child/> </any-service>
	\		</route>
	\	</start>
	"
		;
	
	# "managing_system" is necessary, otherwise we get "Device PD: attach_dma denied"
	AddComponent $(runfile) : pc_platform : "
	\	<start name=\"platform\"  caps=\"400\"  managing_system=\"yes\">
	\		<binary name=\"pc_platform\"/>
	\		<resource name=\"RAM\" quantum=\"6M\"/>
	\		<provides>
	\			<service name=\"Platform\"/>
	\		</provides>
	\		<route>
	\ 			<service name=\"LOG\"> $(LogOutput_Terminal_or_Serial) </service>
	\			<service name=\"ROM\" label=\"system\">	<child name=\"platform_report_rom\"/> </service>
	\				<!-- feed a dummy 'sleep_states' here in static paradigm, it's only in USE_DRV_MANAGER mode that we'll (attempt to) support S3 suspend: -->
	\			<service name=\"ROM\" label=\"sleep_states\"> <child name=\"rom_filter\" label=\"sleep_states\"/> </service>
	\			<service name=\"ROM\" label=\"acpi\">	<child name=\"report_rom\"/> </service>  <!-- for driver/pc_platform to get 'acpi reset via such and such port' info -->
	\				<!-- careful with 'devices' ROM, if not present and set to 'report_rom', ahci(_drv) SILENTLY gets stuck -->
					# if I use rom_filter, PS/2 works but AHCI fails
					# if I use report_rom, AHCI works but PS/2 fails
					# Fixed! Was missing a "policy" in report_rom... (via rom_filter, which I now use):
	\			<service name=\"ROM\" label=\"devices\"> <child name=\"rom_filter\"/> </service>
		#?		<service name=\"Report\" label=\"iommu\">	<parent label=\"iommu\"/> </service>
	\			<service name=\"Report\"> <child name=\"report_rom\"/> </service>
	\			<any-service> <parent/> <any-child/> </any-service>
	\		</route>
			"
				# -- split string to dodge Jam crash --
			"
	\		<config>
	\			<report devices=\"yes\" iommu=\"yes\"/>
	\			
	\			<policy label_prefix=\"ps2\">  <device name=\"ps2\"/> </policy>  <!-- was: label prefix ps2_drv -->
\	<policy label_suffix=\"-> nic\"	info=\"yes\">	<pci class=\"ETHERNET\"/> </policy>
\	<policy label_prefix=\"sequence -> nic\"	info=\"yes\">	<pci class=\"ETHERNET\"/> </policy>
	\			<policy label_prefix=\"nic\"	info=\"yes\">	<pci class=\"ETHERNET\"/> </policy>
	\			<policy label_prefix=\"fb\"     info=\"yes\">   <pci class=\"VGA\"/> </policy>
	\			<policy label_prefix=\"vesa_fb\"    info=\"yes\">   <pci class=\"VGA\"/> </policy>
#	\	<policy label_prefix=\"drivers_init\" info=\"yes\"> <pci class=\"VGA\"/> </policy>
	\			<policy label_prefix=\"wifi_drv\" info=\"yes\"> <pci class=\"WIFI\"/> </policy>
	\			<policy label_prefix=\"usb_host\" info=\"yes\">  <pci class=\"USB\"/> </policy>
	\			<policy label_prefix=\"ahci\"> <pci class=\"AHCI\"/> </policy>
	\			<policy label_prefix=\"nvme\"> <pci class=\"NVME\"/> </policy>
	\			<policy label_prefix=\"audio\"> <pci class=\"AUDIO\"/> <pci class=\"HDAUDIO\"/> </policy>
	\			<policy label_prefix=\"intel_fb\" info=\"yes\">
	\				<pci class=\"VGA\"/>
	\				<pci class=\"ISABRIDGE\"/>
	\			</policy>
	\			<policy label_prefix=\"intel_gpu\" info=\"yes\">
	\				<pci class=\"VGA\"/>
	\				<pci class=\"ISABRIDGE\"/>
	\			</policy>
	\			<policy label_prefix=\"acpica\"> <device name=\"acpi\"/> </policy>
	\		</config>
	\	</start>
		"
		;

	# Genode support: input device drivers
	#
	# Platform support for ps/2 devices etc
	AddBootModule  $(runfile) : devices ;  # "base/board/pc/devices" xml file with IRQ/IO range for ps/2, and infos for driver/platform(_drv)
	#
	# Platform support for pc_platform, ps/2 devices etc
	AddComponentService  $(runfile) : 1M : rom_filter : ROM : "
	\	<config buffer=\"32K\">
	\		<input name=\"devices\"/>
	\		<input name=\"pci_devices\"/>
	\		<output node=\"devices\">
	\			<input name=\"devices\"     skip_toplevel=\"yes\"/>
	\			<input name=\"pci_devices\" skip_toplevel=\"yes\"/>
	\		</output>
	\		<output node=\"sleep_states\">
	\			<inline> <empty /> </inline>
	\		</output>
	\	</config>
	\	<route>
	\		<service name=\"ROM\" label=\"pci_devices\"> <child name=\"report_rom\"/> </service>
	\		<any-service> <parent/> <any-child/> </any-service>
	\	</route>
		"
		;
	AddComponentService $(runfile) : 4M : report_rom : Report ROM : "
	\		<config verbose=\"no\">
	\	<policy label_prefix=\"to_whom_it_may_concern\" report=\"mixer -> channel_list\"/>
	\			<policy label=\"pci_decode -> system\"		report=\"acpi -> acpi\"/>  <!-- was 'acpi_drv..acpi' -->
	\			<policy label=\"rom_filter -> pci_devices\"	report=\"pci_decode -> devices\"/>
	\			<policy label=\"platform -> devices\"		report=\"pci_decode -> devices\"/>  <!-- was 'platform_drv' -->
		# These are needed in static-drivers mode(?):
	\			<policy label=\"platform -> acpi\"             report=\"acpi -> acpi\"/>  <!-- was 'platform_drv...' 'acpi_drv...' -->
	\			<policy label=\"status_bar -> focus\"          report=\"nitpicker -> focus\"/>
	\			<policy label=\"nitpicker_config -> xray\"     report=\"global_keys_handler -> xray\"/>
	\			<policy label=\"global_keys_handler -> hover\" report=\"nitpicker -> hover\"/>
	\			<policy label=\"nit_focus -> clicked\"         report=\"nitpicker -> clicked\"/>
	\			<policy label=\"nitpicker -> focus\"           report=\"nit_focus -> focus\"/>
	\		</config>
		"
		;
	}  # ~USE_DRV_MANAGER .............................
	
	# Common
	#
	AddComponentAsStart  $(runfile) : 4M : report_rom :
			"name=\"platform_report_rom\" caps=\"120\"" :
		"	<binary name=\"report_rom\"/>
		\		<provides> <service name=\"Report\"/> <service name=\"ROM\"/> </provides>
		\		<config verbose=\"yes\">
		\				<!-- Only registrar is allowed to send Power-Off/Suspend/Reset to acpica/ps2/platform -->
		\			<policy label_prefix=\"acpica\"			report=\"registrar -> system\"/>
		\			<policy label_prefix=\"ps2\"			report=\"registrar -> system\"/>  <!-- was: ps2_drv -->
		\			<policy label_prefix=\"platform\"		report=\"registrar -> system\"/>  <!-- was platform_drv -->
		\			<policy label_prefix=\"drivers_init\"	report=\"registrar -> system\"/>
		\		</config>
		"
		;
}



rule AddBootModule  runfile : binary
{
	AddRawComponent  $(runfile) : $(binary) : $(binary) ;
}

#///later-2: rename this (below) to AddBootModule_Etc, and prev (above) to AddBootModuleSimple or such
rule AddRawComponent  runfile : binary_all : binary_bulk : script
{
	local img_gristed = [ FGristedImage $(runfile) ] ;
	local scenario = $(runfile:B) ;
	
	# this de-dupe test is a necessary optim ; otherwise e.g. "report_rom" gets added several times to the image, wasting space
	if ! $(binary_all) in [ GetTargetVar $(img_gristed) : AllBinaries ]
	{
		AllBinaries  on $(img_gristed) += $(binary_all) ;
		BulkBinaries on $(img_gristed) += $(binary_bulk) ;
	}
	# else
	#	Echo "ignoring AddRawComponent's binary add part (the binary is already there), will proceed with CONFIG script increment, though" ;
	
	CONFIG on $(GenodeBuild)/var/run/$(scenario)_genode/config
		+= $(script) ;
}


rule AddComponent  runfile : binary : script
{
	#echo "AddComponent  $(runfile) -- $(binary) -- $(script)" ;
	
	local img_gristed = [ FGristedImage $(runfile) ] ;
	
	# add 'binary' to both AllBinaries and BulkBinaries:
	AddRawComponent $(runfile) : $(binary) : $(binary) : $(script) ;
}


#pragma mark - front-end -


#############
# Front-end #
#############


actions RunQemu1
{
	# - CPU:  Genode 23.08 went from "core2duo" to "Nehalem-v2", but the Qemu package here only knows "Nehalem", so let's try that:
	# - VESA: limit screen resolution in Qemu to 1920x1080, via 8 MB video RAM
	# - KVM:  removed "-no-kvm" switch as that trips up Qemu 7.x in Linux.
	qemu-system-x86_64  -display sdl -cpu Nehalem  -m $(QEMU_RAM) \
		$(QEMU_INPUT) \
		$(QEMU_AHCI) \
		-device ich9-intel-hda  -serial mon:stdio -cdrom $(1)   -machine q35 \
		-device VGA,vgamem_mb=8
}


# Mandatory params:
#	- runfile, e.g. "test.run", from which we'll derive image name "test.iso"
# Optional params:
#	- ahci_disk_image: e.g. "/tmp/test_ntfs_image.raw"
#	(a disk image for Qemu to provide as a block device to e.g. driver/ahci(_drv))
#
rule RunQemu  runfile : ahci_disk_image
{
	local scenario = $(runfile:B) ;
	NotFile $(scenario) ;  # silences e.g. "warning: using independent target hog-demos"
	
	#++fix "warning: indep target ld.lib.so"... maybe similar to this?:
	#$(bulk_binaries:G=$(scenario)) ;
	# fixing "init" involved adding it to AddRawComponents above (was referenced but not copied)..
	
	# e.g. "test.iso" depends on "test.run"
	Depends $(runfile) : $(scenario).iso ;
	
	Xorriso $(scenario).iso : $(scenario) ;
	RunQemu1 $(scenario).iso ;  # this action/rule needs to bear a name distinct from "RunQemu" due to using a $(1) which is different from RunQemu's $(1)
	
	Always $(scenario).iso ;
	
	# For running on QEMU (rather than bare metal), set USB_INPUT to "false" in
	# your Jamfile to disable USB-HID, falling back to PS/2, which is MUCH faster in QEMU emulation.
	#
	USB_INPUT ?= true ;
	
	if $(USB_INPUT) = "true"
	{
		# or "-usb -usbdevice mouse..." as the dde_linux/run file does ?
		QEMU_INPUT on $(scenario).iso += -usb -device usb-mouse -device usb-kbd -device usb-ehci,id=ehci ;
	}
	
	if $(ahci_disk_image)
	{
		Echo "RunQemu $(runfile) will use storage: $(ahci_disk_image)" ;
		
		Always $(ahci_disk_image) ;  # if the image results from a creation & formatting process, make sure the formatting is made at each run, to restore state after each test-run !
		Depends $(scenario).iso : $(ahci_disk_image) ;
		
		# we use "bus=ahci.0" to be compatible with both the USE_DRV_MANAGER case (which supports any port) *and* the static-drivers case (which hardcodes ahci port 0):
		# also use "ide-hd" syntax, since "ide-drive" seems to be deprecated in Qemu 7.x on Debian:
		QEMU_AHCI on $(scenario).iso = "-drive id=disk,file=$(ahci_disk_image),format=raw,if=none -device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 -boot d" ;
		#	-drive id=disk,file=/dev/disk/scsi/0/0/0/raw,format=raw,if=none -device ahci,id=ahci
		#	-device ide-drive,drive=disk,bus=ahci.0 -boot d
	}
	
	if $(OS) = "HAIKU"
	{
		QEMU_RAM on $(scenario).iso = "1024" ;  # Dodge freezes/crashes that occur with high RAM
	}
	else
	{
		QEMU_RAM on $(scenario).iso = "2048" ;  # No problem on Linux
	}
}



rule AddComponentAsStart  runfile : memory : binary : start_tag : extraconfig
{
	local conf ;
	
	conf += $(newline) ;
	conf += "	<start $(start_tag)>" $(newline) ;
	conf += "		<resource name=\"RAM\" quantum=\"$(memory)\"/>" $(newline) ;
	if $(extraconfig) {
		conf += $(extraconfig) $(newline) ;
	}
	conf += "	</start>" $(newline) ;
	
	AddComponent $(runfile) : $(binary) : $(conf) ;
}


rule AddComponentService  runfile : memory : binaries : service : extraconfig
{
	# A service name can optionally be specified:
	# e.g. AddComponentService hog-demos.run : 1M : timer : Timer ;
	#
	# Also supports multiple binaries in one go:
	# e.g. AddComponentService hog-demos.run : 20M : Clock Pulse Mandelbrot ;
	
	local conf ;
	
	for binary in $(binaries)
	{
		conf += $(newline) ;
		conf += "	<start name=\"$(binary)\">" $(newline) ;
		conf += "		<resource name=\"RAM\" quantum=\"$(memory)\"/>" $(newline) ;
		if $(service) {
			conf += "		<provides>" $(newline) ;
			conf += "			<service name=\"$(service)\"/>" $(newline) ;
				# if $(service) holds multiple strings, that will generate multiple lines
				# as needed (you probably don't want to do that and at the same time provide a service name!)
			conf += "		</provides>" $(newline) ;
		}
		if $(extraconfig) {
			conf += $(extraconfig) $(newline) ;
		}
		conf += "	</start>" $(newline) ;
	}
	
	AddComponent $(runfile) : $(binaries) : $(conf) ;
}


rule AddComponentClient  runfile : memory : binaries : extraconfig
{
	AddComponentService $(runfile) : $(memory) : $(binaries) :   : $(extraconfig) ;
}


	#///later: rename to "AddRunningBeApp" (or AddLaunchHogApp) (since this does not just add a boot module but also RUNS it in init), and move to jamrules-hog (since this is not pure-Genode stuff)
# On-target var param: $(Config_Vfs)
# On-target var param: $(Config_Route)
# Params: e.g.   "mmd.run","80M",   "140",         "Pulse"
rule AddPosixApp  runfile : memory : capabilities : binaries
{
#///ToDo: should set the var on $(binaries), not on the $(runfile) common to all binaries !
	# echo [ GetTargetVar $(runfile) : Config_Vfs ] ;
	# echo [ GetTargetVar $(runfile) : Config_Route ] ;
	
	# Caps default value, if none passed to us:
	capabilities ?= 256 ;
	
	local conf_LdVerbose = "no" ; #xx make this a global config var ?
	
	# Handle multiple component binaries:
	# do a for() loop to accomodate AddComponentAsStart (unlike AddComponentService, it does not handle multiple-binaries-in-one-param)
	for binary in $(binaries)
	{
		local conf ;
		conf += "		<config ld_verbose=\""$(conf_LdVerbose)"\">" $(newline) ;
		conf += "			<libc  rtc=\"/dev/rtc\" stdin=\"/dev/null\" stdout=\"/dev/log\" stderr=\"/dev/log\" socket=\"/socket\">" $(newline) ;
		conf += "				<pthread placement=\"all-cpus\" verbose=\"no\" />" $(newline) ;
		conf += "			</libc>" $(newline) ;
		conf += "			<vfs>" $(newline) ;
	#	conf += "				<rom name=\"Vera.ttf\"/>" $(newline) ;
		conf += [ GetTargetVar $(runfile) : Config_Vfs ] $(newline) ;
		conf += "				<dir name=\"dev\">" $(newline) ;
		conf += "					<rtc/>" $(newline) ;
		conf += "					<log/>" $(newline) ;
		conf += "					<null/>" $(newline) ;
		conf += "				</dir>" $(newline) ;
		conf += "			</vfs>" $(newline) ;
			# Some apps (e.g. DeskCalc) require argc==1 or they don't run, so make sure to specify args:
#///later: for running Deskbar etc (from boot-module instead of from Dynit), we could mimic Dynit's ENV setup, instead of reming out TRoster checks -- UPD: inserted the below, *but* these insert e.g. "Pulse" instead of the full path "/boot/apps/Pulse"...
		conf += "			<env key=\"_\" value=\"$(binary)\"/>" $(newline) ;
		conf += "			<arg value=\"$(binary)\"/>" $(newline) ;
		conf += "		</config>" $(newline) ;
		conf += "		<route>" $(newline) ;
		conf += [ GetTargetVar $(runfile) : Config_Route ] $(newline) ;
			# Haiku apps connect directly to Nitpicker, bypassing Wm, since they have their own "Client-side" decorations/wm
			# (unlike Genode-native apps, which  will set routing rules different from this one):
		conf += "			<service name=\"Gui\"> <child name=\"nitpicker\"/> </service>" $(newline) ;
		conf += "			<any-service> <parent/> <any-child/> </any-service>" $(newline) ;
		conf += "		</route>" $(newline) ;
		
		AddComponentAsStart $(runfile) : $(memory) : $(binary) : "name=\"$(binary)\" caps=\"$(capabilities)\"" : $(conf) ;
	}
}


###########################
# Front-end : disk images #
###########################

# These are similar to Genode's test-libc test infrastructure.

actions DiskCreate
{
	dd if=/dev/zero of=$(1) bs=1024 count=65536  2> /dev/null
	#sync  # otherwise mkfs tends to fail...
	# nope, still no go, still getting this on first run :-/... Another haiku cache bug? : ""mkfs: Initialization of "Data" failed: Bad data""
}

actions DiskFormatNTFS_HaikuHost
{
	mkfs  --dont-ask  -t ntfs  $(1)  TestDisk
}
actions DiskFormatNTFS_LinuxHost
{
	sudo mkntfs --force $(1)  --label TestDisk
		# force to proceed despite file not being a "block device":
}

rule DiskNtfsBlank  path_to_raw_img
{
	DiskCreate $(path_to_raw_img) ;
	
	if $(OS) = "HAIKU"
	{
		DiskFormatNTFS_HaikuHost  $(path_to_raw_img) ;
	}
	else
	{
		DiskFormatNTFS_LinuxHost  $(path_to_raw_img) ;
	}
}


