[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Using guile as an extension language for GNU make
From: |
Ludovic Courtès |
Subject: |
Re: Using guile as an extension language for GNU make |
Date: |
Sun, 18 Sep 2011 14:10:19 +0200 |
User-agent: |
Gnus/5.110018 (No Gnus v0.18) Emacs/24.0.50 (gnu/linux) |
Hi Paul,
Paul Smith <address@hidden> 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’.