diff options
-rw-r--r-- | Makefile.am | 24 | ||||
-rw-r--r-- | README | 321 | ||||
-rw-r--r-- | clexer.l | 330 | ||||
-rwxr-xr-x | extract-exports | 81 | ||||
-rw-r--r-- | gint.m4 | 63 | ||||
-rw-r--r-- | gint.mk | 66 | ||||
-rw-r--r-- | guile.m4 | 120 |
7 files changed, 1005 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3b720db --- /dev/null +++ b/Makefile.am @@ -0,0 +1,24 @@ +# This file is part of Gint +# Copyright (C) 2010 Sergey Poznyakoff +# +# This program 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, or (at your option) +# any later version. +# +# This program 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/>. + +noinst_PROGRAMS = clexer +clexer_SOURCES = clexer.l +INCLUDES = @GINT_INCLUDES@ +LDADD = @GINT_LDADD@ +EXTRA_DIST = gint.m4 gint.mk guile.m4 +if GINT_COND_INC +EXTRA_DIST += extract-exports +endif @@ -0,0 +1,321 @@ +GINT +==== +Sergey Poznyakoff <gray@gnu.org> +1, April 5, 2010 + +NAME +---- +GINT -- Guile Integration Framework + +DESCRIPTION +----------- +Integrating *Guile* into a project requires performing a set of routine steps, +such, e.g., as creating additional Makefile rules, which, however trivial, +require additional efforts from authors and impose on them an extra +maintenance burden. Authors maintaining several projects, each of which uses +Guile as an extension language, soon find out that these steps differ only +insigificantly between the projects. It is therefore natural to move their +_common denominator_ into a separate module and share this module between +the projects. + +*GINT* is an attempt to create such a module. It reduces the task of +integrating Guile to importing a submodule and editing a couple of files. + +*GINT* is designed as a Git submodule easily embeddable into any project. +The only requirement to this host project is that it must use GNU Automake +and Autoconf. It is also recommended, but not required, that the host project +use Git for its repository. + +INSTALLATION +------------ + +To illustrate how to use *GINT*, let's suppose your project has the +following structure: + +.Sample project listing +--------------------------------------------------------------------- +-rw-r--r-- 1 gray users 2993 2010-04-05 00:15 Makefile.am <1> +-rw-r--r-- 1 gray users 3026 2010-04-05 19:21 configure.ac <2> +drwxr-xr-x 2 gray users 240 2010-04-05 02:27 m4/ <3> +drwxr-xr-x 5 gray users 1992 2010-04-05 19:20 src/ <4> +-rw-r--r-- 1 gray users 1286 2010-04-05 13:18 src/Makefile.am <5> +-rw-r--r-- 1 gray users 1034 2010-04-05 13:18 src/iface.c +--------------------------------------------------------------------- + +<1> Top-level +Makefile+ source. +<2> Configuration script source. +<3> Directory with macro definitions for *aclocal*. +<4> Source directory. It contains actual +C+ and +scm+ sources, + which define new *Guile* interfaces. +<5> Source +Makefile.am+. + +The purpose of *GINT* is to provide the autotools magic necessary to +check, at configure time, whether Guile is installed, determine its version +number and location of its components on the local file system, then to +compile and _snarf_ the +C+ sources, and finally, to produce the +documentation files (+guile-procedures.texi+ and +guile-procedures.txt+). + +Import GINT as a submodule +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This needs to be done only once: + +---------------------------------------------------- +git submodule add git://git.gnu.org.ua/gint.git gint +git init +---------------------------------------------------- + +[[gint-dir]] +The *submodule add* command takes two arguments: the submodule repository, +which should be exactly as shown above, and the is the pathname of the +cloned submodule in your project. This latter is entirely at your option. +Throughout this document we will suppose that the module pathname is ++gint+. You will need to adjust the examples if you chose another +name for it. + +Edit the toplevel +Makefile.am+ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Add +-I gint+ to the +ACLOCAL_AMFLAGS+ variable, and +gint+ to +the +SUBDIRS+ variable. For example: + +---------------------------------------- +ACLOCAL_AMFLAGS = -I m4 -I gint +SUBDIRS = gint src +---------------------------------------- + +Notice that in +SUBDIRS+, the +gint+ entry must precede the names of +those directories that depend on it. + +Edit the +configure.ac+ file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Obviously, you need to add the submodule's Makefile to the list of ++AC_CONFIG_FILES+, e.g.: + +---------------------------------------- +AC_CONFIG_FILES(Makefile + gint/Makefile + src/Makefile) +---------------------------------------- + +Next, add a call to <<GINT_INIT,+GINT_INIT+>> at an appropriate point. +This macro serves two purposes. First, it informs the +gint+ submodule +about its location relative to the top project directory and configures +its features. Second, it checks for the presence of Guile's binaries +and libraries, verifies if its version is modern enough, and runs a +series of user-supplied commands, depending on the result of this check. + +In a simplest case, the following line will be enough: + +--------- +GINT_INIT +--------- + +For a detailed discussion, see <<GINT_INIT, macro description>>. + +Edit source `Makefile.am` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The source `Makefile.am` (or `Makefile.am`'s, if there are several of these, in +separate directories) must include the file `gint/gint.mk`, which provides ++Makefile+ rules necessary to properly build various files (e.g. +.x+ and ++.doc+ files from corresponding +.c+ sources, etc) and to compute dependencies +between them. Usually, it is OK to use a relative location. For example, in our +sample project the `gint` and `src` directories have same parent directory, +therefore we could add to `src/Makefile.am` the following line: + +----------------------- +include ../gint/gint.mk +----------------------- + +The rules in `gint.mk` make certain assumptions about some `Makefile` +variables. Namely, the following variables must be defined before +including the file: +INCLUDES+, +EXTRA_DIST+, +CLEANFILES+, ++DISTCLEANFILES+, +SUFFIXES+. See the +http://sources.redhat.com/automake/automake.html[`Automake` documentation], +for more info on these. If no special value is needed, yo may define each +of them to an empty string (see example below). + +Firthermore, the +MAKEINFO+ variable must contain the pathname (not +necessarily an absolute one), of the +makeinfo+ binary. It is initialized +by +Automake+ if your project has a `Makefile.am` with the +info_TEXINFOS+ +variable set. If not, you will have to initialize it manually. + +Finally, two +GINT+-specific variables must be defined: + +DOT_X_FILES:: + A list of +.x+ files to be generated. +DOT_DOC_FILES:: + A list of +.doc+ files to be generated. + +These two can be easily produced from the list of source +c+ files +using http://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html[+make+ substitution references]: + +----------------------------------------------- +lib_LTLIBRARIES = libproj.la +libproj_la_SOURCES = iface.c var.c + +INCLUDES =-I$(top_builddir) -I$(srcdir) +EXTRA_DIST= +DOT_X_FILES=$(libproj_la_SOURCES:.c=.x) +DOT_DOC_FILES=$(libproj_la_SOURCES:.c=.doc) + +SUFFIXES= +CLEANFILES= +DISTCLEANFILES= +include ../gint/gint.mk +------------------------------------------------ + +[[GINT_INIT]] +The +GINT_INIT+ macro +--------------------- + +------------------------------------------------ +GINT_INIT(DIR, OPTIONS, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND) +------------------------------------------------ + +This macro configures +GINT+ submodule, located in subdirectory +`DIR` according to the settings given in `OPTIONS`. It then +verifies if Guile is installed on the system and if so executes +commands supplied by `ACTION-IF-FOUND`. Additionaly, it sets +a number of <<gint-subst-vars, substitution variables>> describing +various details of Guile installation. + +Otherwise, if Guile is not installed or is found to be unusable, +`ACTION-IF-NOT-FOUND` is executed. + +All parameters are optional: + +DIR:: ++GINT+ <<gint-dir,submodule directory>> (defaults to `gint`). + +OPTIONS:: +A whitespace-separated list of options. + +ACTION-IF-FOUND:: +Commands to execute if Guile is present. + +IF-NOT-FOUND:: +Commands to execute if Guile is not found or its version is too old. +If not given, the default action is to print a diagnostic message on +the standard error and abort the execution. + +The +OPTIONS+ parameter allows to execute finer control over the +functionality provided by +GINT_INIT+. Its value is a +whitespace-separated list of words. Each word must be either the +name of an option, or the minimum (i.e. the older) allowed version +of Guile. For example, to check for Guile 1.8 or later one could +write: + +------------------------ +GINT_INIT([gint], [1.8]) +------------------------ + +Following is a list of valid options: + +inc:: +Enable generation of `.inc` files. Each such file contains a set of +`export` statements, one for each `SCM_DEFINE` in the corresponding +`.c` source file. + +std-site-dir:: +Set <<sitedir,sitedir>> to the standard Guile site directory, as +returned by the +%site-dir+ primitive. ++ +This is one of locations where Guile looks for its modules. +However, it breaks standard `distcheck` rules and automated builds, +because this directory is normally outside of the installation prefix. +Therefore by default, +GINT+ does not use it. <<guile-site-dir, See +below>>, for a description of the method used to determine it. + +Here is a more complex example: + + +.+GINT_INIT+ macro +------------------------------------------------------- +GINT_INIT([modules/gint], [1.8 std-site-dir], + [use_guile=yes], + [use_guile=no]) +------------------------------------------------------- + +This fragment initializes the +GINT+ module located in `modules/gint` and +checks for Guile version 1.8 or newer. The resulting script sets +<<sitedir,sitedir>> to the standard Guile site directory. The +shell variable `use_guile` is set to `yes` or `no`, depending on +whether Guile was found or not. + +[[gint-subst-vars]] +Substitution Variables +---------------------- +Upon successful return, +GINT_INIT+ sets the following Automake +substitution variables: + +GUILE_VERSION:: +The version of Guile, as a string, e.g. `1.9.9`. Additionally, +a +C+ preprocessor macro with the same name is defined. + +GUILE_VERSION_NUMBER:: +The version of Guile packed into a decimal number using the following formula: ++ +--------------------------------------- +MAJOR * 1000 + MINOR * 100 + PATCHLEVEL +--------------------------------------- ++ +where `MAJOR`, `MINOR` and `PATCHLEVEL` are the three parts of a version +number, separated by dots. For example, the version string `1.9.9` will produce +the value `1909`, and the version `2.0` will yield `2000`. ++ +A +C+ preprocessor macro with the same name is also defined. + +GUILE_INCLUDES:: +The +C+ compiler flags needed to compile with Guile, as +returned by `guile-config compile`. + +GUILE_LIBS:: +The linker flags needed to link with +libguile+, as +returned by `guile-config link`. + +GUILE_SNARF:: +The full pathname of the `guile-snarf` binary. + +GUILE_TOOLS:: +The full pathname of the `guile-tools` binary. + +[[sitedir]] +GUILE_SITE:: +The full pathname of the Guile site-wide module directory. + +[[guile-site-dir]] +THE `SITE DIRECTORY' PROBLEM +---------------------------- +Projects installing some Scheme sources would normally want to install +them under Guile's 'site directory' (+%site-dir+ Guile primitive). This way, +Guile will be able to find them without any additional configuration. However, +such usage breaks the standard GNU practice of not installing files outside +of the project's install prefix, unless the user explicitly requires so. +As a consequence, it also breaks the standard `make distcheck` rule. + +To avoid this, the configuration code generated by +GINT+ macros determines +the site directory using the following algorithm: + +1. Determine actual value of the default Guile site directory, by inspecting +the value returned by the +%site-dir+ primitive. +2. If that value lies under the current installation prefix, it is accepted +as the installation directory. +3. Otherwise, if the +--with-guile-site-dir+ option is used: + a. If it is used without arguments, +%site-dir+ is enforced as the + installation directory. + b. Otherwise, the value of this option is taken as the new installation + directory. Notices, that this value must be an absolute directory name. +4. Otherwise, a warning is issued and `$(datadir)/guile/site` is used as +the installation directory. + +The use if this algorithm is suppressed and Guile site directory is +used instead, if the <<GINT_INIT,+GINT_INIT+>> macros was invoked +with the +std-site-dir+ option. + +COPYRIGHT +--------- +[verse] +Copyright (C) 2010 http://gray.gnu.org.ua[Sergey Poznyakoff] +License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html[<http://gnu.org/licenses/gpl.html>]. +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. diff --git a/clexer.l b/clexer.l new file mode 100644 index 0000000..d7b3b5c --- /dev/null +++ b/clexer.l @@ -0,0 +1,330 @@ +/* clexer -- show lexical structure of a C file. + Copyright (C) 2010 Sergey Poznyakoff + + This program 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, or (at your option) + any later version. + + This program 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/>. */ + +%option noyywrap + +%top { +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +} + +%{ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +enum state + { + s_init, + s_multiline, + s_cookie_multiline, + s_cookie + }; + +enum token + { + t_token = 256, + t_open, + t_close, + t_cookie + }; + +const char *progname; +char *infilename; +char *outfilename; +int guile_snarfer_mode; +enum state state; +FILE *outfile; +unsigned line_no = 1; + +static void +advance_line (const char *text) +{ + for (; *text; text++) + if (*text == '\n') + line_no++; +} + +#define outstr(text) \ + do \ + if (state != s_init) \ + fprintf (outfile, "%s\n", text); \ + while (0) + +#define outtok(tname,tval) \ + do \ + if (state != s_init) \ + fprintf (outfile, "(%s . \"%s\")\n", \ + tname, tval); \ + while (0) + +#define RETTOK(t) { outstr (#t); return t_token; } +#define RETTXT(t,s) { outtok (#t, s); return t_token; } + +%} + +N [0-9] +X [0-9a-fA-F] +O [0-7] +ID [a-zA-Z_][a-zA-Z_0-9]* +IQ (l|L|ll|LL|lL|Ll|u|U) +FQ [fFlL] +E [Ee][+-]?{N}+ +WS [ \t\v\f] + +%% +\/\*(\n|[^*]|\*[^/])*\*\/ { advance_line (yytext); + outtok ("comment", yytext); } +#.*\n { outstr ("hash"); line_no++; } +\n { outstr ("eol"); line_no++; } +{WS}+ ; +\\ ; +{ID} RETTXT (id, yytext); +0[xX]{X}+{IQ}? RETTXT (int_hex, yytext + 1); +0{O}+{IQ}? RETTXT (int_oct, yytext + 2); +{N}+{IQ}? RETTXT (int_dec, yytext); +L?\'[^\\']\' | +L?\'\\[^0xX]\' | +L?\'\\{O}{1,3}\' | +L?\'\\[xX]{X}{1,2} RETTXT (char, yytext); +{N}+E{FQ}? | +{N}*"."{N}+({E})?{FQ}? | +{N}+"."{N}*({E})?{FQ}? RETTXT (flo_dec, yytext); +L?\"([^\\\"]|\\.|\\\n)*\" { outstr (yytext); advance_line (yytext); } + +"^"{WS}*"^" { fputs ("snarf_cookie\n", outfile); + return t_cookie; } +"{" { outstr ("brace_open"); return t_open; } +"}" { outstr ("brace_close"); return t_close; } +"," RETTOK (comma); +":" RETTOK (colon); +"=" RETTOK (assign); +"(" RETTOK (paren_open); +")" RETTOK (paren_close); +"[" RETTOK (bracket_open); +"]" RETTOK (bracket_close); +"&" RETTOK (amp); +"^" RETTOK (caret); +"|" RETTOK (pipe); +"?" RETTOK (question); +"!" RETTOK (bang); +"~" RETTOK (tilde); +"-" RETTOK (minus); +"+" RETTOK (plus); +"*" RETTOK (star); +"/" RETTOK (slash); +"%" RETTOK (percent); +"..." RETTOK (ellipsis); +"->" RETTOK (ptr); +"." RETTOK (dot); +"*=" RETTOK (mul_assign); +"/=" RETTOK (div_assign); +"%=" RETTOK (mod_assign); +"+=" RETTOK (add_assign); +"-=" RETTOK (sub_assign); +"<<=" RETTOK (shift_left_assign); +">>=" RETTOK (shift_right_assign); +"&=" RETTOK (logand_assign); +"|=" RETTOK (logior_assign); +"^=" RETTOK (logxor_assign); +"||" RETTOK (or); +"&&" RETTOK (and); +"==" RETTOK (eq); +"!=" RETTOK (neq); +">=" RETTOK (ge); +">>" RETTOK (right_shift); +">" RETTOK (gt); +"<=" RETTOK (le); +"<<" RETTOK (left_shift); +"<" RETTOK (lt); +"++" RETTOK (inc); +"--" RETTOK (dec); +";" RETTOK (semicolon); +. { fprintf (stderr, + (isascii (yytext[0]) && + isprint (yytext[0])) ? + "%s:%u: stray character %c\n" : + "%s:%u: stray character \\%03o", + infilename, + line_no, (unsigned char) yytext[0]); + return t_token; } + +%% + +void +usage() +{ + printf ("\ +usage: clexer [OPTIONS] [FILE]\n\ +\n\ +Show lexical structure of a C source. When given the --snarfer option,\n\ +display only lexical tokens produced by Guile snarfer. The output may be\n\ +piped to the `guile-tools snarf-check-and-output-texi' command.\n\ +\n\ +OPTIONS are:\n\ + -s, --snarfer filter out Guile docstrings\n\ + -o, --output=FILE write output to FILE\n\ + -h, --help show this help summary\n\ +\n"); + printf ("Report bugs to <gray+clexer@gnu.org.ua>\n"); +} + +int +main (int argc, char **argv) +{ + int i; + enum token tok, last_tok; + + progname = argv[0]; + + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-s") || + !strcmp (argv[i], "--snarfer") || + !strcmp (argv[i], "--guile-snarfer")) + { + guile_snarfer_mode = 1; + } + else if (!strcmp (argv[i], "-h") || + !strcmp (argv[i], "--help")) + { + usage (); + exit (0); + } +#if 0 + else if (!strcmp (argv[i], "-v") || + !strcmp (argv[i], "--version")) + { + version (); + exit (0); + } +#endif + else if (!strncmp (argv[i], "-o", 2)) + { + if (argv[i][2]) + outfilename = argv[i] + 2; + else if (++i < argc) + outfilename = argv[i]; + else + { + fprintf (stderr, "%s: option '-o' requires an argument\n", + progname); + exit (1); + } + } + else if (!strncmp (argv[i], "--output=", 9)) + outfilename = argv[i] + 9; + else if (!strcmp (argv[i], "--output")) + { + if (++i < argc) + outfilename = argv[++i]; + else + { + fprintf (stderr, "%s: option '--output' requires an argument\n", + progname); + exit (1); + } + } + else if (!strcmp (argv[i], "--")) + { + if (++i < argc) + infilename = argv[i]; + break; + } + else if (argv[i][0] == '-') + { + fprintf (stderr, "%s: unknown option: %s\n", + progname, argv[i]); + exit (1); + } + else + { + infilename = argv[i]; + } + } + + if (infilename && strcmp (infilename, "-")) + { + FILE *fp = fopen (infilename, "r"); + if (!fp) + { + fprintf (stderr, "%s: cannot open file `%s': %s\n", + progname, infilename, strerror (errno)); + exit (1); + } + yyrestart (fp); + } + else + infilename = "stdin"; + + if (outfilename) + { + outfile = fopen (outfilename, "w"); + if (!outfile) + { + fprintf (stderr, "%s: cannot open file `%s': %s\n", + progname, outfilename, strerror (errno)); + exit (1); + } + } + else + outfile = stdout; + + state = guile_snarfer_mode ? s_init : s_cookie; + last_tok = t_token; + while ((tok = yylex ()) > 0) + { + if (!guile_snarfer_mode) + continue; + switch (tok) + { + case t_token: + break; + + case t_open: + if (last_tok == t_cookie && state == s_cookie) + state = s_multiline; + break; + + case t_close: + if (last_tok == t_cookie && state == s_cookie_multiline) + state = s_init; + break; + + case t_cookie: + switch (state) + { + case s_init: + state = s_cookie; + break; + + case s_multiline: + case s_cookie_multiline: + state = s_cookie_multiline; + break; + + case s_cookie: + state = s_init; + } + } + last_tok = tok; + } + + fclose (outfile); + exit (0); +} diff --git a/extract-exports b/extract-exports new file mode 100755 index 0000000..b0aeda6 --- /dev/null +++ b/extract-exports @@ -0,0 +1,81 @@ +#!/bin/sh +# aside from this initial boilerplate, this is actually -*- scheme -*- code +main="(module-ref (resolve-module '(gint extract-exports)) 'main) +" +exec ${GUILE-guile} -l $0 -c "(apply $main (command-line))" "$@" +!# + +;;;; This file is part of Gint +;;;; Copyright (C) 2010 Sergey Poznyakoff +;;;; +;;;; This program 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, or (at your option) +;;;; any later version. +;;;; +;;;; This program 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-module (gint extract-exports) + :use-module (ice-9 getopt-long)) + +(define (snarf-exports) + (let loop ((elt (read))) + (cond + ((not (eof-object? elt)) + (if (and (pair? elt) + (eq? (car elt) 'id) + (string? (cdr elt)) + (string=? (cdr elt) "fname")) + (let ((name (read))) + (if (string? name) + (format #t "(export ~A)~%" name)))) + (loop (read)))))) + +(define output-file-name #f) +(define input-file-list '()) + +(define (usage) + (display "usage: extract-exports [OPTIONS] [FILE [FILE...]]\n") + (display "\n") + (display "OPTIONS are:\n") + (display " -o, --output FILE set output file name\n") + (display " -h, --help display this help summary\n")) + +(define (do-file-list) + (if (null? input-file-list) + (snarf-exports) + (for-each + (lambda (file) + (with-input-from-file + file + snarf-exports)) + input-file-list))) + +(define (main . cmdline) + (let ((grammar `((output (single-char #\o) + (value #t)) + (help (single-char #\h))))) + (for-each + (lambda (x) + (case (car x) + ((output) + (set! output-file-name (cdr x))) + ((help) + (usage) + (exit 0)) + ('() + (set! input-file-list (cdr x))))) + (getopt-long cmdline grammar))) + + (if output-file-name + (with-output-to-file + output-file-name + do-file-list) + (do-file-list))) + @@ -0,0 +1,63 @@ +# gint.m4 serial 1 +dnl This file is part of Gint +dnl Copyright (C) 2010 Sergey Poznyakoff +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program. If not, see <http://www.gnu.org/licenses/>. + +dnl The option stuff below is based on the similar code from Automake + +# _GINT_MANGLE_OPTION(NAME) +# ------------------------- +# Convert NAME to a valid m4 identifier, by replacing invalid characters +# with underscores, and prepend the _GINT_OPTION_ suffix to it. +AC_DEFUN([_GINT_MANGLE_OPTION], +[[_GINT_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _GINT_SET_OPTION(NAME) +# ---------------------- +# Set option NAME. If NAME begins with a digit, treat it as a requested +# Guile version number, and define _GINT_GUILE_VERSION to that number. +# Otherwise, define the option using _GINT_MANGLE_OPTION. +AC_DEFUN([_GINT_SET_OPTION], +[m4_if(m4_bpatsubst($1,^[[0-9]].*,[]),,[m4_define([_GINT_GUILE_VERSION],[$1])],[m4_define(_GINT_MANGLE_OPTION([$1]), 1)])]) + +# _GINT_IF_OPTION_SET(NAME,IF-SET,IF-NOT-SET) +# ------------------------------------------- +# Check if option NAME is set. +AC_DEFUN([_GINT_IF_OPTION_SET], +[m4_ifset(_GINT_MANGLE_OPTION([$1]),[$2],[$3])]) + +# _GINT_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Gint options. +AC_DEFUN([_GINT_SET_OPTIONS], +[m4_foreach_w([_GINT_Option], [$1], [_GINT_SET_OPTION(_GINT_Option)])]) + +dnl GINT_INIT([DIR], [OPTIONS], [IF-FOUND], [IF-NOT-FOUND]) +dnl ------------------------------------------------------- +dnl DIR Gint submodule directory (defaults to 'gint') +dnl OPTIONS A whitespace-separated list of options. Currently recognized +dnl options are: 'inc', 'std-site-dir' and version number. +dnl IF-FOUND What to do if Guile is present. +dnl IF-NOT-FOUND What to do otherwise. +dnl +AC_DEFUN([GINT_INIT],[ + AM_PROG_LEX + _GINT_SET_OPTIONS([$2]) + AC_SUBST([GINT_MODULE_DIR],[m4_if([$1],,[gint],[$1])]) + AM_CONDITIONAL([GINT_COND_INC],[_GINT_IF_OPTION_SET([inc],[true],[false])]) + + GINT_CHECK_GUILE(m4_ifdef([_GINT_GUILE_VERSION],_GINT_GUILE_VERSION),[$3],[$4]) +]) + @@ -0,0 +1,66 @@ +# This file is part of Gint +# Copyright (C) 2010 Sergey Poznyakoff +# +# This program 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, or (at your option) +# any later version. +# +# This program 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/>. + +INCLUDES += @GUILE_INCLUDES@ + +EXTRA_DIST += guile-procedures.texi guile-procedures.txt + +site_DATA += guile-procedures.txt + +BUILT_SOURCES=$(DOT_X_FILES) $(DOT_DOC_FILES) guile-procedures.texi + +DISTCLEANFILES +=\ + $(DOT_X_FILES)\ + $(DOT_DOC_FILES)\ + guile-procedures.texi\ + guile-procedures.txt + +ETAGS_ARGS = --regex='/SCM_\(GLOBAL_\)?\(G?PROC\|G?PROC1\|SYMBOL\|VCELL\|CONST_LONG\).*\"\([^\"]\)*\"/\3/' \ + --regex='/[ \t]*SCM_[G]?DEFINE1?[ \t]*(\([^,]*\),[^,]*/\1/' + +GUILE_DOC_SNARF=$(top_builddir)/$(GINT_MODULE_DIR)/clexer --snarfer + +SUFFIXES += .x .doc + +.c.x: + $(AM_V_GEN)AWK=$(AWK) \ + $(GUILE_SNARF) -o $@ \ + $< $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) + +.c.doc: + $(AM_V_GEN)$(CC) -DSCM_MAGIC_SNARF_DOCS \ + $(DEFS) $(INCLUDES) $(CPPFLAGS) -E $< | \ + $(GUILE_DOC_SNARF) -o $@ || { rm $@; false; } + +if GINT_COND_INC +SUFFIXES += .inc +CLEANFILES += *.inc + +.doc.inc: + $(AM_V_GEN)$(top_srcdir)/$(GINT_MODULE_DIR)/extract-exports -o $@ $< +endif + +guile-procedures.texi: $(DOT_DOC_FILES) + $(AM_V_GEN)cat $(DOT_DOC_FILES) | \ + $(GUILE_TOOLS) snarf-check-and-output-texi > $@ + +guile-procedures.txt: guile-procedures.texi + $(AM_V_GEN) rm -f $@; \ + $(MAKEINFO) --force -o $@ guile-procedures.texi || test -f $@ + +## Add -MG to make the .x magic work with auto-dep code. +MKDEP = $(CC) -M -MG $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) + diff --git a/guile.m4 b/guile.m4 new file mode 100644 index 0000000..1d340b4 --- /dev/null +++ b/guile.m4 @@ -0,0 +1,120 @@ +# guile.m4 serial 1 +dnl This file is part of Gint +dnl Copyright (C) 2010 Sergey Poznyakoff +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program. If not, see <http://www.gnu.org/licenses/>. + +m4_define([_gint_eval_version], +[m4_if([$2],,[m4_errprint([Version number without dots: $1 +])],[m4_eval([$1] * 1000 + [$2] * 100[]m4_if([$3],,,[ + $3]))])]) + +m4_define([_gint_mangle_version],[_gint_eval_version(m4_bpatsubst($1,\.,[,]))]) + +m4_define([_gint_site_dir],[`guile -c '(write (%site-dir)) (newline)'`]) + +dnl GINT_CHECK_GUILE(minversion, [act-if-found], [ac-if-not-found]) +dnl $1 $2 $3 +AC_DEFUN([GINT_CHECK_GUILE], +[ + AS_VAR_SET([gint_cv_guile], [no]) + AC_PATH_PROG(GUILE_CONFIG, guile-config, no, $PATH) + if test "$GUILE_CONFIG" = no; then + m4_if([$3],,[AC_MSG_ERROR(cannot find Guile)], [$3]) + else + AC_SUBST(GUILE_INCLUDES) + AC_SUBST(GUILE_LIBS) + AC_SUBST(GUILE_VERSION) + AC_SUBST(GUILE_VERSION_NUMBER) + AC_PATH_PROG(GUILE_SNARF, guile-snarf) + AC_PATH_PROG(GUILE_TOOLS, guile-tools) + + GUILE_INCLUDES=`$GUILE_CONFIG compile` + GUILE_LIBS=`$GUILE_CONFIG link` + GUILE_VERSION=`($GUILE_CONFIG --version 2>&1; echo '')|sed 's/guile-config [[^0-9]]* \([[0-9]][[0-9.]]*\)$/\1/'` + VEX=`echo $GUILE_VERSION | sed 's/\./ \\\\* 1000 + /;s/\./ \\\\* 100 + /'` + GUILE_VERSION_NUMBER=`eval expr "$VEX"` + + m4_if([$1],,,[ + if test $GUILE_VERSION_NUMBER -lt _gint_mangle_version($1); then + m4_if($3,, + [AC_MSG_ERROR([Guile version too old; required is at least ]$1)], + [$3]) + fi]) + + save_LIBS=$LIBS + save_CFLAGS=$CFLAGS + LIBS="$LIBS $GUILE_LIBS" + CFLAGS="$CFLAGS $GUILE_INCLUDES" + AC_TRY_LINK([#include <libguile.h>], + m4_if([$1], , scm_shell(0, NULL);, [$1]), + [AS_VAR_SET([gint_cv_guile], $GUILE_VERSION)]) + LIBS=$save_LIBS + CFLAGS=$save_CFLAGS + fi + + if test $gint_cv_guile = no; then + GUILE_INCLUDES= + GUILE_LIBS= + GUILE_VERSION= + GUILE_VERSION_NUMBER= + m4_if([$3],,[AC_MSG_ERROR(required library libguile not found)], [$3]) + else + save_LIBS=$LIBS + save_CFLAGS=$CFLAGS + LIBS="$LIBS $GUILE_LIBS" + CFLAGS="$CFLAGS $GUILE_INCLUDES" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libguile.h>]], + [SCM_DEVAL_P = 1; + SCM_BACKTRACE_P = 1; + SCM_RECORD_POSITIONS_P = 1; + SCM_RESET_DEBUG_MODE;])], + [gint_cv_guile_debug=yes], + [gint_cv_guile_debug=no]) + if test $gint_cv_guile_debug = yes; then + AC_DEFINE_UNQUOTED(GUILE_DEBUG_MACROS, 1, + [Define to 1 if SCM_DEVAL_P, SCM_BACKTRACE_P, SCM_RECORD_POSITIONS_P and SCM_RESET_DEBUG_MODE are defined]) + fi + AC_CHECK_TYPES([scm_t_off],[],[],[#include <libguile.h>]) + LIBS=$save_LIBS + CFLAGS=$save_CFLAGS + + AC_SUBST(GUILE_SITE) + _GINT_IF_OPTION_SET([std-site-dir], + [GUILE_SITE=_gint_site_dir], + [AC_ARG_WITH([guile-site-dir], + AC_HELP_STRING([--with-guile-site-dir=DIR], + [specify directory to install guile modules to]), + [case $withval in + /*) GUILE_SITE=$withval;; + yes) GUILE_SITE=_gint_site_dir;; + *) AC_MSG_ERROR([Argument to --with-guile-site-dir must be an absolute directory name]);; + esac], + [GUILE_SITE=_gint_site_dir + pfx=$prefix + test "x$pfx" = xNONE && pfx=$ac_default_prefix + case $GUILE_SITE in + $pfx/*) ;; # OK + *) AC_MSG_WARN([guile site directory "$GUILE_SITE" lies outside your current prefix ($pfx).]) + GUILE_SITE='$(datadir)/guile/site' + AC_MSG_WARN([Falling back to ${GUILE_SITE} instead. Use --with-guile-site-dir to force using site directory.]) + ;; + esac])]) + AC_DEFINE_UNQUOTED(GUILE_VERSION, "$GUILE_VERSION", + [Guile version number]) + AC_DEFINE_UNQUOTED(GUILE_VERSION_NUMBER, $GUILE_VERSION_NUMBER, + [Guile version number: MAX*10 + MIN]) + m4_if([$2],,,[$2]) + fi +]) + |