Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
[[tags: egg]] [[toc:]] == csm {{csm}} is a build system for CHICKEN Scheme executables. === Usage {{usage: csm [-help] [-n] [-d] [-static] [-r7rs] [-depends FILENAME] [-ignore FILENAME] [-main MODULE] [-D FEATURE] [-program NAME] [-scan] [-clean] [-dd] [-g] [-x] [-csm-extend FILENAME] [-makefile FILENAME] [-max-procs N] [-compile-imports] [DIRECTORY] [OPTION ...]}} ; -help : show informational message. ; -n : "dry run", display performed operations but do not execute them. ; -d : debug mode - show information about what the tool is doing. ; -x : explain - show additional information why certain actions are performed. ; -static : build static binaries. ; -r7rs : assume all source code is R7RS code. ; -depends FILENAME : all source files depend on FILENAME. ; -ignore FILENAME : ignore given file or directory when scanning source files. ; -program NAME : declare that an executable program with this name should be built. ; -main MODULE : designate main module of executable to be built. ; -D FEATURE : define given feature, similar to the {{csc}} option with the same name. ; -scan : only scan source files, do not build. ; -clean : clean build artifacts. ; -dd : dump dependencies in {{make(1)}} format. ; -g : generate makefile instead of building executables. ; -csm-extend FILENAME : loads FILENAME into csm to allow using read-syntax extensions defined in external files. ; -makefile FILENAME : like {{-g}}, but generate FILENAME instead of {{Makefile}}. ; -max-procs N : compile at most N files in parallel (alias: {{-j}}), the default is 1. ; -compile-imports : compile import libraries after they are generated. ; OPTION ... : pass all other options to {{csc}}. ; DIRECTORY : designates DIRECTORY as root source directory instead of the current directory. Only one of the options {{-scan}}, {{-clean}}, {{-dd}}, {{-g}} or {{-makefile}} can be used in the same invocation of {{csm}}. === Principle of Operation {{csm}} analyzes all Scheme source files in a directory tree and extracts module and file dependencies. Source files can be organized arbitrarily but should contain at most one module. {{csm}} maps modules to source files and builds a dependency graph from {{import}} forms found during the file system traversal. Files having the extensions {{scm}}, {{ss}}, {{scheme}}, {{sch}}, {{r4rs}} or {{r5rs}} are assumed to contain Scheme source code. Files having the extensions {{sld}} or {{r7rs}} are assumed to contain R7RS source code. Files containing module definitions at toplevel are assumed to be components of one or more executable targets. The following toplevel forms are processed and used to infer file and module dependencies among the various components of a project: {{module}}, {{functor}} and {{define-library}} (R7RS) determine nodes in the overall module dependency graph. {{import}}, {{import-for-syntax}}, {{import-syntax-for-syntax}} and the two supported flavours of {{module NAME = ...}} (functor instantiation) determine module dependencies. {{include}}, {{include-relative}}, {{include-ci}} (R7RS) and {{include-library-declarations}} (R7RS) determine file dependencies. The forms {{begin}}, {{begin0}}, {{begin-for-syntax}} are traversed recursively. {{bind-file}} and {{bind-file*}} record external dependencies to C/C++ files. Note that {{csm}} will not detect redefinitions or renamings of these forms, it assumes they have their default semantics. {{cond-expand}} is fully expanded and can be used to conditionalize file or module dependencies. Files having the extensions {{c}}, {{cpp}} or {{cxx}} are assumed to be native C/C++ modules that should be compiled and linked to a module or program. After scanning the file tree, all modules and program targets are build if they do not yet exist or if one of their dependencies have been modified later than the target. By default, all modules are compiled to dynamically loadable shared objects ({{.so}}s). Modules that are designated as the main module of a standalone program are compiled as static ({{.o}}) files, as are direct dependencies of programs main modules. In fully static mode, all modules are compiled to static object files, with the exception of modules imported "for syntax". All source files containing modules are compiled using {{csc}}. The anticipated use case is for a project to have one or more programs, using any number of secondary modules. Programs are declared using "options" (see below) and can be conveniently compiled to static standalone executables. {{csm}} is not intended for eggs, for this the existing declarative [[http://api.call-cc.org/6/doc/chicken/eggs|egg]] specification format is sufficient. === Options In addition to flags given on the {{csm}} command line, options for one or more source files can be defined using designated files in the source tree. If the root directory of the source tree or one of its child directories contains a file named {{all.options}} then that file should hold S-expressions giving command line options that are to be passed to all compiled source files containing a module definition, in the same directory or in a subdirectory. Program- or source-file specific options can be defined by creating a file named {{<PROGRAM>.options}} or {{<ROOTNAME>.options}} in the root source directory or in the directory where the source file is located. ROOTNAME should be the basename of the source file excluding the file extension. The options are read as S-expressions and should be quoted using {{"..."}} in case they are ambiguous (like {{-i}} or {{-I}}). Lists in option files are spliced and {{(cond-expand ...)}} is detected and expanded so it is possible to use the {{#+FEATURE}} read syntax to conditionalize the options. Option files are added as dependencies, so should they change, targets depending on those files are rebuilt. The following additional options are detected and handled specially (all other options are simply passed on to {{csc}}): ; -ignore FILENAME : ignore the file or directory FILENAME when walking the source tree. ; -program NAME : declare program with this name to be a build target. ; -main MODULE : declare main module for program(s) (defaults to name of program). ; -depends OBJECT : module or program depends on source file, binary object file or module. ; -static : the program (or all programs) should be build statically. As a special case, a clause {{-depends FILENAME.o}} declares a dependency on a C/C++ object file if a native module named FILENAME was detected. Note that {{-ignore}} and {{-depends}} do not accept wildcard pathnames. === Conventions and Caveats * Each source file should contain a single module definition, unless the file is included using one of the available forms of {{include}}. * Module import-dependencies must be acyclic. * A modules may be imported normally or "for syntax" but not both. * Custom read syntax or complicated usage of modules at expansion time may not be correctly handled. === Examples ==== Trivial: "hello, word" The most basic project consisting of a single source file. <enscript highlight="scheme"> % cat hello.scm (module hello () (import scheme) (display "Hello, world!\n")) % csm '/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' % csi -q #;1> (import hello) ; loading ./hello.import.scm ... ; loading ./hello.so ... Hello, world! #;2> ,q </enscript> Let's create a standalone executable: <enscript highlight="scheme"> % csm -clean % csm -program hello '/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' % hello Hello, world! </enscript> ==== Complex: a window manager Here we generate an X11 window manager program that uses the foreign function interface to access Xlib. This is a more or less faithful reimplementation of [[http://incise.org/tinywm.html|TinyWM]]. The [[/eggref/6/bind|bind]] extension is required to build this example. Scheme files: <enscript highlight="scheme"> % cat wm.scm (module wm (start) (import scheme) (import (chicken base)) (import (wm x11)) (define (start) (and-let* ((dpy (init_x11))) (let ((start (make_button_event)) (ev (make_event)) (attr (make_attributes))) (subwindow_set start 0) (let loop () (XNextEvent dpy ev) (cond ((and (= (event_type ev) KeyPress) (not (zero? (event_subwindow ev)))) (XRaiseWindow dpy (event_subwindow ev))) ((and (= (event_type ev) ButtonPress) (not (zero? (event_subwindow ev)))) (XGetWindowAttributes dpy (event_subwindow ev) attr) (button_event_copy start ev)) ((and (= (event_type ev) MotionNotify) (not (zero? (event_subwindow start)))) (let ((xdiff (- (event_x_root ev) (event_x_root start))) (ydiff (- (event_y_root ev) (event_y_root start)))) (XMoveResizeWindow dpy (event_subwindow start) (+ (attributes_x attr) (if (= (event_button start) 1) xdiff 0)) (+ (attributes_y attr) (if (= (event_button start) 1) ydiff 0)) (max 1 (+ (attributes_width attr) (if (= (event_button start) 3) xdiff 0))) (max 1 (+ (attributes_height attr) (if (= (event_button start) 3) ydiff 0)))))) ((= (event_type ev) ButtonRelease) (subwindow_set start 0))) (loop))))) ) % cat tinywm.scm (module tinywm () (import scheme) (import (chicken base)) (import wm) (start)) % cat wrap.scm (module (wm x11) * (import scheme) (import bind) (import (chicken foreign)) (foreign-declare "#include <X11/Xlib.h>") (define KeyPress (foreign-value "KeyPress" int)) (define ButtonPress (foreign-value "ButtonPress" int)) (define MotionNotify (foreign-value "MotionNotify" int)) (define ButtonRelease (foreign-value "ButtonRelease" int)) (bind-file* "x11.h")) </enscript> C files: <enscript highlight="c"> % cat x11.c #include <X11/Xlib.h> #include <stdlib.h> Display *init_x11(void) { Display * dpy; if(!(dpy = XOpenDisplay(0x0))) return NULL; XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync); XGrabButton(dpy, 1, Mod1Mask, DefaultRootWindow(dpy), True, ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None); XGrabButton(dpy, 3, Mod1Mask, DefaultRootWindow(dpy), True, ButtonPressMask|ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None); return dpy; } XButtonEvent *make_button_event(void) {return malloc(sizeof(XButtonEvent));} XEvent *make_event(void) {return malloc(sizeof(XEvent));} XWindowAttributes *make_attributes(void) {return malloc(sizeof(XWindowAttributes));} void subwindow_set(XButtonEvent *start, int win) {start->subwindow = win;} int event_type(XEvent *ev) {return ev->type;} int event_subwindow(XButtonEvent *ev) {return ev->subwindow;} void button_event_copy(XButtonEvent *ev1, XButtonEvent *ev2) {*ev1 = *ev2;} int event_x_root(XEvent *e) {return e->xmotion.x_root;} int event_y_root(XEvent *e) {return e->xmotion.y_root;} int attributes_x(XWindowAttributes *attr) {return attr->x;} int attributes_y(XWindowAttributes *attr) {return attr->y;} int attributes_width(XWindowAttributes *attr) {return attr->width;} int attributes_height(XWindowAttributes *attr) {return attr->height;} int event_button(XButtonEvent *ev) {return ev->button;} % cat x11.h int XMoveResizeWindow(Display *dpy, unsigned long win, int x, int y, unsigned int w, unsigned int h); int XNextEvent(Display *dpy, XEvent *ev); int XRaiseWindow(Display *dpy, unsigned long win); int XGetWindowAttributes(Display *dpy, unsigned long win, XWindowAttributes *attr); Display *init_x11(void); XButtonEvent *make_button_event(void); XEvent *make_event(void); XWindowAttributes *make_attributes(void); void subwindow_set(XButtonEvent *start, unsigned long win); int event_type(XEvent *ev); int event_subwindow(XButtonEvent *ev); void button_event_copy(XButtonEvent *ev1, XButtonEvent *ev2); int event_x_root(XEvent *e); int event_y_root(XEvent *e); int attributes_x(XWindowAttributes *attr); int attributes_y(XWindowAttributes *attr); int attributes_width(XWindowAttributes *attr); int attributes_height(XWindowAttributes *attr); int event_button(XButtonEvent *ev); </enscript> Options customizing the build: <enscript highlight="scheme"> % cat all.options -program tinywm -C -I/usr/X11R6/include % cat tinywm.options -L -L/usr/X11R6/lib -L -lX11 % cat wrap.options -depends x11.o </enscript> This time we generate a makefile: <enscript highlight="scheme"> % csm -g % make /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' /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' /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' /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' </enscript> Now let's try it out, using Xephyr(1): <enscript highlight="scheme"> % Xephyr -ac -reset :1 & % DISPLAY=:1 tinywm & % DISPLAY=:1 xterm </enscript> ==== An R7RS program Here we build the example given in the R7RS report: <enscript highlight="scheme"> % cat main.scm (define (damped-oscillator R L C) (lambda (state) (let ((Vc (vector-ref state 0)) (Il (vector-ref state 1))) (vector (- 0 (+ (/ Vc (* R C)) (/ Il C))) (/ Vc L))))) (define (main count) (let ((the-states (integrate-system (damped-oscillator 10000 1000 .001) '#(1 0) .01))) (do ((i count (- i 1)) (states the-states (tail states))) ((zero? i)) (let ((h (head states))) (printf "~a ~a\n" (vector-ref h 0) (vector-ref h 1)))))) % cat example.scm (define-library example (import (scheme)) (import (integrate)) (import (chicken format)) (include "main.scm") (begin (main 100))) % cat integrate.scm (define-library integrate (import (scheme base)) (import (scheme lazy)) (export integrate-system head tail) (include "integrate-implementation.scm")) % cat integrate-implementation.scm ; The procedure integrate-system integrates the system ; y′k = fk(y1, y2, . . . , yn), k = 1, . . . , n ; of differential equations with the method of Runge-Kutta. ; The parameter system-derivative is a function that ; takes a system state (a vector of values for the state vari- ; ables y1, . . . , yn) and produces a system derivative (the val- ; ues y′1, . . . , y′n). The parameter initial-state provides ; an initial system state, and h is an initial guess for the ; length of the integration step. ; The value returned by integrate-system is an infi- ; nite stream of system states. (define (integrate-system system-derivative initial-state h) (let ((next (runge-kutta-4 system-derivative h))) (letrec ((states (cons initial-state (delay (map-streams next states))))) states))) (define (runge-kutta-4 f h) (let ((*h (scale-vector h)) (*2 (scale-vector 2)) (*1/2 (scale-vector (/ 1 2))) (*1/6 (scale-vector (/ 1 6)))) (lambda (y) ;; y is a system state (let* ((k0 (*h (f y))) (k1 (*h (f (add-vectors y (*1/2 k0))))) (k2 (*h (f (add-vectors y (*1/2 k1))))) (k3 (*h (f (add-vectors y k2))))) (add-vectors y (*1/6 (add-vectors k0 (*2 k1) (*2 k2) k3))))))) (define (elementwise f) (lambda vectors (generate-vector (vector-length (car vectors)) (lambda (i) (apply f (map (lambda (v) (vector-ref v i)) vectors)))))) (define (generate-vector size proc) (let ((ans (make-vector size))) (letrec ((loop (lambda (i) (cond ((= i size) ans) (else (vector-set! ans i (proc i)) (loop (+ i 1))))))) (loop 0)))) (define add-vectors (elementwise +)) (define (scale-vector s) (elementwise (lambda (x) (* x s)))) (define (map-streams f s) (cons (f (head s)) (delay (map-streams f (tail s))))) (define head car) (define (tail stream) (force (cdr stream))) % cat all.options -program example </enscript> We recklessly decide to link the final program statically. Note that we could have added the {{-static}} to {{all.options}} to build statically by default: <enscript highlight="scheme"> % csm -static '/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' '/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' % example <lots of numbers> </enscript> === Author Felix Winkelmann === Repository This egg is hosted on the CHICKEN Subversion repository: [[https://anonymous@code.call-cc.org/svn/chicken-eggs/release/6/csm]] If you want to check out the source code repository of this egg and you are not familiar with Subversion, see [[/egg-svn-checkout|this page]]. === Version history ; 0.6 : port to CHICKEN 6 ; 0.5 : added support for functors, contributed by "gahr". ; 0.4 : added {{-max-procs}}, the logic for parallelizing the build has been contributed by "gahr". ; 0.3 : added {{-compile-imports}} ; 0.2 : added {{-csm-extend}} option (thanks to Matt Welland for reporting this shortcoming) ; 0.1 : Initial release === License BSD
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you subtract 4 from 5?