DNA.
ribosome
Version.
1.1.9
Language.
tcl
Manpage.
ribosome (1WY)
Manpage.
package.test (1WY)
Testbase.
Test Script
Test Report
Import.
Package.
wyrmwif
wyrm-expat
wyrm-uri
Export.
Dtd.
dna.dtd
Executable.
ribosome
Implementation.
ribosome.tcl

Convert DNA to Executables and Libraries

Sections.
DNA Objects
Define Tree Relationships
HTML
Text and Code
DNA Document Type Definition
DNA Control
DNA Section
DNA Doc
DNA Declaration
DNA Code
DNA Ref
DNA Test Base
Convert DNA to Executables and Libraries
Utilities
Test base
Make.
Executable.
rule $bin/ribosome "pkgIndex.tcl $source/ribosome.tcl" "
  rm -f $bin/ribosome
  cp $source/ribosome.tcl $bin/ribosome
  chmod a+rx $bin/ribosome
"
Script.
rule executables :: ribosome
rule ribosome $bin/ribosome
rule ribosome.test [list \
    $bin/ribosome \
    $test/wyrm-ribosome.test.html \
]
rule clean :: {} "
  -rm $test/ribosome.TESTING
"
rule clobber :: {} "
  -rm $bin/ribosome
"
   
top

1 :: Convert DNA to Executables and Libraries.

Copyright (C) 2002, 2004 SM Ryan

Wyrmwif Tcl extensions. For non-profit uses only, provided this copyright is preserved on all copies, this work may be freely copied, modified, redistributed, compiled, and incorporated in other works. This work is distributed with no warranty of any kind; no author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing.

   
   

DNA Objects

   
top

2. DNA parsing :: The Development Network Archive (DNA) is an XML resource which encapsulates a piece of software, include the content or build instructions of its files. The XML resource is parsed and stored in a global array ::dna. The subscript is the expat %index (joined with full stops), followed by a property name.

The %span is used to skip over irrelevant spaces and newlines between tags.

$dna(index:name)
tag if a start or end element, otherwise %element-kind.
$dna(index:children)
List of indices of all daughter elements.
$dna(index.%text)
Actual text data. This is content data of %content or %cdata.
$dna(index.%data)
Content data of other elements.
$dna(index.attribute)
Start element attributes.
$dna(index.%property)
Other properties from expat.
$dna(index.*property)
Ribosome property or method.
$dna(index.*control-html)
<control> element HTML generation method.
$dna(index.*define)
Define method.
$dna(index.*export)
Export file generation.
$dna(index.*group)
Links titled section groups.
$dna(index.*html)
HTML generation method.
$dna(index.*label)
If this is an <label ...> element.
$dna(index.*makescript)
Make script generation.
$dna(index.*owner)
Links titled section groups.
$dna(index.*part)
Which section part this element is belongs to.
$dna(index.*preptest)
Test generation preparation.
$dna(index.*sectnum)
Section numbers.
$dna(index.*toc)
Fragments in a group.
$dna(def:tag)
Indices of sections which define the tag.
$dna(export:tag)
Pairs list exported files and descriptions by section tag.
$dna(exports)
Tags of all export sections.
$dna(html)
The channel to html image.
$dna(manpage)
Tags of all manpage sections.
$dna(ref:tag)
Indices of sections which reference the tag.
$dna(section)
Current section.
$dna(sectnum)
Next section number.
$dna(testbases)
Tag of testbase section.
$dna(version)
<control> <version> if any.

proc parseInputXML {parses} {
	global dna default
	set dna(:children) {}
	set cdata 0
	foreach parse $parses {
		foreach {p v} $parse {
			switch $p {
				%what {set what [string map {-begin ""} $v]}
				%index {set daughter $v}
				%name {set name $v}
				%data {set data $v}
				%line {set line $v}
			}
		}
		set mother [join [lrange $daughter 0 end-1] .]
		set daughter [join $daughter .]
		switch $what {
			cdata {
				set cdata 1; set cdataIndex $daughter
			}
			cdata-end {
				set cdata 0; continue
			}
			content {
				if {$cdata} {
					append dna($cdataIndex.%text) $data
					continue
				} elseif {[string length [string trim $data]]==0} {
					if {!$dna($mother.%span)} continue
				}
			}
			end - namespace-end - doctype-end - entity-parser-end continue
		}
		if {![string equal $what start]} {
			set name %$what
		}
		set dna($daughter:name) $name
		lappend dna($mother:children) $daughter
		set dna($daughter:children) {}
		<Ribosome object types>
		array set P $parse
		foreach {p v} [array get P] {
			if {[string equal $p %data] && [string equal $name %content]} {
				set dna($daughter.%text) $v 
			} else {
				set dna($daughter.$p) $v
			}
		}
		array unset P *
	}
}

		
 
section    top

3. Ribosome object types :: Ribosome uses a simple object type system to identify how to act on parsed elements from the DNA. The name property is used to index into the default global array. $default(name.values) is a property list of method implementations and default attribute values for that name; $default(name.delegate) is an optional additional name to read defaults from. As the element is parsed and enterred into the dna array, all these defaults are discoverred and added in with the expat properties and attributes.

Then when a method m is called on an element with index x, $dna(x.*m) is the name of the proc which implements the method for that element. The names of the procs have the general form name.m, but this naming scheme is a convenience: it is the dna subscript that identifies the method implementation, not the proc name.


set delegates [list $name]; set x $name
while {[info exists default($x.delegate)]} {
	set x $default($x.delegate); lappend delegates $x
}
while {[llength $delegates]} {
	set x [lindex $delegates end]
	set delegates [lrange $delegates 0 end-1]
	if {![info exists default($x.values)]} {
		error "\[$line\] unknown tag <$x ...>"
	}
	array set P $default($x.values)
}

		
 
section    top

%root.values {
	*part none *label 0
	%text ""
	%span 0
	*define %root.define
	*html %root.html
	*control-html %root.control-html
	*export %root.export
	*makescript %root.makescript
	*preptest %root.preptest
}
%init.delegate %root
%init.values {
}
%content.delegate %root
%content.values {
	*define hide.define
	*html %content.html
	*export %content.export
	%span 1
}
%pi.delegate %root
%pi.values {
}
%comment.delegate %root
%comment.values {
}
%cdata.delegate %root
%cdata.values {
	*define hide.define
	*html %cdata.html
	*export %cdata.export
}
%namespace.delegate %root
%namespace.values {
}
%xml.delegate %root
%xml.values {
}
%text.delegate %root
%text.values {
}
%doctype.delegate %root
%doctype.values {
}
%element.delegate %root
%element.values {
}
%attlist.delegate %root
%attlist.values {
}
%entityInternal.delegate %root
%entityInternal.values {
}
%entityExternalUnparsed.delegate %root
%entityExternalUnparsed.values {
}
%entityExternalParsed.delegate %root
%entityExternalParsed.values {
}
%parameterEntityInternal.delegate %root
%parameterEntityInternal.values {
}
%parameterEntityExternal.delegate %root
%parameterEntityExternal.values {
}
%notation.delegate %root
%notation.values {
}
%entity-parser.delegate %root
%entity-parser.values {
}

		
   
   

Define Tree Relationships

   
top

proc DEFINE {} {
	array set P {}
	foreach daughter $::dna(:children) {
		array set P [define $daughter [array get P]]
	}
	array get P
}

proc define {daughter props args} {
	if {[llength $args]} {
		array set P $props
		array set P $args
		set props [array get P]
	}
	$::dna($daughter.*define) $daughter $props
}

proc %root.define {mother props} {
	array set P $props
	foreach daughter $::dna($mother:children) {
		array set P [define $daughter [array get P]]
	}
	array get P
}

		
   
   

HTML

   
top

6. HTML translation :: The html method converts a DNA into a presentation HTML element. If the expand property is none, <ref>s are translated as hyperlinks. Otherwise <ref>s are replaced with text of the part of the section; if the <ref> does not have an element attribute, expand is which part of the section is expanded.

The hx property is used to pass <msect> nesting depth.

There is no returned value. The html methods output to the current html channel.

HTML title sections expand path
Recursively convert the sections to html and write to path.
html index props [ p v ] ...
Call the *html method of index with the property list props editted with the properties and values p v, ... .
escape string
Convert reserved characters to &-entity references.
h tag [ attribute value ] ... script
Generate the HTML element <tag attribute='value'...> ... </tag>. The script is evaluated to generate the interior elements and content data of the element. If the script is --, it is an empty element.

proc HTML {title mothers expand path} {
	file delete $path
	set ::dna(html) [open $path w]
	h HTML {
		h HEAD {
			h TITLE {
				h %content $title
			}
			h META name "creator" content "ribosome" --
			h META name "dna" content "$::base" --
			h STYLE {
				h %content {
					A.navigation:link {
						text-decoration: none;
						color: blue;
						font-family: Optima, Helvetica, sans-serif;
						font-size: x-x-small;
						font-weight: bold;
					}
					A.navigation:visited {
						text-decoration: none;
						color: blue;
						font-family: Optima, Helvetica, sans-serif;
						font-size: x-x-small;
						font-weight: bold;
					}
					DIV.navigation {
						font-family: Optima, Helvetica, sans-serif;
						font-size: x-x-small;
						font-weight: bold;
					}
					.ERROR {
						background-color: black;
						color: red;
						font-weight: bold;
					}
					.b {
						font-family: Optima, Helvetica, sans-serif;
					}
					.c {
						font-family: Optima, Helvetica, sans-serif;
						font-weight: bold;
					}
					.code { 
						background-color: #F0FFF8; 
						font-family: Optima, Helvetica, sans-serif;
						font-size: 13px;
					}
					CODE { 
						font-family: Optima, Helvetica, sans-serif;
						font-size: 13px;
					}
					.decl {
						background-color: white;
						float: right;
						width: 95%;
					}
					.declspace {
						background-color: white;
						float: left;
						width: 5%;
					}
					.decls {
						background-color: #FFFFC0;
						width: 100%;
					}
					DL.nested {
						margin-top: 0; margin-bottom: 0;
					}
					.em {
						font-weight: bold;
					}
					.gloss {
						font-style: italic;
					}
					H1.msect {
						text-transform: uppercase;
					}
					H2.msect {
						text-transform: capitalize;
					}
					H3.msect {
						text-transform: capitalize;
					}
					H4.msect {
						text-transform: capitalize;
					}
					H5.msect {
						text-transform: capitalize;
					}
					H6.msect {
						text-transform: capitalize;
					}
					.hp {
						text-indent: -3em;
						font-size: 9px;
					}
					.re {
						font-family: Monaco, Courier, monospace;
					}
					.rule {
						font-family: Monaco, Courier, monospace;
						font-size: small;
					}
					.rulemark {
						font-family: Helvetica, sans-serif;
						font-weight: bold;
						font-size: large;
					}
					.sectmarksmall {
						font-family: Helvetica, sans-serif;
						font-size: larger;
					}
					SPAN.formula {
						font-family: Palatino, serif;
					}
					SPAN.samp {
						font-family: Optima, Helvetica, sans-serif;
						font-size: 13px;
					}
					.string {
						font-family: Monaco, Courier, monospace;
					}
					TABLE.control {
						width: 100%;
					}
					TABLE.mx {
						border-color: red;
						border-width: 2;
						border-style: solid;
						width: 100%;
					}
					TABLE {
						width: 100%;
					}
					TD.controlabstract {
						background-color: lightblue;
						text-align: left;
						vertical-align: top;
						width: 40%;
					}
					TD.controldetail {
						background-color: tan;
						text-align: left;
						vertical-align: top;
						width: 60%;
					}
					TD.controlmakes {
						text-align: left;
						vertical-align: top;
						width: 100%;
						background-color: #FFFFC0;
					}
					PRE.controlmakes {
						font-family: Optima, Helvetica, sans-serif;
						font-size: 11px;
					}
					TD.controltoc {
						text-align: left;
						vertical-align: top;
						width: 60%;
					}
					TD {
						text-align: left;
						vertical-align: top;
					}
					TABLE.page {
						width: 100%;
					}
					TD.pageborder {
						background-color: lightblue;
						text-align: left;
						vertical-align: top;
						width: 99%;
					}
					TD.pagecontent {
						background-color: white;
						text-align: left;
						vertical-align: top;
						width: 99%;
					}
					TD.pagelink {
						background-color: lightblue;
						text-align: center;
						vertical-align: bottom;
						width: 1%;
					}
					TD.pagespacer {
						background-color: white;
						text-align: left;
						vertical-align: top;
					}
					TABLE.section {
						width: 100%;
						padding-top: 2em;
					}
					TD.sectionabstract {
						background-color: lightgray;
						text-align: left;
						vertical-align: top;
						width: 39%;
					}
					H1.sectionabstract {
						font-family: Optima, Helvetica, sans-serif;
						font-weight: bold;
					}
					TD.sectioncontent {
						background-color: lightgray;
						text-align: left;
						vertical-align: top;
						width: 99%;
					}
					TD.sectionlink {
						background-color: lightgray;
						text-align: center;
						vertical-align: bottom;
						width: 1%;
					}
					SPAN.sectiontitle {
						font-family: Optima, Helvetica, sans-serif;
						font-weight: bold;
						font-size: 16px;
					}
					TD.sectiontoc {
						background-color: lightgray;
						text-align: right;
						vertical-align: top;
						width: 30%;
						padding-right: 1em;
					}
					SPAN.sectiontoc {
						font-style: italic;
						font-size: 11px;
					}
					.type {
						font-family: Optima, Helvetica, sans-serif;
						font-weight: bold;
						font-size: 13px;
					}
					.v {
						font-family: Optima, Helvetica, sans-serif;
						font-style: italic;
						font-size: 12px;
					}
					.vw2 {
						font-family: Gill Sans, Arial, Helvetica, san-serif;
						font-size: small;
					}
				}
			}
		}
		h BODY {
			foreach mother $mothers {
				foreach daughter $::dna($mother:children) {
					html $daughter {} expand $expand hx 0 trimleft 0 trimright 0 sectionname ""
				}
			}
		}
	}
	close $::dna(html)
}

proc html {daughter props args} {
	if {[llength $args]} {
		array set P $props
		array set P $args
		set props [array get P]
	}
	$::dna($daughter.*html) $daughter $props
}

proc hash {} {
	return "#"
}

proc escape {text} {string map {&# &# — —     & &amp; < &lt; > &gt;} $text}

proc h {tag args} {
	if {[string equal $tag %content]} {
		puts -nonewline $::dna(html) [escape [join $args { }]]
	} else {
		set script [lindex $args end]
		puts -nonewline $::dna(html) <$tag
		foreach {p v} [lrange $args 0 end-1] {
			puts -nonewline $::dna(html) " $p='[escape $v]'"
		}
		puts -nonewline $::dna(html) >
		if {![string equal $script --]} {
			uplevel 1 $script
			puts -nonewline $::dna(html) </$tag>
		}
	}
	return ""
}

proc %root.html {mother props} {
	array set P $props; set sectionname $P(sectionname)
	foreach daughter $::dna($mother:children) {
		html $daughter $props sectionname $sectionname
		set sectionname ""
	}
}

proc %root.control-html {mother} {
	return {}
}

proc %content.html {mother props} {
	set data [escape $::dna($mother.%text)]
	array set P {eol ""}
	array set P $props
	if {$P(trimleft)} {
		set data [string trimleft $data]
	} elseif {$P(trimright)} {
		set data [string trimright $data]
	}
	if {[string length $P(sectionname)]} {
		h SPAN CLASS sectiontitle {h %content $P(sectionname)}
	}
	if {[string equal $P(eol) break]} {set data [string map {\n <BR>\n} $data]}
	puts -nonewline $::dna(html) $data
}

proc %cdata.html {mother props} {
	array set P $props
	%content.html $mother $props
	array set P [list sectionname "" trimleft [expr {$P(trimleft)&&[string length $::dna($mother.%text)]==0}]]
	%root.html $mother [array get P]
}

		
 
section    top

puts "    generating html $base.html"
HTML $::dna(tag) [list ""] none $image
foreach manpage $::dna(manpages) {
	set tag $::dna($manpage.tag)
	set title $::dna($manpage.title)
	set msect $::dna($manpage.msect)
	puts "    manpage $title ($msect)"
	HTML [string toupper ${title}($msect)] [links [list $tag] doc] doc $doc/$title-$msect.html
}

		
 
section    top

proc toc {list} {
	set list [lsort -index 0 $list]
	set o [expr {([llength $list]+3)/4}]
	h TABLE WIDTH 100% {
		for {set i 0} {$i<$o} {incr i} {
			h TR {
				for {set j 0} {$j<4} {incr j} {
					setp {ref caption} [lindex $list [expr {$i+$j*$o}]]
					h TD WIDTH 25% ALIGN left VALIGN top {
						h A HREF $ref {
							h %content $caption
						}
					}
				}
			}
		}
	}
}

proc docIndex {} {
	global doc
	puts "indexing $doc"
	set dona {}
	array set manpage {}
	foreach fn [glob -nocomplain $doc/*.html] {	#*/
		set fn [file tail $fn]
		if {[string equal $fn index.html]} {
			;
		} elseif {[regexp {^([^/]+)-(\d[^/]+)[.]html$} $fn all tt se]} {
			lappend manpage($se) [list $fn $tt]
		} else {
			lappend dona [list $fn [file root $fn]]
		}
	}
	file mkdir $doc
	file delete $doc/index.html
	set ::dna(html) [open $doc/index.html w]
	h HTML {
		h HEAD {
			h TITLE {
				h %content DNAs and Man Pages
			}
		}
		h BODY {
			if {[llength $dona]} {	
				h H1 {h %content DNAs}
				toc $dona
			}
			if {[llength [array names manpage]]} {
				h H1 {h %content Man Pages}
				foreach se [lsort -dictionary [array names manpage]] {
					h H2 {h %content Section $se}
					toc $manpage($se)
				}
			}
		}
	}
	close $::dna(html)
}

		
   
   

Text and Code

   
top

proc TEXT {mother} {
	set text $::dna($mother.%text)
	foreach daughter $::dna($mother:children) {
		append text [TEXT $daughter]
	}
	return $text
}

proc EXPORT {category path mothers numberred} {
	puts "    export $category $path"
	set newtext ""
	foreach mother $mothers {
		append newtext [export $mother $numberred]
	}
	set newtext [string trim $newtext]
	if {[catch {open $path r} channel]} {
		set same 0
	} else {
		set oldtext [string trim [read $channel]]
		close $channel
		set same [string equal $newtext $oldtext]
	}
	if {$same} {
		puts "      up to date"
	} else {
		file mkdir [file dirname $path]
		file delete $path
		set channel [open $path w]
		puts $channel $newtext
		close $channel
	}
}

proc export {daughter {numberred 0} {expand code}} {
	$::dna($daughter.*export) $daughter $numberred $expand
}

proc %root.export {mother {numberred 0} {expand code}} {
	set text ""
	foreach daughter $::dna($mother:children) {
		append text [export $daughter $numberred $expand]
	}
	return $text
}

proc %content.export {daughter {numberred 0} {expand code}} {
	set line $::dna($daughter.%line)
	if {$line==0} {
		set numberred 0
	}
	if {$numberred} {
		set T "\n#line $line \"$::base.dna\"\n"
	} else {
		set T ""
	}
	append T $::dna($daughter.%text)
	return $T
}

proc %cdata.export {mother {numberred 0} {expand code}} {
	set line $::dna($mother.%line)
	if {$line==0} {
		set numberred 0
	}
	if {$numberred} {
		set T "\n#line $line \"$::base.dna\"\n"
	} else {
		set T ""
	}
	append T $::dna($mother.%text)
	foreach daughter $::dna($mother:children) {
		append T [export $daughter 0 $expand]
	}
	return $T
}

		
 
section    top

foreach x $::dna(exports) {
	set tag $::dna($x.tag); if {![string length $tag]} continue
	EXPORT $::dna($x.category) [uplevel #0 [list subst $::dna($x.path)]] [links [list $tag] code] 1
}

		
   
   

DNA Document Type Definition

   
top

<DTD dna element>
	<DTD control element>
		<DTD ver element>
		<DTD namespace element>
		<DTD command element>
		<DTD language element>
		<DTD manpage element>
		<DTD testbase element>
		<DTD import element>
		<DTD export element>
			<DTD file element>
			<DTD prelude element>
			<DTD postlude element>
			<DTD make element>
	<DTD section element>
		<DTD image element>
		<DTD doc element>
			<DTD asis element>
			<DTD em element>
			<DTD formula element>
			<DTD gloss element>
			<DTD ip element>
				<DTD label element>
			<DTD lines element>
			<DTD link element>
			<DTD msect element>
			<DTD name element>
			<DTD p element>
			<DTD rule element>
				<DTD opt element>
				<DTD or element>
				<DTD rep element>
			<DTD vw2 element>
			<DTD samp element>
			<DTD seealso element>
		<DTD declaration elements>
		<DTD code element>
			<DTD b element>
			<DTD c element>
			<DTD hide element>
			<DTD re element>
			<DTD string element>
			<DTD type element>
			<DTD v element>
			<DTD ref element>
		<DTD mx element>
			<DTD tn element>
				<DTD ta element>
				<DTD tt element>
				<DTD ft element>
				<DTD tx element>
				<DTD mr element>
				<DTD rr element>
				<DTD sc element>
				<DTD po element>
				<DTD ex element>

		
 
section    top

%element.delegate %root
%element.values {
	tag "" ab ""
}

		
 
section    top

<!ELEMENT dna (control,section+)>

		
 
section    top

dna.delegate %element
dna.values {
	*makescript control.makescript
}

		
   
   

DNA Control

   
top
15. DTD control element ::
<control>
Overall information about the DNA, its interfaces, and build.
tag
Software name.
ab
Table of contents title.

EXAMPLE

<control tag='sample' ab='Sample DNA'>
<export interface='temb.h'>
<file tag='INTERFACE'/>
</export>
<export implementation='temb.c'>
<file tag='IMPLEMENTATION'/>
</export>
<export object='temb.o'>
<import interface='blor.dna'/>
<make>
rule [
export object
] [
concat [import interface blor] [export interface] [export implementation]
] "
cc -O -o [export object] [export implementation]
"
</make>
</export>
<export package='temb[info sharedlibextension]'>
<import object='quake'/>
<make>
switch $tcl_platform(os) {
Darwin {
rule [
export package
] [
concat [import object] [export object]
] "
cc -dynamiclib [export package] [import object] [export object]
"
}
default {
rule [
export package
] [
concat [import object] [export object]
] "
cc -pipe -shared [export package] [import object] [export object]
"
}
}
</make>
</export>
</control>
produces the makefile
temb.o: blor.h temb.h temb.c
cc -O -o temb.o temb.c
temb.dylib: quake.o temb.o
cc -dynamiclib temb.dylib quake.o temb.o
or the makefile
temb.o: blor.h temb.h temb.c
cc -O -o temb.o temb.c
temb.so: quake.o temb.o
cc -pipe -shared temb.so quake.o temb.o


<!ELEMENT control (ver|namespace|command|language|manpage|testbase|export|import|make|prelude|postlude)*>
<!ATTLIST control tag CDATA #IMPLIED ab CDATA #IMPLIED>

		
 
section    top

control.delegate %element
control.values {
	*define control.define
	*html control.html
	*makescript control.makescript
}

		
 
section    top

proc control.define {mother props} {
	set ::dna(tag) $::dna($mother.tag)
	%root.define $mother $props
}

proc control.html {mother props} {
	array set P {detail {}}
	foreach daughter $::dna($mother:children) {
		foreach {p vv} [$::dna($daughter.*control-html) $daughter] {
			foreach v $vv {
				lappend P($p) $v
			}
		}
	}
	h TABLE CLASS control CELLSPACING 0 CELLPADDING 0 FRAME box RULES all {
		h TR {
			h TD CLASS controldetail {
				h DL CLASS nested {
					h DT CLASS em {h %content DNA.}
					h DD {h %content $::dna($mother.tag)}
					foreach pv $P(detail) {
						setp {p v} $pv
						h DT CLASS em {h %content $p.}
						html $v $props
					}
					set L [lsort -dictionary [array names P import.*]]
					if {[llength $L]} {
						h DT CLASS em {h %content Import.}
						h DD {
							h DL CLASS nested {
								foreach x $L {
									setp {im dt} [split $x .]
									h DT CLASS em {h %content [string totitle $dt].}
									foreach daughter $P($x) {
										html $daughter $props
									}
								}
							}
						}
					}
					set L [lsort -dictionary [array names P export.*]]
					if {[llength $L]} {
						h DT CLASS em {h %content Export.}
						h DD {
							h DL CLASS nested {
								foreach x $L {
									setp {ex dt} [split $x .]
									h DT CLASS em {h %content [string totitle $dt].}
									foreach daughter $P($x) {
										html $daughter $props
									}
								}
							}
						}
					}
				}
			}
			h TD CLASS controlabstract ROWSPAN 2 {
				h H1 CLASS sectionabstract {h %content $::dna($mother.ab)}
			}
		}
		h TR {
			h TD CLASS controltoc {
				h A NAME *TOC { }
				if {[llength $::dna(ab)]} {
					h DL CLASS nested {
						set first 1
						foreach ab $::dna(ab) {
							if {$first} {
								h DT CLASS em {h %content Sections.}
							}
							set first 0
							h DD {
								h A HREF [hash]$::dna($ab.*sectnum) {
									h %content $::dna($ab.ab)
								}
							}
						}
					}
				}
			}
		}
		set L [lsort -dictionary [array names P make.*]]
		if {[llength $L]} {
			h TR {
				h TD CLASS controlmakes COLSPAN 2 {
					h DL CLASS nested {
						h DT CLASS em {h %content Make.}
						h DD {
							h DL CLASS nested {
								foreach x $L {
									setp {mk dt} [split $x .]
									h DT CLASS em {h %content [string totitle $dt].}
									h DD {
										h PRE CLASS controlmakes {
											set T [string map {\t   } [join $P($x) \n]]
											h %content $T
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

		
 
section    top

18. Makefile script method :: The makescript extract items for the .transport command in the DNA's make file script. The script returns a property list with the possible properties

control
Sublist concatenated to the control parameters.
transport
Sublist concatenated to the import or export parameters.
prelude
Script text concatenated to the prelude.
make
Script text concatenated to the makefile script.
postlude
Script text concatenated to the postlude.

The generic version returns an empty property list. The document focuses on its dna element, and the dna element on the control element. Only the control element contributes to the makescript.

MAKESCRIPT
Recursively in the <control> gather information for the dynamic makefile script fragment (the makescript) and write it out.
makescript index
Extract makescript information and return in a property list.

proc MAKESCRIPT {} {
	puts "    makescript $::lib/make/$::base.make"
	array set C {transport {} prelude "" postlude "" make ""}
	foreach daughter $::dna(:children) {
		array set C [makescript $daughter]
	}
	file delete $::lib/make/$::base.make
	set channel [open $::lib/make/$::base.make w]
	set control {}
	foreach {p v} [array get C control.*] {
		lappend control [lindex [split $p .] 1] $v
	}
	puts $channel ".transport $::base [list $control] {
[leftflush [join $C(prelude) \n]]
} {
[leftflush [join $C(make) \n]]
} {
[leftflush [join $C(postlude) \n]]
} $C(transport)"
	close $channel
}

proc makescript {daughter} {
	$::dna($daughter.*makescript) $daughter
}

proc %root.makescript daughter {
	list
}

proc control.makescript mother {
	array set P {transport {} prelude {} postlude {} make {}}
	foreach daughter $::dna($mother:children) {
		foreach {p v} [makescript $daughter] {
			switch $p {
				transport {
					foreach {x y z} $v {
						lappend P($p) $x $y $z
					}
				}
				prelude - make - postlude {
					foreach x $v {
						lappend P($p) $x
					}
				}
				default {
					set P($p) $v
				}
			}
		}
	}
	array get P
}

		
 
section    top

proc dynamicMake {args} {
	evaller eval {
		array set ::serpiente {testbases {}}
		proc rule {targets args} {
			global serpiente
			switch -- [lindex $args 0] {
				: {set sep :; set args [lrange $args 1 end]}
				:: {set sep ::; set args [lrange $args 1 end]}
				default {set sep :}
			}
			switch -- [llength $args] {
				0 {
					error {no prerequisites nor make commands provided}
				}
				1 {
					set prerequisites [lindex $args 0]
					set commands ""
				}
				default {
					set prerequisites [lindex $args 0]
					set commands \n[join [lrange $args 1 end] \n]
				}
			}
			set commands [string map {\n \n\t} $commands]
			regsub -all {\n[ \t]*\n} $commands \n commands
			puts $serpiente(makefile) "[join $targets { }]$sep [join $prerequisites { }]$commands"
		}
		proc control {element} {
			global serpiente
			if {[info exists serpiente(control:$element,$serpiente(dona))]} {
				return [join $serpiente(control:$element,$serpiente(dona)) " "]
			} else {
				return ""
			}
		}
		proc import {category args} {
			global serpiente
			switch -- [llength $args] {
				0 {
					if {[info exists serpiente(import:$category,$serpiente(dona))]} {
						set imports [list]
						foreach dona $serpiente(import:$category,$serpiente(dona)) {
							if {[info exists serpiente(export:$category,$dona)]} {
								set imports [concat $imports $serpiente(export:$category,$dona)]
							}
						}
						return $imports
					} else {
						return [list]
					}
				}
				1 {
					set dona [lindex $args 0]
					if {[info exists serpiente(export:$category,$dona)]} {
						return $serpiente(export:$category,$dona)
					} else {
						return [list]
					}
				}
				default {error {too many arguments to import}}
			}
		}
		proc export {category} {
			global serpiente
			if {[info exists serpiente(export:$category,$serpiente(dona))]} {
				return $serpiente(export:$category,$serpiente(dona))
			} else {
				return [list]
			}
		}
		proc .transport {dona controls prelude script postlude args} {
			global serpiente lib
			if {[llength $args]%3!=0} {
				error {.transport called with wrong number of arguments}
			}
			set ::base [file tail [file root $dona]]
			set serpiente(dona) $::base.dna
			lappend serpiente(donas) $serpiente(dona)
			set serpiente(prelude:$serpiente(dona)) $prelude
			if {[file exists $serpiente(dona)]} {
				set serpiente(script:$serpiente(dona)) $script
			} else {
				set serpiente(script:$serpiente(dona)) ""
			}
			set serpiente(postlude:$serpiente(dona)) $postlude
			set antecedents {}
			set packages {}
			foreach {port category path} $args {
				#	If port is import, the path is the dna name.
				#		The exports of the path are serpiente(export:$category,$path).
				#	If port is export, the path is the exported file name,
				#		and the exporting dna is $serpiente(dona)
				set path [uplevel #0 [list subst $path]]
				if {[string equal $port import]} {
					set path [file tail [file root $path]].dna
				}
				lappend serpiente($port:$category,$serpiente(dona)) $path
				if {[string equal $port:$category import:package]} {
					lappend antecedents $path
				} elseif {[string equal $port:$category export:package]} {
					lappend packages $path
				}
			}
			set serpiente(control:sysreq,$serpiente(dona)) ""
			foreach {element value} $controls {
				set serpiente(control:$element,$serpiente(dona)) $value
			}
			array set control $controls
			if {[info exists control(testbase)]} {
				lappend serpiente(testbases) [uplevel #0 [list subst $control(testbase)]]
			}
			foreach package $packages {
				set fn [uplevel #0 [list subst $package]]
				if {[info exists control(ver)]} {
					set ver $control(ver)
				} else {
					set ver 1.0
				}
				set name [file root [file tail $fn]]
				set serpiente(control:name,$serpiente(dona)) $name
				if {[file exists $serpiente(dona)]} {
					if {![info exists serpiente(control:language,$serpiente(dona))]} {
						set serpiente(control:language,$serpiente(dona)) c
					}
					lappend serpiente(packages) $fn $name $ver $serpiente(control:language,$serpiente(dona)) \
							[file tail $fn] $antecedents  $serpiente(control:sysreq,$serpiente(dona))
				}
			}
		}
		proc .dynmake {} {
			global serpiente lib
			set serpiente(packages) {}
			foreach makepiece [glob $lib/make/*.make] { #*/
				uplevel #0 [list source $makepiece]
			}
			file delete makefile
			set serpiente(makefile) [open makefile w]
			rule pkgIndex.tcl $lib/pkgIndex.tcl
			set dependents {}
			set body "
					rm -rf $lib/pkgIndex.tcl
					echo '# pkg_mkIndex is too weak to use.' >$lib/pkgIndex.tcl
					echo '# All the information necessary is already in the control element,' >>$lib/pkgIndex.tcl
					echo '# so just use that.' >>$lib/pkgIndex.tcl
					echo '' >>$lib/pkgIndex.tcl"
			foreach {fn name ver language tailfn antecedents sysreq} $serpiente(packages) {
				switch -glob $sysreq {
					os=* {
						if {![string equal $::tcl_platform(os) [lindex [split $sysreq =] end]]} continue
					}
					platform=* {
						if {![string equal $::tcl_platform(platform) [lindex [split $sysreq =] end]]} continue
					}
				}
				lappend dependents $fn
				set script ""
				foreach antecedent $antecedents {
					if {[info exists serpiente(control:name,$antecedent)]} {
						append script "package require {$serpiente(control:name,$antecedent)}; "
					}
				}
				if {[string equal $language c]} {
					append script "load {\[file join \$\$dir \$\$::tcl_platform(os) [
							file root $tailfn]\[info sharedlibextension\]\]}"
				} else {
					append script "source {\[file join \$\$dir $tailfn\]}"
				}
				append body "\necho 'package ifneeded $name $ver \"$script\"' >>$lib/pkgIndex.tcl"
			}
			rule $lib/pkgIndex.tcl $dependents $body
			foreach test $serpiente(testbases) {
				rule test :: $test.html
				rule $test.html [list pkgIndex.tcl $test] "
						debug=\$\$debug $test -matrix -run | tee $test.out
				"
			}
			foreach dona $serpiente(donas) {
				set serpiente(dona) $dona
				set ::base [file tail [file root $dona]]
				uplevel #0 $serpiente(prelude:$serpiente(dona))
			}
			foreach dona $serpiente(donas) {
				set serpiente(dona) $dona
				set ::base [file tail [file root $dona]]
				uplevel #0 $serpiente(script:$serpiente(dona))
			}
			foreach dona $serpiente(donas) {
				set serpiente(dona) $dona
				set ::base [file tail [file root $dona]]
				uplevel #0 $serpiente(postlude:$serpiente(dona))
			}
			close $serpiente(makefile)
		}
	}
	puts "constructing makefile"
	evaller eval .dynmake
	puts "make $args"
	set ::making 1
	set ::makepipe [open "|make $args |& cat" r]
	fconfigure $::makepipe -blocking 0
	fileevent $::makepipe readable "
		puts -nonewline \[read $::makepipe\]
		if {\[eof $::makepipe\]} {
			close $::makepipe
			set ::making 0
		}
	"
	vwait ::making
	puts "make complete"
}

		
 
section    top

MAKESCRIPT

		
 
section    top

<!ELEMENT ver (#PCDATA)>

		
 
section    top

ver.delegate %element
ver.values {
	*define ver.define
	*html ver.html
	*control-html ver.control-html
	*makescript ver.makescript
	%span 1
}

		
 
section    top

version 1.0

		
 
section    top

proc ver.define {mother props} {
	set version [TEXT $mother]
	if {![catch {uri get file:build-cycle} cycle]} {append version .[string trim $cycle]}
	set ::dna(version) $version
	return {}
}

proc ver.control-html {daughter} {
	list detail [list [list Version $daughter]]
}
proc ver.html {daughter props} {
	h DD {h %content $::dna(version)}
}

proc ver.makescript daughter {
	list control.ver $::dna(version)
}

		
 
section    top

<!ELEMENT sysreq (#PCDATA)>

		
 
section    top

sysreq.delegate %element
sysreq.values {
	*html sysreq.html
	*control-html sysreq.control-html
	*makescript sysreq.makescript
}

		
 
section    top

proc sysreq.control-html {daughter} {
	list detail [list [list System-requirement $daughter]]
}
proc sysreq.html {daughter props} {
	h DD {h %content [TEXT $daughter]}
}

proc sysreq.makescript daughter {
	list control.sysreq [TEXT $daughter]
}

		
 
section    top

<!ELEMENT namespace (#PCDATA)>

		
 
section    top

namespace.delegate %element
namespace.values {
	*html namespace.html
	*control-html namespace.control-html
	*makescript namespace.makescript
	%span 1
}

		
 
section    top

proc namespace.control-html {daughter} {
	list detail [list [list Namespace $daughter]]
}
proc namespace.html {daughter props} {
	h DD {h %content [TEXT $daughter]}
}

proc namespace.makescript daughter {
	list control.namespace [TEXT $daughter]
}

		
 
section    top

<!ELEMENT command (#PCDATA)>

		
 
section    top

command.delegate %element
command.values {
	*html command.html
	*control-html command.control-html
	*makescript command.makescript
	%span 1
}

		
 
section    top

proc command.control-html {daughter} {
	list detail [list [list Command $daughter]]
}
proc command.html {daughter props} {
	h DD {h %content [TEXT $daughter]}
}

proc command.makescript daughter {
	list control.command [TEXT $daughter]
}

		
 
section    top

<!ELEMENT language (#PCDATA)>

		
 
section    top

language.delegate %element
language.values {
	*html language.html
	*control-html language.control-html
	*makescript language.makescript
	%span 1
}

		
 
section    top

proc language.control-html {daughter} {
	list detail [list [list Language $daughter]]
}
proc language.html {daughter props} {
	h DD {h %content [TEXT $daughter]}
}

proc language.makescript daughter {
	list control.language [TEXT $daughter]
}

		
 
section    top

<!ELEMENT manpage EMPTY>
<!ATTLIST manpage title CDATA #REQUIRED msect CDATA #REQUIRED tag CDATA #REQUIRED>

		
<