From: ···············@gmail.com
Subject: declarative configuration file handling
Date: 
Message-ID: <698030c0-d9af-4f07-8893-6833a71e4414@u6g2000prc.googlegroups.com>
Hello,

I am trying to create a simple "make" like build system in common
lisp. The basic functionality is in place and is usable at the
interactive prompt.

E.g. I can do the following:

    (setf *p* (program "test.out" '("a.c" "b.c" "c.c")))

This would create the relevant data structures following which
I can do things like:

    (build *p*)  ; creates test.out with dependency / timestamp check
    (clean *p*)  ; clean generated files

For taking this app a step closer to a make like utility, I was
thinking of the user being able to create a config file like:

bld.conf:
  (program "test.out" '("a.c" "b.c" "c.c"))

-end bld.conf

and at the shell prompt the user could do:

$ clisp bld.lisp
    ... read bld.conf and calls (build x) on the program data struct

$ clisp bld.lisp clean
   ... reads bld.conf, constructs program data structures
       and calls clean on generated files

I looked at the read/write functionality which is very cool but
most of the examples/discussion I found were related to using these
to store/restore data.

To keep the enduser bld.conf simple, I would like to only keep the
above 1 statement in it and not bother a non-lisp user with setf etc.

Coming from a C and Python background, the only way I can think of
for handling the build script is:
  - read the s-expr "(program ...)" in
  - based on (first expr) .. which gives 'program use a mapping
    to send the arguments to a process-program function

    or

  - use eval to evaluage the '(program ...) form and the assign
    to a var and issue a build

What might be really nice is to have the above bld.conf work for
a non-lisp user while an advanced user can write lisp code in the
bld.conf for some configuration etc. But I am trying to take this
one step at a time.

I am still learning lisp and am not quite sure if the above methods
are the ideomatic way of doing this in lisp. Or maybe I am totally off
the mark and there is a very different way of doing this in lisp :)

Any comment and suggestions are much appreciated.

Thanks,
Parth
PS: Code at:
http://code.google.com/p/bld/source/browse/trunk/src/bld-core.lisp

From: Kaz Kylheku
Subject: Re: declarative configuration file handling
Date: 
Message-ID: <d6878380-40b6-4c75-9a5e-d729aa91393e@79g2000hsk.googlegroups.com>
On Jun 23, 10:47 am, ···············@gmail.com wrote:
> Coming from a C and Python background, the only way I can think of
> for handling the build script is:
>   - read the s-expr "(program ...)" in
>   - based on (first expr) .. which gives 'program use a mapping
>     to send the arguments to a process-program function
>
>     or
>
>   - use eval to evaluage the '(program ...) form and the assign
>     to a var and issue a build

The third possibility is that the build script is simply loaded as a
Lisp source file. The build rules are just Lisp top-level forms, (and
in fact the script can include arbitrary forms like DEFUN, etc). The
build system's rules are written in a macro language. The macros
expand into something which builds up pieces of the rule tree and
enters it into a global rule database. The BUILD function then just
works with that database.
From: Rainer Joswig
Subject: Re: declarative configuration file handling
Date: 
Message-ID: <joswig-E78105.20422623062008@news-europe.giganews.com>
In article 
<····································@u6g2000prc.googlegroups.com>,
 ···············@gmail.com wrote:

> Hello,
> 
> I am trying to create a simple "make" like build system in common
> lisp. The basic functionality is in place and is usable at the
> interactive prompt.
> 
> E.g. I can do the following:
> 
>     (setf *p* (program "test.out" '("a.c" "b.c" "c.c")))
> 
> This would create the relevant data structures following which
> I can do things like:
> 
>     (build *p*)  ; creates test.out with dependency / timestamp check
>     (clean *p*)  ; clean generated files
> 
> For taking this app a step closer to a make like utility, I was
> thinking of the user being able to create a config file like:
> 
> bld.conf:
>   (program "test.out" '("a.c" "b.c" "c.c"))
> 
> -end bld.conf
> 
> and at the shell prompt the user could do:
> 
> $ clisp bld.lisp
>     ... read bld.conf and calls (build x) on the program data struct
> 
> $ clisp bld.lisp clean
>    ... reads bld.conf, constructs program data structures
>        and calls clean on generated files
> 
> I looked at the read/write functionality which is very cool but
> most of the examples/discussion I found were related to using these
> to store/restore data.
> 
> To keep the enduser bld.conf simple, I would like to only keep the
> above 1 statement in it and not bother a non-lisp user with setf etc.
> 
> Coming from a C and Python background, the only way I can think of
> for handling the build script is:
>   - read the s-expr "(program ...)" in
>   - based on (first expr) .. which gives 'program use a mapping
>     to send the arguments to a process-program function
> 
>     or
> 
>   - use eval to evaluage the '(program ...) form and the assign
>     to a var and issue a build

a)

Using READ to read the data is one way. Then you have to
process it. For the user it might be important to get
good feedback on syntax errors and not some obscure error
message deep from some code maybe with a mile long backtrace
which says nothing.

Reading the data and then 'interning' it is quite common.

b)

The other typical approach would be to write a macro
that processes the data (say, a PROGRAM macro) and
just load the file. Again, the macro should have
good error reporting on syntax errors. A macro
has the advantage over a function that the surface
syntax could be a bit easier to use:

function PROGRAM

(program "test.out" '("a.c" "b.c" "c.c"))

Macro PROGRAM

(program "test.out" ("a.c" "b.c" "c.c"))

Without the quote. The advantage may be more visible
with more complex descriptions.
Again, just load the file.


I find it especially important that there are first class
datastructures (that can be inspected) describing
the software system. Not just some bunch of generated
procedures (which may have not accessible data).

Common Lisp also allows you to use descriptive keywords/argument
instead of unnamed positional arguments.

Most software in that area also would register ('intern')
the data structure under some name. So you could
also retrieve the data structure by name:


(defprogram my-test-program
   :out "test.out"
   :version :released
   :author "Foo Bar"
   :files ("a.c" "b.c" "c.c"))

(build-program (find-program-name 'my-test-program))

Or just:

(build-program 'my-test-program)


You could also use one of the existing DEFSYSTEMs. There are
some widely used ones, which you could extend to C
files and building of compiled c applications.




> 
> What might be really nice is to have the above bld.conf work for
> a non-lisp user while an advanced user can write lisp code in the
> bld.conf for some configuration etc. But I am trying to take this
> one step at a time.
> 
> I am still learning lisp and am not quite sure if the above methods
> are the ideomatic way of doing this in lisp. Or maybe I am totally off
> the mark and there is a very different way of doing this in lisp :)
> 
> Any comment and suggestions are much appreciated.
> 
> Thanks,
> Parth
> PS: Code at:
> http://code.google.com/p/bld/source/browse/trunk/src/bld-core.lisp

-- 
http://lispm.dyndns.org/
From: Pascal J. Bourguignon
Subject: Re: declarative configuration file handling
Date: 
Message-ID: <87mylbn949.fsf@hubble.informatimago.com>
···············@gmail.com writes:
> [...]
> bld.conf:
>   (program "test.out" '("a.c" "b.c" "c.c"))
>
> -end bld.conf
> [...]
> What might be really nice is to have the above bld.conf work for
> a non-lisp user while an advanced user can write lisp code in the
> bld.conf for some configuration etc. But I am trying to take this
> one step at a time.

Instead of doing the read eval loop yourself, you could let CL do it
itself.  Namely, using LOAD.  Then both naive users and lisp
programmer could use your system.

For this to work, you have to define your 'make' language, mostly by
defining macros.

  (defmacro program ...)
  (defmacro target (label &body body) ...)

Naive users could write:

  (program "test.out" ("a.c" "b.c" "c.c"))

while lisp programmers may write:

  (defun compile-to-c (source)
     ...) 
  (target all    (mapc (function compile-to-c) (directory "*.lisp"))) 
  (target clean  (mapc (function delete-file)  (directory "*.c")))

  (program "test.out" #.(directory "*.c"))


You forgot to load your system:

clisp bld-core.lisp  test.lake  # Lisp mAKE.



PS: for lisp code, it would be better to use http://paste.lisp.org/
than any other less lisp oriented code paste web services.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"A TRUE Klingon warrior does not comment his code!"
From: Pascal J. Bourguignon
Subject: Re: declarative configuration file handling
Date: 
Message-ID: <87iqvzn8wi.fsf@hubble.informatimago.com>
···@informatimago.com (Pascal J. Bourguignon) writes:
> For this to work, you have to define your 'make' language, mostly by
> defining macros.
>
>   (defmacro program ...)
> [...]
>   (program "test.out" #.(directory "*.c"))

Oops.  A macro has its advantage, but if we want to be able to have
make rules arguments evaluated, it might be better not to do it at
read time, so these macro should perhaps evaluate (or  just
conditionnaly evaluate) their arguments:

(defmacro program (target form)
  (if (and (listp form)
           (every (function stringp) form))
     `(... :dependencies (quote ,form) ...)
     `(... :dependencies ,form ...)))

So we can write either:

   (program "test.out" ("a.c" "b.c" "c.c"))

or:

   (program "test.out" (directory "*.c"))

while having the directory form evaluated after the forms in the
TARGET macros.
 
-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

READ THIS BEFORE OPENING PACKAGE: According to certain suggested
versions of the Grand Unified Theory, the primary particles
constituting this product may decay to nothingness within the next
four hundred million years.
From: ···············@gmail.com
Subject: Re: declarative configuration file handling
Date: 
Message-ID: <adb36136-183a-45fa-b332-73dbf8986bcf@w8g2000prd.googlegroups.com>
On Jun 24, 2:22 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> ····@informatimago.com (Pascal J. Bourguignon) writes:
>
> > For this to work, you have to define your 'make' language, mostly by
> > defining macros.
>
> >   (defmacro program ...)
> > [...]
> >   (program "test.out" #.(directory "*.c"))
>
> Oops.  A macro has its advantage, but if we want to be able to have
> make rules arguments evaluated, it might be better not to do it at
> read time, so these macro should perhaps evaluate (or  just
> conditionnaly evaluate) their arguments:
>
> (defmacro program (target form)
>   (if (and (listp form)
>            (every (function stringp) form))
>      `(... :dependencies (quote ,form) ...)
>      `(... :dependencies ,form ...)))
>
> So we can write either:
>
>    (program "test.out" ("a.c" "b.c" "c.c"))
>
> or:
>
>    (program "test.out" (directory "*.c"))
>
> while having the directory form evaluated after the forms in the
> TARGET macros.
>
> --
> __Pascal Bourguignon__                    http://www.informatimago.com/
>
> READ THIS BEFORE OPENING PACKAGE: According to certain suggested
> versions of the Grand Unified Theory, the primary particles
> constituting this product may decay to nothingness within the next
> four hundred million years.

Now that you have mentioned it, using "load" makes a lot of sense.
It just didn't occur to me. :P

Yes, macros is probably more suited than functions for end user
APIs this scenario.

Thanks all, for the wonderful suggestions.

--
Parth