From: Paul Smith <psmith@gnu.org>
To: guile-user@gnu.org
Subject: Guile support in GNU make
Date: Sat, 14 Jan 2012 14:55:05 -0500 [thread overview]
Message-ID: <1326570905.3482.136.camel@homebase> (raw)
[-- Attachment #1: Type: text/plain, Size: 1690 bytes --]
Hi all. I'm about to commit initial support for Guile to GNU make. I'm
interested in any comments you may have on the implementation (there is
still time to make changes before the next release of GNU make).
I'm committed to leaving Guile as an optional component for now. As a
result I'm not rewriting existing features of GNU make in terms of
Guile, or embedding it any more deeply than an extensions language. I
understand that some feel this is a sub-optimal solution and would like
to see Guile become a requirement, or even GNU make rewritten as a
loadable module for Guile, but I've decided that's not the way I'm going
to go at this time.
Attached please find three things:
1. The documentation I've added to the GNU make manual regarding
Guile
2. The default Guile scripting that is embedded in GNU make
3. The C code interface, for those who are curious
For #3 not ALL the code is provided: I left out the little bit that
wraps main() for example, and the part that wraps the $(guile ...)
function. But the critical parts are there. The rest should be
available in the GNU make repository sometime this weekend.
My Scheme-fu is fairly weak and my Guile-fu is even weaker. Any
suggestions about improvements to any of the above items from more
advanced users will be welcome--keeping in mind my comments of paragraph
#2 above, please, thanks!
--
-------------------------------------------------------------------------------
Paul D. Smith <psmith@gnu.org> Find some GNU make tips at:
http://www.gnu.org http://make.mad-scientist.net
"Please remain calm...I may be mad, but I am a professional." --Mad Scientist
[-- Attachment #2: guile-doc.txt --]
[-- Type: text/plain, Size: 4783 bytes --]
8.13 The `guile' Function
=========================
GNU make may be built with support for GNU Guile as an embedded
extension language. You can check the `.FEATURES' variable for the
word `guile' to determine if your version of GNU make provides this
capability.
GNU Guile implements the Scheme language. A review of GNU Guile and
the Scheme language and its features is beyond the scope of this
manual: see the documentation for GNU Guile and Scheme.
If GNU Guile is available as an extension language, there will be one
new `make' function available: `guile'. The `guile' function takes one
argument which is first expanded by `make' in the normal fashion, then
passed to the GNU Guile evaluator function. The result of the
evaluator is converted into a string and used as the expansion of the
`guile' function in the makefile.
8.13.1 Conversion of Guile Results
----------------------------------
When the `guile' function is evaluated, `make' will convert the result
of the function into a string and use it as the result of the
evaluation. The conversion of different Guile types into a string is
as follows:
`#f'
The empty string: in `make' the empty string is considered false.
`#t'
The string `t': in `make' any non-empty string is considered true.
`symbol'
`number'
A symbol or number is converted into the string representation of
that symbol or number.
`character'
A printable character is converted to the same character.
`string'
A string containing only printable characters is converted to the
same string.
`list'
A list is converted recursively according to the above rules. This
implies that any structured list will be flattened (that is, a
result of `'(a b (c d) e)' will be converted to the `make' string
`a b c d e').
`other'
Any other Guile type results in an error. In future versions of
`make', other Guile types may be converted.
As a consequence of these conversion rules you must be careful what
results your Guile forms evaluate to. If there is no natural result
for the script (that is the script exists solely for its side-effects,
not for its result), you should have it return `#f' to avoid syntax
errors in your makefile from result conversions.
8.13.2 Interfaces from Guile to `make'
--------------------------------------
In addition to the `guile' function available in makefiles, there are
three functions exported into GNU Guile by `make', for use in your
Guile scripts.
The `make' program creates a new module, `gnu make', and exports
these functions as public interfaces from that module:
`gmk-expand'
This GNU Guile function takes a single string as an argument. The
string is expanded by `make' using normal expansion rules. The
result of the expansion is converted into a string and provided to
GNU Guile as the result of the function.
`gmk-eval'
This function takes a single string as an argument. The string is
evaluated by `make' as if it were a makefile. This is the same
capability available via the `eval' function (*note Eval
Function::). The result of the `gmk-eval' function is always the
empty string.
`gmk-var'
This function takes a single string as an argument. The string is
interpreted as the name of a `make' variable, which is then
expanded. The result of the expansion is converted into a string
and provided as the result of the function in GNU Guile.
8.13.3 Example Using Guile in `make'
------------------------------------
Here is a very simple example using GNU Guile to manage writing to a
file. This set of Guile code simply opens a file, then allows writing
strings to it (one string per line), then closes it again. Note that
because we cannot store complex values such as Guile ports in `make'
variables, we'll keep the port as a global variable in the Guile
interpreter.
This code defines the Guile functions:
define GUILEIO
;; A simple Guile IO library for GNU make
(define MKPORT #f)
(define (mkopen name mode)
(set! MKPORT (open-file name mode))
#f)
(define (mkwrite s)
(display s MKPORT)
(newline MKPORT)
#f)
(define (mkclose)
(close-port MKPORT)
#f)
endef
# Internalize the Guile IO functions
$(guile $(GUILEIO))
Now you can use these Guile functions to create files. Suppose you
need to operate on a very large list, which cannot fit on the command
line, but the utility you're using accepts the list as input as well:
prog: $(PREREQS)
@$(guile (mkopen "tmp.out" "w")) \
$(foreach X,$^,$(guile (mkwrite "$(X)"))) \
$(guile (mkclose))
$(LINK) < tmp.out
[-- Attachment #3: gmk-default.scm --]
[-- Type: text/x-scheme, Size: 1900 bytes --]
;; Contents of the (gnu make) Guile module
;; Copyright (C) 2011 Free Software Foundation, Inc.
;; This file is part of GNU Make.
;;
;; GNU Make is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 3 of the License, or (at your option)
;; any later version.
;;
;; GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
;; details.
;;
;; You should have received a copy of the GNU General Public License along
;; with this program. If not, see <http://www.gnu.org/licenses/>.
(define (to-string-maybe x)
(cond
;; In GNU make, "false" is the empty string
((or (not x)
(unspecified? x)
(null? x)
(and (string? x) (string-null? x)))
#f)
;; We want something not false... not sure about this
((eq? x #t) "t")
;; Basics
((or (symbol? x) (number? x))
(object->string x))
((char? x)
(string x))
;; Printable string (no special characters)
((and (string? x)
(eq? (string-length (string-delete x char-set:printing)) 0))
x)
;; No idea: fail
(else (error "Unknown object:" x))))
(define (obj-to-str x)
(let ((acc '()))
(define (walk x)
(cond ((pair? x) (walk (car x)) (walk (cdr x)))
((to-string-maybe x) => (lambda (s) (set! acc (cons s acc))))))
(walk x)
(string-join (reverse! acc))))
;; eval (GNU make eval) the input string S
(define (gmk-eval s)
(gmk-expand (format #f "$(eval ~a)" (obj-to-str s))))
;; Return the value of the GNU make variable V
(define (gmk-var v)
(gmk-expand (format #f "$(~a)" (obj-to-str v))))
;; Export the public interfaces
(export gmk-expand gmk-eval gmk-var)
[-- Attachment #4: guile.c --]
[-- Type: text/x-csrc, Size: 3120 bytes --]
/* GNU Guile interface for GNU Make.
Copyright (C) 2011 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.
GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>. */
#include "make.h"
#ifndef HAVE_GUILE
/* Without Guile we just call the real main() directly. */
void
guile_boot (void (*func)(int, char **, char **),
int argc, char **argv, char **envp)
{
(*func) (argc, argv, envp);
}
#else
#include "debug.h"
#include "dep.h"
#include "variable.h"
#include <libguile.h>
static SCM make_mod = SCM_EOL;
static SCM obj_to_str = SCM_EOL;
/* Convert an SCM object into a string. */
static char *
cvt_scm_to_str (SCM obj)
{
return scm_to_locale_string (scm_call_1 (obj_to_str, obj));
}
/* Perform the GNU make expansion function. */
static SCM
guile_expand_wrapper (SCM obj)
{
char *str = cvt_scm_to_str (obj);
SCM ret;
char *res;
DB (DB_BASIC, (_("guile: Expanding '%s'\n"), str));
res = allocated_variable_expand (str);
ret = scm_from_locale_string (res);
free (str);
free (res);
return ret;
}
/* Invoked by scm_c_define_module(), in the context of the GNU make module. */
static void
guile_define_module (void *data UNUSED)
{
/* Ingest the predefined Guile module for GNU make. */
#include "gmk-default.h"
/* Register a subr for GNU make's eval capability. */
scm_c_define_gsubr ("gmk-expand", 1, 0, 0, guile_expand_wrapper);
/* Define the rest of the module. */
scm_c_eval_string (GUILE_module_defn);
}
/* Initialize the GNU make Guile module. */
static void
guile_init ()
{
/* Define the module. */
make_mod = scm_c_define_module ("gnu make", guile_define_module, NULL);
/* Get a reference to the object-to-string translator, for later. */
obj_to_str = scm_variable_ref (scm_c_module_lookup (make_mod, "obj-to-str"));
/* Import the GNU make module exports into the generic space. */
scm_c_eval_string ("(use-modules (gnu make))");
}
static void (*real_main)(int, char **, char **);
/* Initialize Guile, then run the real GNU make main. */
static void
guile_boot_internal (void *data, int argc, char **argv)
{
guile_init ();
(*real_main) (argc, argv, data);
}
/* ----- Public interface ----- */
void
guile_boot (void (*func)(int, char **, char **),
int argc, char **argv, char **envp)
{
real_main = func;
scm_boot_guile (argc, argv, guile_boot_internal, envp);
/* Never returns. */
}
/* This is the make interface for passing programs to Guile. */
char *
guile_eval_string (char *str)
{
return cvt_scm_to_str (scm_c_eval_string (str));
}
#endif
next reply other threads:[~2012-01-14 19:55 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-01-14 19:55 Paul Smith [this message]
2012-01-15 8:51 ` Guile support in GNU make Thien-Thi Nguyen
2012-01-15 12:33 ` Thien-Thi Nguyen
2012-01-15 16:12 ` Paul Smith
2012-01-15 20:11 ` Thien-Thi Nguyen
2012-01-15 20:49 ` Paul Smith
2012-01-15 22:02 ` Ludovic Courtès
2012-01-16 14:07 ` Paul Smith
2012-01-17 22:42 ` Ludovic Courtès
2012-01-17 23:26 ` Paul Smith
2012-01-19 20:42 ` Ludovic Courtès
2012-01-19 22:14 ` Ludovic Courtès
[not found] <1316557011.28907.237.camel@homebase>
[not found] ` <1326572016.3482.144.camel@homebase>
[not found] ` <20120122182923.GB3460@mini.zxlink>
2012-01-22 21:56 ` Paul Smith
2012-01-23 6:01 ` Thien-Thi Nguyen
2012-01-23 6:08 ` Thien-Thi Nguyen
2012-01-29 20:05 ` Paul Smith
2012-01-31 19:17 ` Kirill Smelkov
2012-01-31 21:32 ` Paul Smith
2012-01-25 23:45 ` Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1326570905.3482.136.camel@homebase \
--to=psmith@gnu.org \
--cc=guile-user@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).