retroforth/build/generate-makefile.forth
crc 183c5bae38 Initial checkin (from 58fa921 in the old git repo)
FossilOrigin-Name: d2b8467883db80cb179089e1db1b1ed4dff1f11b4bee7086ee46d83f3ee0136e
2017-10-16 16:09:39 +00:00

201 lines
4.8 KiB
Forth
Executable file

#!/usr/bin/env rre
This is a tool to help in building RETRO. It will scan a source
file (in C), using comments in the file as directives to help it
in this process.
Currently this aims to support:
//HEAD
//FLAG
//USES
//LIBS
Each of these will be followed by one or more whitespace delimited
tokens. These will be broken apart and added to different lists if
not already present.
So a case:
#include <stdio.h>
//LIBS m curses
//USES nga bridge image
//FLAG -DPOSIX_FILES -DPOSIX_GETC -DPOSIX_ARGS
//FLAG -O3
....
This tool will generate a Makefile suitable for compiling the
source and its dependencies:
CC = clang
FLAGS = -DPOSIX_FILES -DPOSIX_GETC -DPOSIX_ARGS -O3
example:
$(CC) $(FLAGS) -c example.c -o example.o
$(CC) $(FLAGS) -c nga.c -o nga.o
$(CC) $(FLAGS) -c bridge.c -o bridge.o
$(CC) $(FLAGS) -c image.c -o image.o
$(CC) $(FLAGS) example.o nga.o bridge.o image.o -o example
rm example.o nga.o bridge.o image.o
The ultimate intention is to allow this to generate the Makefiles
for each interface automatically so that only the image rebuild
will need a manually written Makefile.
Begin by creating arrays for each type of directive.
~~~
'Uses d:create #1024 allot
'Flag d:create #1024 allot
'Libs d:create #1024 allot
~~~
Next up, some extensions to the `file:` namespace. The first new
word will read a line from the file and the second one uses the
first, calling a combinator after reading each line.
~~~
{{
'FID var
'FSize var
'Action var
'Buffer var
:-eof? (-f) @FID file:tell @FSize lt? ;
:preserve (q-) &FID [ &FSize [ call ] v:preserve ] v:preserve ;
---reveal---
:file:read-line (f-s)
!FID
[ here dup !Buffer buffer:set
[ @FID file:read dup buffer:add
[ ASCII:CR eq? ] [ ASCII:LF eq? ] [ ASCII:NUL eq? ] tri or or ] until
buffer:get drop ] buffer:preserve
@Buffer ;
:file:for-each-line (sq-)
[ !Action
file:R file:open !FID
@FID file:size !FSize
[ @FID file:read-line @Action call -eof? ] while
@FID file:close
] preserve ;
}}
~~~
With the `file:for-each-line`, it's easy to build something like
the Unix "cat" utility:
#!/usr/bin/env rre
#0 sys:argv [ puts nl ] file:for-each-line
So moving on, it's now time to begin the parser.
Our first word here is `split`, which will divide a string into
two parts. We check the second one (the first six characters)
against the commands we know how to deal with.
~~~
:split (s-ss)
[ #7 + ] [ #0 #6 s:substr ] bi ;
~~~
This is an addition to the `set:` namespace. Sets made using
`set:from-results` and similar are fixed size with no padding. The
sets here are 1024 cells each, so new items can be appended. This
word makes it easy to do so.
~~~
:set:append (na-)
dup v:inc dup fetch + store ;
~~~
And now for code to append unique items to each set.
TODO: refactor this - there's a lot of duplication.
~~~
:build:uses (s-)
dup &Uses set:contains-string?
[ drop ]
[ dup '#SRC:_ puts puts nl s:keep &Uses set:append ] choose ;
:build:libs (s-)
dup &Libs set:contains-string?
[ drop ]
[ dup '#LIB:_ puts puts nl s:keep &Libs set:append ] choose ;
:build:flag (s-)
dup &Flag set:contains-string?
[ drop ]
[ dup '#FLG:_ puts puts nl s:keep &Flag set:append ] choose ;
~~~
Next up is the scanner. This is currently as simple as possible:
- split the line
- see if it starts with a directive
- yes? process the directive
- no? discard the line
- repeat until done
~~~
:scan (s-)
[ split
'//USES [ dup build:uses '.c s:append scan ] s:case
'//HEAD [ '.h s:append scan ] s:case
'//LIBS [ build:libs ] s:case
'//FLAG [ build:flag ] s:case
drop-pair
] file:for-each-line ;
~~~
Getting near the end, the remaining words are needed to create the
Makefile. These are short, and each is intended to handle a simple
role.
~~~
:target (-s) #0 sys:argv s:to-upper ;
:libraries
target puts 'LIBS_=_ puts
&Libs [ '-l puts puts sp ] set:for-each nl ;
:flags
target puts 'FLAGS_=_ puts
&Flag [ puts sp ] set:for-each nl ;
:build-target (-)
#0 sys:argv puts $: putc nl ;
:compile-dependency (s-)
target over '\tclang_-c_%s.c_$(%sFLAGS)_-o_%s.o\n s:with-format puts ;
:dependencies (-)
&Uses [ compile-dependency ] set:for-each ;
:object-files (-)
target '\tclang_$(%sLIBS)_ s:with-format puts
&Uses [ puts '.o_ puts ] set:for-each
#0 sys:argv '_-o_%s\n s:with-format puts ;
:binary (-)
tab 'mv_ puts #0 sys:argv puts '_../bin puts nl ;
~~~
The top level "display-Makefile" is simple: all it has to do is list the
items we want in the proper order.
~~~
:display-Makefile (-)
(declare libraries
(declare flags
(specify build-target
(list dependencies
(link object-files
(move binary
;
~~~
~~~
#0 sys:argv dup build:uses '.c s:append scan
display-Makefile
~~~