source: project/wiki/eggref/5/csm @ 40140

Last change on this file since 40140 was 40140, checked in by felix winkelmann, 2 months ago

csm: missing example code

File size: 17.5 KB
Line 
1[[tags: egg]]
2[[toc:]]
3
4== csm
5
6{{csm}} is a build system for CHICKEN Scheme executables.
7
8=== Usage
9
10{{usage: csm [-help] [-n] [-d] [-static] [-r7rs] [-depends FILENAME] [-ignore FILENAME] [-main MODULE] [-D FEATURE] [-program NAME] [-scan] [-clean] [-dd] [-g] [-x] [-makefile FILENAME] [DIRECTORY] [OPTION ...]}}
11
12; -help : show informational message.
13; -n : "dry run", display performed operations but do not execute them.
14; -d : debug mode - show information about what the tool is doing.
15; -x : explain - show additional information why certain actions are performed.
16; -static : build static binaries.
17; -r7rs : assume all source code is R7RS code.
18; -depends FILENAME : all source files depend on FILENAME.
19; -ignore FILENAME : ignore given file or directory when scanning source files.
20; -program NAME : declare that an executable program with this name should be built.
21; -main MODULE : designate main module of executable to be built.
22; -D FEATURE : define given feature, similar to the {{csc}} option with the same name.
23; -scan : only scan source files, do not build.
24; -clean : clean build artifacts.
25; -dd : dump dependencies in {{make(1)}} format.
26; -g : generate makefile instead of building executables.
27; -makefile FILENAME : like {{-g}}, but generate FILENAME instead of {{Makefile}}.
28; OPTION ... : pass all other options to {{csc}}.
29; DIRECTORY : designates DIRECTORY as root source directory instead of the current directory.
30
31Only one of the options {{-scan}}, {{-clean}}, {{-dd}}, {{-g}} or {{-makefile}} can be used in
32the same invocation of {{csm}}.
33
34=== Principle of Operation
35
36{{csm}} analyzes all Scheme source files in a directory tree and extracts module
37and file dependencies. Source files can be organized arbitrarily but should contain
38at most one module. {{csm}} maps modules to source files and builds a dependency
39graph from {{import}} forms found during the file system traversal.
40
41Files having the extensions {{scm}}, {{ss}}, {{scheme}}, {{sch}}, {{r4rs}} or {{r5rs}}
42are assumed to contain Scheme source code. Files having the extensions {{sld}}
43or {{r7rs}} are assumed to contain R7RS source code. Files containing module
44definitions at toplevel are assumed to be components of one or more executable
45targets.
46
47The following toplevel forms are processed and used to infer file and module
48dependencies among the various components of a project:
49
50{{module}} and {{define-library}} (R7RS) determine nodes in the overall module
51dependency graph.
52
53{{import}}, {{import-for-syntax}} and {{import-syntax-for-syntax}} determine module
54dependencies.
55
56{{include}}, {{include-relative}}, {{include-ci}} (R7RS) and {{include-library-declarations}}
57(R7RS) determine file dependencies.
58
59The forms {{begin}}, {{begin0}}, {{begin-for-syntax}} are traversed recursively.
60
61{{bind-file}} and {{bind-file*}} record external dependencies to C/C++ files.
62
63Note that {{csm}} will not detect redefinitions or renamings of these forms, it assumes
64they have their default semantics.
65
66{{cond-expand}} is fully expanded and can be used to conditionalize file or module
67dependencies.
68
69Files having the extensions {{c}}, {{cpp}} or {{cxx}} are assumed to be native C/C++
70modules that should be compiled and linked to a module or program.
71
72After scanning the file tree, all modules and program targets are build if they do not
73yet exist or if one of their dependencies have been modified later than the target.
74
75By default, all modules are compiled to dynamically loadable shared objects ({{.so}}s).
76Modules that are designated as the main module of a standalone program are compiled
77as static ({{.o}}) files, as are direct dependencies of programs main modules. In
78fully static mode, all modules are compiled to static object files, with the exception
79of modules imported "for syntax". All source files containing modules are compiled
80using {{csc}}.
81
82The anticipated use case is for a project to have one or more programs,
83using any number of secondary modules. Programs are declared using "options" (see
84below) and can be conveniently compiled to static standalone executables. {{csm}}
85is not intended for eggs, for this the existing declarative
86[[http://api.call-cc.org/5/doc/chicken/eggs|egg]] specification format is sufficient.
87
88=== Options
89
90In addition to flags given on the {{csm}} command line, options for one or more
91source files can be defined using designated files in the source tree. If the root
92directory of the source tree or one of its child directories contains a file named
93{{all.options}} then that file should hold S-expressions giving command line options
94that are to be passed to all compiled source files containing a module definition,
95in the same directory or in a subdirectory. Program- or source-file specific options
96can be defined by creating a file named {{<PROGRAM>.options}} or
97{{<ROOTNAME>.options}} in the root source directory or in the directory where
98the source file is located. ROOTNAME should be the basename of the source
99file excluding the file extension.
100
101The options are read as s-expressions and should be quoted using {{"..."}} in
102case they are ambiguous. Lists in option files are spliced and {{cond-expand}}
103is detected and expanded so it is possible to use the {{#+FEATURE}} read syntax
104to conditionalize the options.
105
106Option files are added as dependencies, so should they change, targets depending
107on those files are rebuilt.
108
109The following additional options are detected and handled specially (all
110other options are simply passed on to {{csc}}):
111
112; -ignore FILENAME : ignore the file or directory FILENAME when walking the source tree.
113; -program NAME : declare program with this name to be a build target.
114; -main MODULE : declare main nodule for program(s) (defaults to name of program).
115; -depends OBJECT : module or program depends on source file, binary object file or module.
116; -static : the program (or all programs) should be build statically.
117
118As a special case, a clause {{-depends FILENAME.o}} declares a dependency on
119a C/C++ object file if a native module named FILENAME was detected.
120
121Note that {{-ignore}} and {{-depends}} do not accept wildcard pathnames.
122
123=== Conventions and Caveats
124
125* Each source file should contain a single module definition, unless the file is included using one of the available forms of {{include}}.
126* Module import-dependencies must be acyclic.
127* A modules may be imported normally or "for syntax" but not both.
128* Custom read syntax or complicated usage of modules at expansion time may not be correctly handled.
129
130=== Examples
131
132==== Trivial: "hello, word"
133
134The most basic project consisting of a single source file.
135
136<enscript highlight="scheme">
137% cat hello.scm
138(module hello ()
139  (import scheme)
140  (display "Hello, world!\n"))
141% csm
142  '/home/felix/.local/bin/csc' '-s' '-J' '-I' '/home/felix/csm/tests/hello' '-C' '-I' '-C' '/home/felix/csm/tests/hello' '/home/felix/csm/tests/hello/hello.scm' '-o' 'hello.so'
143% csi -q
144#;1> (import hello)
145; loading ./hello.import.scm ...
146; loading ./hello.so ...
147Hello, world!
148#;2> ,q
149</enscript>
150
151Let's create a standalone executable:
152<enscript highlight="scheme">
153% csm -clean
154% csm -program hello
155  '/home/felix/.local/bin/csc' '-o' 'hello' '-I' '/home/felix/csm/tests/hello' '-C' '-I' '-C' '/home/felix/csm/tests/hello' '/home/felix/csm/tests/hello/hello.scm'
156% hello
157Hello, world!
158</enscript>
159
160==== Complex: a window manager
161
162Here we generate an X11 window manager program that uses the foreign function
163interface to access Xlib. This is a more or less faithful reimplementation of
164[[http://incise.org/tinywm.html|TinyWM]].
165
166Scheme files:
167
168<enscript highlight="scheme">
169% cat wm.scm
170(module wm (start)
171  (import scheme)
172  (import (chicken base))
173  (import (wm x11))
174 
175(define (start)
176  (and-let* ((dpy (init_x11)))
177    (let ((start (make_button_event))
178          (ev (make_event))
179          (attr (make_attributes)))
180      (subwindow_set start 0)
181      (let loop ()
182        (XNextEvent dpy ev)
183        (cond ((and (= (event_type ev) KeyPress)
184                    (not (zero? (event_subwindow ev))))
185                (XRaiseWindow dpy (event_subwindow ev)))
186              ((and (= (event_type ev) ButtonPress)
187                    (not (zero? (event_subwindow ev))))
188                (XGetWindowAttributes dpy (event_subwindow ev) attr)
189                (button_event_copy start ev))
190              ((and (= (event_type ev) MotionNotify)
191                    (not (zero? (event_subwindow start))))
192                (let ((xdiff (- (event_x_root ev) (event_x_root start)))
193                      (ydiff (- (event_y_root ev) (event_y_root start))))
194                  (XMoveResizeWindow dpy (event_subwindow start)
195                    (+ (attributes_x attr)
196                       (if (= (event_button start) 1) xdiff 0))
197                    (+ (attributes_y attr)
198                       (if (= (event_button start) 1) ydiff 0))
199                    (max 1 (+ (attributes_width attr)
200                              (if (= (event_button start) 3) xdiff 0)))
201                    (max 1 (+ (attributes_height attr)
202                              (if (= (event_button start) 3) ydiff 0))))))
203              ((= (event_type ev) ButtonRelease)
204                (subwindow_set start 0)))
205        (loop)))))
206)
207% cat tinywm.scm
208(module tinywm ()
209  (import scheme)
210  (import (chicken base))
211  (import wm)
212  (start))
213% cat wrap.scm
214(module (wm x11) *
215  (import scheme)
216  (import bind)
217  (import (chicken foreign))
218  (foreign-declare "#include <X11/Xlib.h>")
219  (define KeyPress (foreign-value "KeyPress" int))
220  (define ButtonPress (foreign-value "ButtonPress" int))
221  (define MotionNotify (foreign-value "MotionNotify" int))
222  (define ButtonRelease (foreign-value "ButtonRelease" int))
223  (bind-file* "x11.h"))
224</enscript>
225
226C files:
227
228<enscript highlight="c">
229% cat x11.c
230#include <X11/Xlib.h>
231#include <stdlib.h>
232
233Display *init_x11(void) {
234    Display * dpy;
235    if(!(dpy = XOpenDisplay(0x0))) return NULL;
236    XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask,
237            DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync);
238    XGrabButton(dpy, 1, Mod1Mask, DefaultRootWindow(dpy), True,
239            ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None);
240    XGrabButton(dpy, 3, Mod1Mask, DefaultRootWindow(dpy), True,
241            ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None);
242    return dpy;
243}
244
245XButtonEvent *make_button_event(void) {return malloc(sizeof(XButtonEvent));}
246XEvent *make_event(void) {return malloc(sizeof(XEvent));}
247XWindowAttributes *make_attributes(void) {return malloc(sizeof(XWindowAttributes));}
248void subwindow_set(XButtonEvent *start, int win) {start->subwindow = win;}
249int event_type(XEvent *ev) {return ev->type;}
250int event_subwindow(XButtonEvent *ev) {return ev->subwindow;}
251void button_event_copy(XButtonEvent *ev1, XButtonEvent *ev2) {*ev1 = *ev2;}
252int event_x_root(XEvent *e) {return e->xmotion.x_root;}
253int event_y_root(XEvent *e) {return e->xmotion.y_root;}
254int attributes_x(XWindowAttributes *attr) {return attr->x;}
255int attributes_y(XWindowAttributes *attr) {return attr->y;}
256int attributes_width(XWindowAttributes *attr) {return attr->width;}
257int attributes_height(XWindowAttributes *attr) {return attr->height;}
258int event_button(XButtonEvent *ev) {return ev->button;}
259% cat x11.h
260int XMoveResizeWindow(Display *dpy, unsigned long win, int x, int y, unsigned int w, unsigned int h);
261int XNextEvent(Display *dpy, XEvent *ev);
262int XRaiseWindow(Display *dpy, unsigned long win);
263int XGetWindowAttributes(Display *dpy, unsigned long win, XWindowAttributes *attr);
264Display *init_x11(void);
265XButtonEvent *make_button_event(void);
266XEvent *make_event(void);
267XWindowAttributes *make_attributes(void);
268void subwindow_set(XButtonEvent *start, unsigned long win);
269int event_type(XEvent *ev);
270int event_subwindow(XButtonEvent *ev);
271void button_event_copy(XButtonEvent *ev1, XButtonEvent *ev2);
272int event_x_root(XEvent *e);
273int event_y_root(XEvent *e);
274int attributes_x(XWindowAttributes *attr);
275int attributes_y(XWindowAttributes *attr);
276int attributes_width(XWindowAttributes *attr);
277int attributes_height(XWindowAttributes *attr);
278int event_button(XButtonEvent *ev);
279</enscript>
280
281Options customizing the build:
282
283<enscript highlight="scheme">
284% cat all.options
285-program tinywm -C -I/usr/X11R6/include
286% cat tinywm.options
287-L -L/usr/X11R6/lib -L -lX11
288% cat wrap.options
289-depends x11.o
290</enscript>
291
292This time we generate a makefile:
293
294<enscript highlight="scheme">
295% csm -g
296% make
297/home/felix/.local/bin/csc '-c' '-s' '-I' '/home/felix/csm/tests/wm' '-C' '-I' '-C' '/home/felix/csm/tests/wm' '/home/felix/csm/tests/wm/x11.c' '-o' 'x11.o' '-C' '-I/usr/X11R6/include'
298/home/felix/.local/bin/csc '-s' '-J' '-I' '/home/felix/csm/tests/wm' '-C' '-I' '-C' '/home/felix/csm/tests/wm' '/home/felix/csm/tests/wm/wrap.scm' '-o' 'wm.x11.so' 'x11.o' 'x11.o' '-C' '-I/usr/X11R6/include'
299/home/felix/.local/bin/csc '-c' '-J' '/home/felix/csm/tests/wm/wm.scm' '-I' '/home/felix/csm/tests/wm' '-C' '-I' '-C' '/home/felix/csm/tests/wm' '-unit' 'wm' '-o' 'wm.o' '-C' '-I/usr/X11R6/include'
300/home/felix/.local/bin/csc '-o' 'tinywm' '-I' '/home/felix/csm/tests/wm' '-C' '-I' '-C' '/home/felix/csm/tests/wm' 'wm.o' '-uses' 'wm' '/home/felix/csm/tests/wm/tinywm.scm' '-C' '-I/usr/X11R6/include' '-L' '-L/usr/X11R6/lib' '-L' '-lX11'
301</enscript>
302
303Now let's try it out, using Xephyr(1):
304
305<enscript highlight="scheme">
306% Xephyr -ac -reset :1 &
307% DISPLAY=:1 tinywm &
308% DISPLAY=:1 xterm
309</enscript>
310
311==== An R7RS program
312
313Here we build the example given in the R7RS report:
314
315<enscript highlight="scheme">
316% cat main.scm
317(define (damped-oscillator R L C)
318  (lambda (state)
319    (let ((Vc (vector-ref state 0))
320          (Il (vector-ref state 1)))
321       (vector (- 0 (+ (/ Vc (* R C)) (/ Il C)))
322               (/ Vc L)))))
323
324(define (main count)
325  (let ((the-states
326          (integrate-system
327            (damped-oscillator 10000 1000 .001)
328            '#(1 0)
329            .01)))
330    (do ((i count (- i 1))
331         (states the-states (tail states)))
332        ((zero? i))
333      (let ((h (head states)))
334        (printf "~a ~a\n" (vector-ref h 0) (vector-ref h 1))))))
335% cat example.scm
336(define-library example
337  (import (scheme))
338  (import (integrate))
339  (import (chicken format))
340  (include "main.scm")
341  (begin (main 100)))
342% cat integrate.scm
343(define-library integrate
344  (import (scheme base))
345  (import (scheme lazy))
346  (export integrate-system head tail)
347  (include "integrate-implementation.scm"))
348% cat integrate-implementation.scm
349; The procedure integrate-system integrates the system
350; y′k = fk(y1, y2, . . . , yn), k = 1, . . . , n
351; of differential equations with the method of Runge-Kutta.
352; The parameter system-derivative is a function that
353; takes a system state (a vector of values for the state vari-
354; ables y1, . . . , yn) and produces a system derivative (the val-
355; ues y′1, . . . , y′n). The parameter initial-state provides
356; an initial system state, and h is an initial guess for the
357; length of the integration step.
358; The value returned by integrate-system is an infi-
359; nite stream of system states.
360
361(define (integrate-system system-derivative initial-state h)
362  (let ((next (runge-kutta-4 system-derivative h)))
363    (letrec ((states
364               (cons initial-state
365                     (delay (map-streams next states)))))
366      states)))
367
368(define (runge-kutta-4 f h)
369  (let ((*h (scale-vector h))
370        (*2 (scale-vector 2))
371        (*1/2 (scale-vector (/ 1 2)))
372        (*1/6 (scale-vector (/ 1 6))))
373    (lambda (y)
374      ;; y is a system state
375      (let* ((k0 (*h (f y)))
376             (k1 (*h (f (add-vectors y (*1/2 k0)))))
377             (k2 (*h (f (add-vectors y (*1/2 k1)))))
378             (k3 (*h (f (add-vectors y k2)))))
379        (add-vectors y
380          (*1/6 (add-vectors k0
381                             (*2 k1)
382                             (*2 k2)
383                             k3)))))))
384
385(define (elementwise f)
386  (lambda vectors
387    (generate-vector
388      (vector-length (car vectors))
389      (lambda (i)
390        (apply f
391               (map (lambda (v) (vector-ref v i))
392                    vectors))))))
393
394(define (generate-vector size proc)
395  (let ((ans (make-vector size)))
396    (letrec ((loop
397               (lambda (i)
398                 (cond ((= i size) ans)
399                       (else
400                         (vector-set! ans i (proc i))
401                         (loop (+ i 1)))))))
402      (loop 0))))
403
404(define add-vectors (elementwise +))
405
406(define (scale-vector s)
407  (elementwise (lambda (x) (* x s))))
408
409(define (map-streams f s)
410  (cons (f (head s))
411        (delay (map-streams f (tail s)))))
412
413(define head car)
414
415(define (tail stream)
416  (force (cdr stream)))
417; % cat all.options
418-program example
419</enscript>
420
421We recklessly decide to link the final program statically. Note that
422we could have added the {{-static}} to {{all.options}} to build statically by default:
423
424<enscript highlight="scheme">
425% csm -static
426  '/home/felix/.local/bin/csc' '-c' '-static' '-J' '/home/felix/csm/tests/rk/integrate.scm' '-I' '/home/felix/csm/tests/rk' '-C' '-I' '-C' '/home/felix/csm/tests/rk' '-X' 'r7rs' '-R' 'r7rs' '-M' '-unit' 'integrate' '-emit-link-file' 'integrate.link' '-o' 'integrate.o'
427  '/home/felix/.local/bin/csc' '-o' 'example' '-I' '/home/felix/csm/tests/rk' '-C' '-I' '-C' '/home/felix/csm/tests/rk' '-X' 'r7rs' '-R' 'r7rs' '-static' 'integrate.o' '-uses' 'integrate' '/home/felix/csm/tests/rk/example.scm'
428% example
429<lots of numbers>
430</enscript)
431
432=== Author
433
434Felix Winkelmann
435
436=== Repository
437
438This egg is hosted on the CHICKEN Subversion repository:
439
440[[https://anonymous@code.call-cc.org/svn/chicken-eggs/release/5/csm]]
441
442If you want to check out the source code repository of this egg and
443you are not familiar with Subversion, see [[/egg-svn-checkout|this page]].
444
445=== Version history
446
447; 0.1 : Initial release
448
449=== License
450
451BSD
Note: See TracBrowser for help on using the repository browser.