Hi Paul, Paul Smith <psm...@gnu.org> skribis:
> I've been experimenting with using Guile as an extension language to GNU > make (optionally built-in of course). Sounds like great news! :-) > What I've done so far: > > * Modified GNU make's main to be invoked from scm_boot_guile(), if > Guile is enabled. Ideally, when Guile support is enabled, GNU make would be turned into a Guile extension (a shared library and its companion Scheme module that loads it with ‘load-extension’) that would expose make’s functionality. The main advantage is that make could then be used by “normal” Guile programs: (use-modules (make)) (define c->o (make-rule (make-target "foo.o") (make-prerequisites '("foo.c" "foo.h")) (list (make-expand "$(CC)") "-c -Wall" (make-expand "$^") " -o " (make-expand "$@")))) (eval-make-rule c->o) Or: (use-modules (make)) (define mf (parse-makefile "/foo/GNUmakefile")) (format #t "the targets are: ~a~%" (makefile-targets mf)) ;; Imagine code that extracts the complete DAG, computes the critical ;; path length, and schedules tasks... The ‘make’ executable would be a Guile script like: (use-modules (make)) (apply run-make (command-line)) This is more intrusive than embedding libguile in make, but it’s also more fruitful. On the general patterns of embedding vs. extending, see the excellent <http://www.twistedmatrix.com/users/glyph/rant/extendit.html>. > * Created a new GNU make function, $(guile ...), where the > argument is passed to Guile for expansion and the result is > turned into a string and used as the result; the code looks like > this: > func_guile (char *o, char **argv, const char *funcname UNUSED) > { > if (argv[0] && argv[0][0] != '\0') > { > char *str = scm_to_locale_string (scm_object_to_string > (scm_c_eval_string (argv[0]), > > SCM_UNDEFINED)); > char *s = str; > unsigned int l = strlen (s); > > if (s[0] == '"' && s[l-1] == '"') There are two problems I can think of here: - string unquoting is actually more complex than this (recall that ‘object->string’ merely calls ‘write’): --8<---------------cut here---------------start------------->8--- (call-with-output-string (lambda (p) (set-port-encoding! p "ISO-8859-1") (set-port-conversion-strategy! p 'substitute) (write "\"λ\" is a Greek letter" p))) => "\"\\\"\\u03bb\\\" is a Greek letter\"" --8<---------------cut here---------------end--------------->8--- - ‘scm_c_eval_string’ can return any Scheme objects, some of which have meaningless representations as strings: --8<---------------cut here---------------start------------->8--- (object->string (current-module)) => "#<directory (guile-user) 2405090>" --8<---------------cut here---------------end--------------->8--- The latter is probably the most serious question. I think you would really want to constrain expressions passed in $(guile ...) to return a string, and not any other type of objects. In that case, you could solve the first problem by using (display ...) instead of (write ...). > * Created two new functions and registered them with Guile: > (make-expand <string>) which takes a string argument and expands > it as a make expression, so it can be something like > (make-expand "$(VAR)") for example to get the value of the make > variable VAR. And (make-eval <string>) which takes a string > argument and evaluates it as a makefile snippet; this is > essentially the same as running (make-expand "$(eval <string>)") > just shorter to type. This lets you define make constructs like > rules and variables from within the Guile interpreter. The code > looks like this: Looks good to me. Thanks! Ludo’.