Introduction ------------ `libit' is a collection of common files and `lib/' type modules which, for most of them, already exist and were collected from other GNU packages. Libit also provides a program "libitize" that can ferry libit files from the database to the lib/ directory of your ongoing software project. The problems with libit in its present form: 1. It requires a lot of parallelizable collaboration between GNU maintainers. Other people won't want to collaborate unless we make their life super-simple. Emacs got lots of people to collaborate because it is very trivial to extend it by writing Emacs Lisp code. And because Emacs Lisp is addictive and fun. 2. Some libit files depend on other libit files. If you want to use the resources provided by one of them, you need to bring some others along. 3. Generating appropriate configure.in and Makefile.am codes is very tedious. 4. A header file is needed to make the resources of the lib/ modules available to the main source code. Putting that header file together is also a tedious task In this file, I discuss an approach for dealing with these problems. The grand scheme of things -------------------------- A new implementation will be composed of: 1. A Guile module called "autolib" 2. A Guile script called "libitize" that uses the module to provide functionality similar to the canonical "libitize" perl script 3. A Guile script called "autolib" that will generate configure.in and Makefile.am Ideally, to get started on a project, all you should have to do is: % acmkdir foo % cd foo-0.1 % libitize -i argp % autolib and then go type in some more source code. People who insist on crafting configure.in and all that by hand can simply not call "autolib" or pass options to autolib to suppress some of the things that it normally does. The libit database should contain the following stuff: 1. A collection of *.c and *.h files, the good old libit files. 2. A collection of needed autoconf macros. Some of these macros may actually belong in the Autoconf distribution. 3. A collection of Guile scripts stuffed in some directory somewhere that will be invoked in random order by the "autolib" module when it initializes itself. The Guile scripts will do two jobs: 1. Describe how each libit file must be handled. 2. Define "libit modules" as collections of libit files. Example modules we might want to have: argp -- all the argp files argp.h, argp-ba.c, ..... strings -- all the string stuff mem -- all the mem stuff etc.... Modules can overlap in terms of what libit files they covered. The actual handling is done on a per-file basis, not on a per-module basis. The API for the Guile scripts is where the juice is. We describe it after this general blurb. From the user point of view, all that needs to be done is to use "libitize" to install the files or modules that are wanted. Then invoke "autolib". Autolib will go over each file and check first if all the dependencies have been installed too. If not, it will prompt the user interactively on whether he wants them installed as well. User sez "sir yes sir". After all the dependencies are resolved, configure.in, Makefile.am and libit.h are put together. The description files should also contain some short documentation for each libit file that can be accessed through the 'libitdoc' program. Now we describe the Autolib Guile module. The programs libitdoc, libitize and autolib must be written on top of that module. I hope that the rest of what I want to say can be deduced from what I have already said, as far as this general mumbo-jumbo goes. 1. Action commands --------------- Procedure: autolib-initialize Initialize the autolib module. Parse .autolib in user's home directory. Look for the installed libit files, and get their names. Look for installed descriptions and parse them. Now the interpreter is in a state that it's ready to be used. Procedure: autolib-libit "file.c" If the file "file.c" does not exist, bring in a copy from the libit archive. If a copy exists, then compare against copy in the archive and figure out which copy we want to use. If the file does not exist, return an error message. Procedure: autolib-current-files Return a list of the files that have been installed so far in the autolib directory. Procedure: autolib-rebuild Rebuild the header file, configure.in and Makefile.am for the lib directory. This should basically involve getting a list of the files installed in lib/ and invoking autolib-handle on each one of them. Procedure: autolib-handle "file.c" Invoke the callback for handling the specific file "file.c" 2. Query commands -------------- Procedure: autolib-current-files Return a list of the files that have been installed so far in the lib directory. Scan the lib directory on the spot to determine what files are present. Only the files that are known to libit will be put on this list. Warnings for files not known. There should be a list of files that must be ignored. Maybe the user should be able to customize that list? Procedure: autolib-get-installed-files Return a list of the files that have been installed with libit Procedure: autolib-get-installed-packages Return a list of all the packages that have been installed with libit Procedure: autolib-is-libit-file-p "foo.c" Return #t is "foo.c" is a libit file. Return #f if "foo.c" is not a libit file. Procedure: autolib-get-file-dependencies "foo.c" Resolve all the dependencies of file "foo.c". This must be done recursively. Procedure: autolib-get-file-documentation "foo.c" Get documentation for a specific file. Procedure: autolib-get-package-files "package" Get the list of libit files that correspond to a specific package. 3. Defining callbacks for handling files and packages -------------------------------------------------- Procedure: autolib-package "packagename" "description" file-list Define an "autolib" package. An autolib package is a collection of libit files that provide a specific service. These files may be dependent on other files that would need to be installed as well. When a request goes to install a package, all the files listed as belonging to the package must be installed. During "rebuild" any dependencies will also be installed. Then rebuild would have to be invoked repeatedly until everything settles down. Procedure: autolib-file "foo.c" 'dependencies 'handler Specifify how to handle file "foo.c". You must specify a list of filenames: 'dependencies and an anonymous proc to handle the file named 'handler. The handler may use any Guile commands you like. However the API described in section 4 is most appropriate 'dependencies should be a list of libit files that are dependencies for this file. 4. Describe handlers for libit files --------------------------------- The following stuff should be used to put together the handler needed by autolib-file: Procedure: autolib-run "foo" Run the program "foo" with current directory the toplevel directory of your source code. You can use this for example to run things like "gettextize". 4.1 Autoconf related stuff ---------------------- According to Joel Weber in acheader-0.5, a configure.in file should have stuff listed in the following order: a AC_INIT, AM_INIT_AUTOMAKE | b AC_PROG_CC, any other PROG macros | 'program c wart macros (AC_AIX etc) | 'wart d X11 checks, -lit check | e library checks | 'library f header files | 'header g typedefs | 'typedef h structures | 'structure i compiler characteristics | 'compiler j system services | 'system k ud_GNU_GETTEXT | s AC_CONFIG_HEADER(config.h) | t AC_OUTPUT( | u list of Makefiles, one per line | 'makefiles v , | w extra commands | 'extra-commands x , | y init commands (I've never seen this used...) | 'init-commands z ) This stuff can be put together by the following procedure: Procedure: autoconf-output where "text" "Text" is whatever you want to add to autoconf. Usually we want to put put here predefined Autoconf macros so that we don't have to hardcord m4'ed shell scripts under Guile (yuck). The 'where' argument should say in which section of configure.in we wish to add the stuff outputed. A whole bunch of possibilities for the 'where' argument are shown on the table above. The following however deserve special treatement: Procedure: autoconf-check-headers "header1.h" "header2.h" .... Add stuff for invokation of AC_CHECK_HEADERS Eliminate duplicates. Procedure: autoconf-replace-funcs "memmove" "mkstemp" ... Add stuff for invokation of AC_REPLACE_FUNCS Eliminate duplicates Procedure: autoconf-need-declarations "stuff" "stuff" ... Add stuff for invokation of GCC_NEED_DECLARATIONS or whatever we happen to make out of this autoconf macro. I found a copy of that macro in gnome-support. Perhaps it should be a permanent part of Autoconf. Eliminate suplicates They should be implemented in terms of hooks. Hooks are stuff that are called at the end. Procedure: autoconf-output-hook 'handler After we are done handling this file, invoke this procedure. Hooks should be used to implement the 3 previous macros and any possible future macros that handle specialized crap. The handlers themselves would need to use autoconf-output to actually do something useful. The call to autoconf-output-hook itself should be done during autolib-initialize. The user may want to put some of these in his .autolib. Also a contributor might want to make a hook for yet another Autoconf macro that takes the form: MACRO(stuff1 stuff2 stuff3 ... stuffn) In fact perhaps we may want some additional API for putting such stuff together easier? Maybe an API for handling the duplicates? 4.2 Automake stuff -------------- Well, if Automake implements that += thing, then all I'll need here is: Procedure: automake-output "text" Add the quoted text to automake. To make things nicer, I might want to add some shortcuts: Procedure: automake-set-variable "name" "value" Add a variable assignment: name = value to Makefile.am Procedure: automake-append-variable "name" "value" Add a variable assignment: name += value Procedure: automake-append-libadd "name" "name" Append stuff to libfoo_a_LIBADD where 'foo' is the name of the support library. We must allow the invoker of autolib a way of specifying the name of the support library. Also, if the user wants this to be a shared library, we must append stuff to libfoo_la_LIBADD instead Procedure: automake-append-sources "name" "name" Same for libfoo_a_SOURCES as before Procedure: automake-target "target" "dependencies" rules Add text of the form: target: dependencies rules .......... where 'rules is a list of text lines. Each line must be preceded with one of these annoying things. 4.3 Header file stuff ----------------- The entire lib directory must have a header file dedicated to it. By default the file should contain the following: #ifndef STUFF_H #define STUFF_H #ifdef __cplusplus extern "C" { #endif [...CONTENTS...] #ifdef __cplusplus } #endif #endif This header file should be dedicated to C stuff. Perhaps a separate header file can handle C++ stuff, if the need arises. Contents should be added to the header file like this: Procedure: header-output "text" ... Add the quoted text to the header file To make things nicer, I might want to add some shortcuts: Procedure: header-prototype "HAVE_FOO" "prototype" "description" Output text for defining a missing prototype. The C preprocessor macro HAVE_FOO says whether the function is provided by the system or not. If not, then we obviously need to declare it with "prototype" and add a comment "description". The end result looks like this: #ifndef HAVE_FOO /* ** description text */ prototype #endif Note that sometimes, instead of HAVE_FOO we might want NEED_DECLARATION_FOO for functions that are available but the system is too dump to define them in their system prototypes. (Solaris for example tends to do that crap a lot). Other shortcuts can be defined in terms of header-output. 4.4 Example - alloca.c ------------------- Here's an example that describes alloca.c. This should be the contents of alloca.scm: -------------------------------------------------------------------- ;; Define the handler (define alloca-handler (lambda () ;; autoconf stuff (autoconf-output 'library "AC_FUNC_ALLOCA") (autoconf-check-headers "string.h" "stdlib.h") ;; automake stuff (automake-append-libadd "@ALLOCA@") ;; header stuff (header-output "# ifdef __GNUC__" "# define alloca __builtin_alloca" "# define HAVE_ALLOCA 1" "# else" "# if defined HAVE_ALLOCA_H || defined _LIBC" "# include " "# else" "# ifdef _AIX" " #pragma alloca" "# else" "# ifndef alloca" "char *alloca (); "# endif" "# endif" "# endif" "# endif" "#endif"))) ;; Associate the handler with alloca.c ;; no dependencies. (autolib-file "alloca.c" "" 'alloca-handler) ---------------------------------------------------------------------- Developers who collaborate with contributing to libit would then simply turn over to us the source files and write *.scm files for them. The autolib maintainer would take the stuff, go over it, and if it looks okey add it to the distribution. The autolib manual should have examples for alloca.c, getopt and argp that developers can imitate to write their own description files. Ideas needed for documentation strings. Each description file should state who maintains the file so that users of autolib will know where to send their bug reports.