From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Paul Smith Newsgroups: gmane.lisp.guile.user Subject: Guile support in GNU make Date: Sat, 14 Jan 2012 14:55:05 -0500 Organization: GNU's Not Unix! Message-ID: <1326570905.3482.136.camel@homebase> Reply-To: psmith@gnu.org NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-rRsT4ls3Z9xivPU8+ewy" X-Trace: dough.gmane.org 1326576776 1112 80.91.229.12 (14 Jan 2012 21:32:56 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Sat, 14 Jan 2012 21:32:56 +0000 (UTC) To: guile-user@gnu.org Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Sat Jan 14 22:32:50 2012 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([140.186.70.17]) by lo.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1RmBDU-0002QW-JE for guile-user@m.gmane.org; Sat, 14 Jan 2012 22:32:48 +0100 Original-Received: from localhost ([::1]:39369 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RmBDU-00008A-0S for guile-user@m.gmane.org; Sat, 14 Jan 2012 16:32:48 -0500 Original-Received: from eggs.gnu.org ([140.186.70.92]:37085) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rm9h3-0005mQ-IP for guile-user@gnu.org; Sat, 14 Jan 2012 14:55:15 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Rm9h1-0002ZY-0B for guile-user@gnu.org; Sat, 14 Jan 2012 14:55:13 -0500 Original-Received: from oproxy4-pub.bluehost.com ([69.89.21.11]:50902) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1Rm9h0-0002ZN-GZ for guile-user@gnu.org; Sat, 14 Jan 2012 14:55:10 -0500 Original-Received: (qmail 23622 invoked by uid 0); 14 Jan 2012 19:55:09 -0000 Original-Received: from unknown (HELO box531.bluehost.com) (74.220.219.131) by cpoproxy1.bluehost.com with SMTP; 14 Jan 2012 19:55:09 -0000 Original-Received: from 146-115-71-23.c3-0.lex-ubr1.sbo-lex.ma.cable.rcn.com ([146.115.71.23] helo=[172.31.1.105]) by box531.bluehost.com with esmtpsa (SSLv3:AES256-SHA:256) (Exim 4.76) (envelope-from ) id 1Rm9gy-0003qg-E2 for guile-user@gnu.org; Sat, 14 Jan 2012 12:55:08 -0700 X-Mailer: Evolution 2.32.2 X-Identified-User: {678:box531.bluehost.com:madscie1:mad-scientist.us} {sentby:smtp auth 146.115.71.23 authed with paul+mad-scientist.us} X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 69.89.21.11 X-Mailman-Approved-At: Sat, 14 Jan 2012 16:32:44 -0500 X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.user:9138 Archived-At: --=-rRsT4ls3Z9xivPU8+ewy Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 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 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 --=-rRsT4ls3Z9xivPU8+ewy Content-Disposition: attachment; filename="guile-doc.txt" Content-Type: text/plain; name="guile-doc.txt"; charset="us-ascii" Content-Transfer-Encoding: 7bit 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 --=-rRsT4ls3Z9xivPU8+ewy Content-Disposition: attachment; filename="gmk-default.scm" Content-Type: text/x-scheme; name="gmk-default.scm"; charset="us-ascii" Content-Transfer-Encoding: 7bit ;; 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 . (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) --=-rRsT4ls3Z9xivPU8+ewy Content-Disposition: attachment; filename="guile.c" Content-Type: text/x-csrc; name="guile.c"; charset="us-ascii" Content-Transfer-Encoding: 7bit /* 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 . */ #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 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 --=-rRsT4ls3Z9xivPU8+ewy--