* $Id: setuputil.sno,v 1.73 2021-01-02 04:14:37 phil Exp $
* setuputil.sno - help build CSNOBOL4 LOAD()able libraries
* (on POSIX and Win32)
* Phil Budne November 3, 2013
* from sqlite3 build.sno, in imitation of Python setup/distutils

* included into (trivial) module setup.sno programs
* Not careful about local variables!
* This is NOT meant for general inclusion into programs!!

-INCLUDE 'host.sno'
-INCLUDE 'snopea.sno'
-INCLUDE 'basename.sno'
-INCLUDE 'cmp.sno'

* TODO
*   Honor more vars:
*	name
*	author -- use to generate "AUTHOR" section!!
*	version
*	description
*	url
*	download_url
*	extra_compile_args

**=pea
**=sect NAME
**snobol4setup \- SNOBOL4 Loadable module setup utility
**=sect SYNOPSIS
**B<snobol4 setup.sno> I<command>
**=sect DESCRIPTION
**The example program below (canonically called B<setup.sno>) can be invoked
**(portably across different operating systems)
**with the following commands:
**
**=sect OPTIONS
**=item build
**Compiles and links a dynamicly loadable executable file
**named I<modulename> (with the appropriate extension for the local system).
**I<modulename>B<.sno> will be created from:
**B<pre.sno> (if it exists), followed by lines from I<sourcefiles>:
**any instances of I<LOAD(...)> present in B<*> comments,
**any groups of lines starting with B<*=pea> and ending with B<*=cut> (inclusive),
**any comment lines bracketed by B<*=snobol4> and B<*=cut> (exclusive)
**will be copied without the leading B<*>,
**and finally B<funcs.sno> (if it exists).
**
**=item install
**installs binary, include and man page files.
**
**=item clean
**cleans up.
**
**=sect EXAMPLE
**=code
**-INCLUDE 'setuputil.sno'
**        t = TABLE()
**        t['module'] = 'I<modulename>'
**        t['sources'] = 'I<sourcefile>.c,I<othername.c>'
**        t['author'] = t['maintainer'] = 'I<Yourname Here>'
**        t['author_email'] = t['maintainer_email'] = 'I<name@doma.in>'
**        t['include_dirs'] = 'I<list,of,dirs,to,search>'
**        t['library_dirs'] = 'I<list,of,dirs,to,search>'
**        t['libraries'] = 'I<list,of,library,names,to,link,against>'
**        t['license'] = 'BSD'
**        setup(t)
**end
**=ecode
**
**If B<LOAD()> functions appear as the first thing on a comment line
**in the source files, they are output to the generated
**I<modulename>.sno include file so that the module and its functions
**are loaded when the include file is used.
**
**B<snopea>(7) directives in comments in the source files will be used
**to generate B<man>(7) and .html documentation files.
**
**=sect NOT CONFORMING TO
**Inspired by Python setuptools, easyinstall, et.al.
**=sect AUTHOR
**Philip L. Budne
**=sect BUGS
**Has not been tested with proprietary Un*x native toolchains.
**=sect SEE ALSO
**B<snobol4>(1), B<snobol4load>(3), B<snopea>(7)
**=cut
****************
* make code less confusing!
	setup.c = ','
	setup.s = ' '
*	tab
	setup.t = '	'

**************** prefetch config, so config.sno can override!
* (I really should NEVER have built the toolchain particulars
*  into the binary)

* get switch presets from envionment
	setup.verbose = DIFFER(HOST(HOST_GETENV, 'SETUPUTIL_VERBOSE')) 1
	setup.debug = HOST(HOST_GETENV, 'SETUPUTIL_DEBUG')

	DEFINE('fetch(name)')			:(fetch.end)
fetch	$name = HOST($('HOST_' name))		:f(freturn)
	terminal = DIFFER(setup.verbose) 'HOST_' name ' = ' $name
	:(return)
fetch.end

	fetch('CC')
	fetch('COPT')
	fetch('DL_CFLAGS')
	fetch('DL_EXT')
	fetch('DL_LD')
	fetch('DL_LDFLAGS')
	fetch('INCLUDE_DIR')
	fetch('OBJ_EXT')
	fetch('SETUP_SYS')
	fetch('CONFIG_CFLAGS')
	fetch('CONFIG_CPPFLAGS')
	fetch('CONFIG_LDFLAGS')

	cygwin = DIFFER(HOST() ? POS(0) BREAK(':') (':CYGWIN' | ':MSYS')) 1
	windows = DIFFER(HOST() ? POS(0) BREAK(':') ':Win') 1
	mingw = DIFFER(windows) IDENT(SETUP_SYS, "posix") 1

-INCLUDE 'config.sno'

****************
	DEFINE('setup(t)')			:(setup.end)
setup	TERMINAL = DIFFER(DATATYPE(t),'TABLE') 'setup needs TABLE'
+						:s(setup.fatal)
	podseen =

* build against distribution include files
	dist_build = FILE("../../snolib/setuputil.sno") 1

	i = HOST(HOST_FIRSTARG)
setup.switches
	cmd = HOST(HOST_ARGN, i)		:f(setup.usage)
	i = i + 1
	IDENT(cmd)				:s(setup.usage)
	cmd POS(0) '-'				:f(setup.cmd)
	setup.verbose = IDENT(cmd, '-v') 1	:s(setup.switches)
	setup.debug = IDENT(cmd, '-d') ' -g'	:s(setup.switches)
	:(setup.usage)

setup.cmd

	IDENT(cmd, 'help')			:s(setup.usage)

	lbl = 'setup.' SETUP_SYS '.' cmd 'cmd'
	FUNCTION(lbl)				:f(setup.not.found)
	TERMINAL = APPLY(lbl, t) t['module'] ' ' cmd ' succeeded.' :s(return)
	TERMINAL = t['module'] ' ' cmd ' failed.' :(setup.fatal)

setup.not.found
+	TERMINAL = SETUP_SYS ' ' cmd ' not found'
setup.usage
+	TERMINAL = 'Usage: snobol4 setup.sno [-v] [-d] COMMAND'
	TERMINAL = 'Commands: build test install clean help'
setup.fatal
+	&CODE = 1				:(freturn)
setup.end

****************************************************************
*	utilities

*	replace all instances of string "from" with "to"
	DEFINE('setup.repl(s,from,to)pat,pre,suf,x') :(setup.repl.end)
setup.repl
+	pat = (POS(0) BREAKX(SUBSTR(from,1,1))) . pre from REM . suf
setup.repl.loop
	s pat = pre to suf			:s(setup.repl.loop)
	setup.repl = s				:(RETURN)
setup.repl.end

*	return name of Dynamic Library file
	DEFINE('setup.dlname(t)')		:(setup.dlname.end)
setup.dlname
	setup.dlname = t['module'] DL_EXT	:(return)
setup.dlname.end

*	return name of snobol include file
	DEFINE('setup.sno(t)')			:(setup.sno.end)
setup.sno
	setup.sno = t['module'] '.sno'		:(return)
setup.sno.end

****************
*	generate -INCLUDE file from .c files
	DEFINE('setup.makeinclude(t)'
+		'modname,files,lines,line,runpea,inpea,extras')
+						:(setup.makeinclude.end)
setup.makeinclude
+	ou = IO_FINDUNIT()
	of = setup.sno(t)
	OUTPUT(.out, ou,, of)			:f(freturn)
	out = "* Generated by setuputil.sno setup.makeinclude() " DATE()
	modname = REPLACE(t['module'], &LCASE, &UCASE) '_DL'
	out = setup.t modname ' = "' t['module'] '"'
	iu = IO_FINDUNIT()

	runpea =	
***** copy pre.sno, if it exists
	INPUT(.in, iu,, 'pre.sno')		:f(smi.nopre)
	out = '**** pre.sno:'
smi.copy.pre
+	lines = lines + 1
	line = in				:f(smi.copy.pre.eof)
	line '=pea'				:f(smi.copy.pre.out)
	runpea = 1
smi.copy.pre.out
+	out = line				:s(smi.copy.pre)
smi.copy.pre.eof
+	ENDFILE(iu)
	DETACH(.in)
	out = '******** end pre.sno'
smi.nopre

***** loop for each C source file
* look at each source line for LOAD(....) prototypes, POD docs
* XXX maybe copy arbitrary code between two snopea markers
*	got the idea from Perl, which reused =cut for the end marker!
	files = t['sources'] setup.c
	lines = 0
smi.floop
+	files POS(0) BREAKX(setup.c) . file setup.c = :f(smi.close)
	iu = IO_FINDUNIT()
	INPUT(.in, iu,, file)			:f(smi.openerr)
	inpea =
smi.iloop
+	line = in				:f(smi.eof)

* copy SNOPOD sections to output
	differ(inpea)				:s(smi.inpea)
	line POS(0) span('*') '='		:f(smi.lloop)
	line POS(0) span('*') '=snobol4'	:s(smi.insno)
	runpea = 1
	inpea = 1
smi.inpea
+	out = line
	line POS(0) span('*') '=cut'		:f(smi.iloop)
	inpea =

* look for multiple LOAD()s on a line??!
smi.lloop
+	line BREAKX('L') ('LOAD' BAL) . load =	:f(smi.iloop)
	lines = lines + 1
	out = setup.t load    	 		:(smi.lloop)

* here in after =snobol4 (new in 2.2)
* copy lines (without leading stars) to out until =cut seen
* (I can SWEAR I got the idea from Perl!)
smi.insno
	line = in				:f(smi.eof)
	line POS(0) span('*') '=cut'		:s(smi.iloop)
	line POS(0) span('*') rem . out		:f(smi.insno)s(smi.insno)

smi.eof

***** copy funcs.sno, if it exists
	iu = IO_FINDUNIT()
	INPUT(.in, iu,, 'funcs.sno')		:f(smi.nofuncs)
	out = '**** funcs.sno:'
smi.copy.funcs
+	line = in				:f(smi.copy.funcs.eof)
	line '=pea'				:f(smi.copy.funcs.out)
	runpea = 1
smi.copy.funcs.out
+	out = line				:s(smi.copy.funcs)

smi.copy.funcs.eof
+	lines = lines + 1
	out = in				:s(smi.copy.funcs)
	ENDFILE(iu)
	DETACH(.in)
	out = '******** end funcs.sno'
smi.nofuncs
+	out = '**************** end ' t['module'] '.sno'
	ENDFILE(iu)
	DETACH(.in)

smi.close
+	ENDFILE(ou)
	DETACH(.out)
	TERMINAL = GT(lines,0) 'created ' of
* delete if EQ(lines,0) ???
	IDENT(runpea)				:s(smi.endman)

* man file
	mf = pn = 'snobol4' of
	mf '.sno' = '.3'
	htmlmf = mf '.html'

* page name
	pn '.sno' =

	TERMINAL = snopea(of, mf, pn, '3', 'roff') 'created ' mf
	TERMINAL = snopea(of, htmlmf, pn, '3', 'html') 'created ' htmlmf
smi.endman

*** process 'extras' files for 'snopea' docs
	extras = t['extras'] ','
smi.extras
+	extras break(',') . fn ',' =		:f(smi.done)
	ident(fn)				:s(smi.done)

	iu = IO_FINDUNIT()
	INPUT(.in, iu,, fn)			:s(smi.checkpea)
	TERMINAL = 'could not open "extras": ' fn :(smi.extras)

	runpea =
smi.checkpea
+	line = in				:f(smi.checkpea.eof)
	line POS(0) SPAN('*') '=pea'		:f(smi.checkpea)
	runpea = 1
smi.checkpea.eof
+	DETACH(.in)
	ENDFILE(iu)
	IDENT(runpea)				:s(smi.done)

	pn = 'snobol4' fn
	pn '.sno' =
	mf = pn '.3'
	htmlmf = mf '.html'

	TERMINAL = snopea(fn, mf, pn, '3', 'roff') 'created ' mf
	TERMINAL = snopea(fn, htmlmf, pn, '3', 'html') 'created ' htmlmf
+	:(smi.extras)

smi.done
+	:(return)

smi.openerr
	TERMINAL = "Could not open " file	:(freturn)
setup.makeinclude.end
****************
*	get sourcefile list
	DEFINE('setup.sources(t)tmp')		:(setup.sources.end)
setup.sources
+	setup.sources = t['sources']
+	:(return)
setup.sources.end

*
****************************************************************
*	POSIX

* can be used on any system where zero status code means success
* (ie; NOT VMS!!!)
	DEFINE('setup.posix.system(command)')	:(setup.posix.system.end)
setup.posix.system
	TERMINAL = DIFFER(setup.verbose) 'command: ' command
	IDENT(command)				:s(FRETURN)
	&CODE = HOST(HOST_SYSCMD, command)	:f(FRETURN)
	TERMINAL = DIFFER(setup.verbose) 'status: ' &CODE
	EQ(&CODE, 0)		  		:s(RETURN)f(FRETURN)
setup.posix.system.end

	DEFINE('setup.posix.objs(t)srcs,file')	:(setup.posix.objs.end)
setup.posix.objs
	srcs = setup.repl(setup.sources(t) setup.c, '.c,', OBJ_EXT ' ')
	srcs = setup.repl(srcs setup.c, '.cpp,', OBJ_EXT ' ')
setup.posix.objs.loop
	IDENT(srcs)				:s(RETURN)
	srcs (break(' ') ' ') . file =		:f(RETURN)
	file = basename(file)
	setup.posix.objs = setup.posix.objs file :(setup.posix.objs.loop)
setup.posix.objs.end

****************
*	POSIX build command
*		requires C compiler have -c and -I options
*		and linker/loader with -o option

	DEFINE('setup.posix.buildcmd(t)')	:(setup.posix.buildcmd.end)
setup.posix.buildcmd
  	iflags = ' -I' INCLUDE_DIR
	iflags = DIFFER(dist_build) ' -I../.. -I../../include'
	iflags = DIFFER(dist_build) DIFFER(mingw) iflags ' -I../../config/win32'

setup.posix.buildcmd2
	idirs = t['include_dirs']
	iflags = DIFFER(idirs) iflags setup.repl(setup.c idirs, setup.c, ' -I')


	sources = setup.repl(setup.sources(t), setup.c, setup.s)

* build "dynamic library" (DL) -- something attached at runtime
	compile = CC ' -c ' COPT
+		' ' DL_CFLAGS setup.debug
+		(DIFFER(t['cflags']) ' ' t['cflags'], '')
+		(DIFFER(CONFIG_CFLAGS) ' ' CONFIG_CFLAGS, '')
+		(DIFFER(CONFIG_CPPFLAGS) ' ' CONFIG_CPPFLAGS, '')
+		iflags
+		' ' sources
	setup.posix.system(compile)		 :f(FRETURN)

	libdirs = t['library_dirs']
	libdirs = DIFFER(libdirs) setup.repl(setup.c libdirs, setup.c, ' -L')
	libdirs = DIFFER(mingw cygwin) libdirs ' -L' (DIFFER(dist_build) '../..',
+							HOST(HOST_SNOLIB_VERS))

	libs = t['libraries']
	libs = DIFFER(libs) setup.repl(setup.c libs, setup.c, ' -l')
	libs = DIFFER(mingw cygwin) libs ' -lsnobol4'

	link = DL_LD
+		' ' DL_LDFLAGS
+		' -o ' setup.dlname(t) ' ' setup.posix.objs(t)
+		libdirs
+		' ' CONFIG_LDFLAGS
+		libs
+		' ' t['ldflags']

	setup.posix.system(link)		:f(FRETURN)
	setup.makeinclude(t)			:(RETURN)
setup.posix.buildcmd.end

****************
*	POSIX test command
*		uses cmp.sno

	DEFINE('setup.posix.testcmd(t)i,tmp,tests,fn,base,snobol')
+						:(setup.posix.testcmd.end)
setup.posix.testcmd
	snobol = HOST(2, 0) ' -b'
	i = 1
setup.posix.snoloop
	tmp = HOST(2, i)			:f(setup.posix.testcmd.tests)
	tmp POS(0) '-'				:f(setup.posix.testcmd.tests)
	snobol = snobol ' ' tmp
	i = i + 1				:(setup.posix.snoloop)

setup.posix.testcmd.tests
	i = 0
	tests = DIFFER(t['tests']) t['tests'] ',' :s(setup.posix.testcmd.loop)
	tests = FILE('test.sno') 'test.sno,'	:s(setup.posix.testcmd.loop)
	TERMINAL = 'no tests found'		:(RETURN)

setup.posix.testcmd.loop
+	tests BREAK(',') . fn ',' =		:f(setup.posix.testcmd.done)
	base = fn
	base '.sno' =
	testerr = ident(setup.verbose) ' 2>/dev/null'
	setup.posix.system(snobol ' ' fn ' >' base '.tmp' testerr)
+						:f(setup.posix.testcmd.failed)
	cmp(base '.tmp', base '.ref', 1)	:s(setup.posix.testcmd.ok)
setup.posix.testcmd.failed
	TERMINAL = fn ': FAILED'
* i is error count
	i = i + 1				:(setup.posix.testcmd.loop)

setup.posix.testcmd.ok
	DELETE(base '.tmp')
	TERMINAL = fn ': OK'			:(setup.posix.testcmd.loop)

setup.posix.testcmd.done
	EQ(i, 0)				:S(RETURN)F(FRETURN)

setup.posix.testcmd.end
****************
*	POSIX install command
*		uses "cp -f"

	DEFINE('setup.posix.installcmd(t)')	:(setup.posix.installcmd.end)
setup.posix.installcmd

* if no CVS directory-- install as a "local" file
	dest = (FILE_ISDIR('CVS') HOST(HOST_SNOLIB_VLIB), 
+			HOST(HOST_SNOLIB_VLOCAL))

	destdir = HOST(HOST_GETENV, 'DESTDIR')
	libfile = setup.dlname(t)
	shared = (HOST(HOST_SHARED_OBJ_SUBDIR), 'shared')
	command = 'cp -f ' libfile ' ' destdir dest '/' shared
	setup.posix.system(command)		:f(FRETURN)

	sno = setup.sno(t)
	snofiles = sno

* get man files:
	man = 'snobol4' sno
	man '.sno' = '.3'
	manfiles = (FILE(man) man, '')

	x = t['extras'] ','
	xfiles =
setup.posix.instcmd.xloop
	x break(',') . fn ',' =			:f(setup.posix.instcmd.xdone)
	ident(fn)				:s(setup.posix.instcmd.xdone)
	snofiles = FILE(fn) snofiles ' ' fn
	fn '.sno' = '.3'
	fn = 'snobol4' fn
	manfiles = FILE(fn) manfiles ' ' fn 	:(setup.posix.instcmd.xloop)
setup.posix.instcmd.xdone

	command = 'cp -f ' snofiles ' ' destdir dest
	setup.posix.system(command)		:f(FRETURN)

	IDENT(manfiles)				:s(RETURN)
	command = 'cp -f ' manfiles ' ' destdir MAN3DIR
	setup.posix.system(command)		:f(FRETURN)
	differ(MAN_COMPRESS) setup.posix.system("cd " destdir MAN3DIR "; " MAN_COMPRESS " " manfiles)
	:(RETURN)
setup.posix.installcmd.end

****************
*	POSIX clean command
*		uses "rm -f"

	DEFINE('setup.posix.cleancmd(t)sno,man,x') :(setup.posix.cleancmd.end)
setup.posix.cleancmd
* XXX move path generation into a function???
	sno = setup.sno(t)
	man = 'snobol4' sno
	man '.sno' = '.3'
	man = man ' ' man '.html'

	x = t['extras'] ','
	xfiles =
setup.posix.cleancmd.xloop
	x break(',') . fn ',' =			:f(setup.posix.cleancmd.xdone)
	ident(fn)				:s(setup.posix.cleancmd.xdone)
	fn '.sno' = '.3'
	fn = 'snobol4' fn
	xfiles = xfiles ' ' fn ' ' fn '.html'	:(setup.posix.cleancmd.xloop)
setup.posix.cleancmd.xdone
	command = 'rm -f ' setup.posix.objs(t) setup.dlname(t) ' ' sno ' ' man
+				xfiles
	setup.posix.system(command)		:s(RETURN)f(FRETURN)
setup.posix.cleancmd.end
*
****************************************************************
* Windows with MicroSoft C

	DEFINE('setup.win.msc.basename(fname)') :(setup.win.msc.basename.end)
setup.win.msc.basename
	setup.win.msc.basename = basename(fname, '/\')	:(RETURN)
setup.win.msc.basename.end

	DEFINE('setup.win.msc.objs(t)srcs,file') :(setup.win.msc.objs.end)
setup.win.msc.objs
	srcs = setup.repl(setup.sources(t) setup.c, '.c,', OBJ_EXT ' ')
	srcs = setup.repl(srcs setup.c, '.cpp,', OBJ_EXT ' ')

setup.win.msc.objs.loop
	IDENT(srcs)				:s(RETURN)
	srcs (break(' ') ' ') . file =		:f(RETURN)
	file = setup.win.msc.basename(file)
	setup.win.msc.objs = setup.win.msc.objs file :(setup.win.msc.objs.loop)
setup.win.msc.objs.end

****************
*	Windows build command using MSC

	DEFINE('setup.win.msc.buildcmd(t)')	:(setup.win.msc.buildcmd.end)
setup.win.msc.buildcmd
  	iflags = (IDENT(dist_build) ' -I' INCLUDE_DIR,
+			' -I..\.. -I..\..\include -I..\..\config\win32')

setup.win.msc.buildcmd2
	idirs = t['include_dirs']
	iflags = DIFFER(idirs) iflags setup.repl(setup.c idirs, setup.c, ' -I')

	sources = setup.repl(setup.sources(t), setup.c, setup.s)

* build "dynamic library" (DL) -- something attached at runtime
	compile = CC ' -c -DHAVE_CONFIG_H ' COPT
+		' ' DL_CFLAGS
+		' ' t['cflags']
+		' ' iflags
+		' ' sources
	setup.posix.system(compile)		 :f(FRETURN)

	libdirs = t['library_dirs']
	libdirs = DIFFER(libdirs)
+		setup.repl(setup.c libdirs, setup.c, ' /LIBPATH:')

* XXX might need quotes on HOST_SNOLIB_VERS????
	snobol4lib_dir = ' /LIBPATH:' (DIFFER(dist_build) '..\..',
+					HOST(HOST_SNOLIB_VERS))

	libs = t['libraries']
	libs = DIFFER(libs) setup.repl(libs setup.c, setup.c, '.lib ')

	link = DL_LD
+		' ' DL_LDFLAGS
+		' /OUT:' setup.dlname(t)
+		' ' t['ldflags']
+		' ' setup.win.msc.objs(t)
+		' ' libdirs snobol4lib_dir
+		' ' libs 'snobol4.lib'

	setup.posix.system(link)		:f(FRETURN)
	setup.makeinclude(t)			:(RETURN)
setup.win.msc.buildcmd.end

****************
*	Windows install command w/o MINGW
*		uses "copy /y"

	DEFINE('setup.win.msc.installcmd(t)')	:(setup.win.msc.installcmd.end)
setup.win.msc.installcmd

* if no CVS directory-- install as a "local" file
	dest = (FILE_ISDIR('CVS') HOST(HOST_SNOLIB_VLIB), 
+			HOST(HOST_SNOLIB_VLOCAL))
	htmldir = HOST(HOST_SNOLIB_VERS) '\doc'

	libfile = setup.dlname(t)
	FILE(libfile)				:F(FRETURN)
	shared = (HOST(HOST_SHARED_OBJ_SUBDIR), 'shared')
	command = 'copy /y ' libfile ' "' dest '\' shared '"'
	setup.posix.system(command)

* snobol4<module>.sno, snobol4<module>.html
	fn = setup.sno(t)
	FILE(fn) setup.posix.system('copy /y ' fn ' "' dest '"')

	fn = 'snobol4' fn
	fn '.sno' = '.3.html'
	FILE(fn) setup.posix.system('copy /y ' fn ' "' htmldir '"')

	x = t['extras'] ','
setup.win.msc.instcmd.xloop
	x break(',') . fn ',' =			:f(setup.win.msc.instcmd.xdone)
	ident(fn)				:s(setup.win.msc.instcmd.xdone)

	FILE(fn) setup.posix.system('copy /y ' fn ' "' dest '"')

	fn = 'snobol4' fn
	fn '.sno' = '.3.html'
	FILE(fn) setup.posix.system('copy /y ' fn ' "' htmldir '"')
	:(setup.win.msc.instcmd.xloop)
setup.win.msc.instcmd.xdone			:(RETURN)

setup.win.msc.installcmd.end

****************
*	Windows clean command w/ Microsoft C
*		uses "erase"

	DEFINE('setup.win.msc.cleancmd(t)sno,man,x') :(setup.win.msc.cleancmd.end)
setup.win.msc.cleancmd
* XXX move path generation into a function???
	sno = setup.sno(t)
	man = 'snobol4' sno
	man '.sno' = '.3'
	man = man ' ' man '.html'

	x = t['extras'] ','
	xfiles =
setup.win.msc.cleancmd.xloop
	x break(',') . fn ',' =			:f(setup.win.msc.cleancmd.xdone)
	ident(fn)				:s(setup.win.msc.cleancmd.xdone)
	fn '.sno' = '.3'
	fn = 'snobol4' fn
	xfiles = xfiles ' ' fn ' ' fn '.html'	:(setup.win.msc.cleancmd.xloop)
setup.win.msc.cleancmd.xdone
	dll = setup.dlname(t)
	exp = lib = dll
	exp '.dll' = '.exp'
	lib '.dll' = '.lib'
	command = 'erase ' setup.win.msc.objs(t) dll ' ' sno ' ' man ' ' exp ' ' lib
+				xfiles
	setup.posix.system(command)		:s(RETURN)f(FRETURN)
setup.win.msc.cleancmd.end
*

****************************************************************
* more systems here


