smake musings

2003-11-20 VI I just love smake. Mostly because I have tclsh across all the platforms I work on. I thought there was something called "Jam", but couldn't find it.

[ LV see http://freetype.sf.net/jam/index.html for an intro to jam, which appears to be a part of perforce. ]

In any case I like the simple smake to the more complex (and more capable) bras.

Some notes about smake follow. I have also emailed the author.

  • The documentation describes "uptarget". The command is really "upTarget", note the uppercase T
  • In the code after the depend, the variable "target" is the current target being worked on.
  • If a target doesn't exist and if all the dependencies are up-to-date, then smake doesn't run the rule, which is counterintuitive, more on this below.
  • stdout and stderr redirection on the exec doesn't work.

Hm, didn't see your email. Maybe it got mixed up in all the spam :P Will look at these issues when I have a bit of time. --Setok


When a target doesn't exist but all the dependencies are up-to-date, smake doesn't run the rule, which is counter-intuitive. e.g.

   target a {
     depend {b.o c.o} {
        link a [list b.o c.o]
     }
   }

Here you would expect that if a doesn't exist, but b.o and c.o are up-to-date, then the link will run. That doesn't seem to happen for me (I could be just using it wrong). In any case, to fix this here's what I added:

Just before this:

    if {$update} {
        uplevel 1 $op
        uplevel 1 {set targetUpdate 1}
    }

in the proc "depend", I added this:

    if !$update {
        set thistarget [lindex $SmakeTargetPath end]
        if ![file exists $thistarget] {
            dputs 3 "Target $thistarget hasn't been made yet, update"
            set update 1
        }
    }

Hope this helps someone!

2003-11-20 VI : A possible cleaner way to do the set this target is to do it like this:

    if !$update {
        upvar target thistarget
        if ![file exists $thistarget] {
            dputs 3 "Target $thistarget hasn't been made yet, update"
            set update 1
        }
    }

2003-11-20 VI : stdout and stderr redirection doesn't work. I often write a command like

     exec grep -n Error $logfile > errors.log

smake nicely renames exec and prints the line before executing, but in this case the redirection doesn't work. To fix that, I change:

        eval "exec_old $args >@ stdout 2>@ stderr"

to just

        eval "exec_old $args"

I'm not sure why the >@ and 2>@ are there. When would it be needed?


2003-12-03 VI : I do know now why 2>@ is required. If you don't do that then the stuff printed to stderr causes the tcl exec to think that there's been an error. So like make if we want to be just return code based, then we need to add the 2>@. A naive implementation :

 proc exec {args} {
    global TCL_ERROR

    if {![regexp {>&} $args] && ![regexp {[|][&]} $args] && ![regexp {2>} $args]} {
        append args " 2>@ stderr"
    }

    puts $args

    set rc [catch {
        eval "exec_old $args"
    } errmsg]

    if {$rc == $TCL_ERROR} {
        puts stderr "$::errorCode

$::errorInfo"

        error $errmsg
    }
 }

I also found that sometimes I have stuff that I want to print the stuff I'm doing in Tcl to the stdout because many things that were external commands in make become internal. e.g. file mkdir. So I added another trivial proc:

 proc tcl {args} {
    puts $args
    eval $args
 }

So I can mix exec and tcl calls in my commands and everything gets printed...

Should the eval above really be uplevel? And perhaps there's a better way of writing those three regexps in that expression..


2003-12-19 VI I also like the way make lets you set variables on the command line, so:

        } elseif {[string equal $arg "--version"]} {
            # Show version
            puts $SmakeVersion
            exit
        } elseif {[regexp {^(.*)=(.*)$} $arg => name value]} {
            set ::$name $value
        } else {
            set parsed_args [lappend $parsed_args $arg]
        }

The two lines starting with the regexp are added to the parse_opt proc.


2003-12-26 VI Some bright guy added this line, saying this:

TMK [L1 ] is also very capable and easy to use.

in the middle of a paragraph which I wrote with MY name in the beginning. I have never used TMK and do not have any opinion on it. I rather resent somebody putting a statement like I wrote it. Feel free to add a paragraph, or a few paragraphs or a whole page anywhere, but please don't make it sound like it came from me!


2004-04-27 VI Two more features of make that I like. One is the ability to define variables on the command line the second is to have multiple targets. The original code uses a variable value as the first argument to lappend, which I think is not the intent:

In parse_opt, replace:

        } else {
            set parsed_args [lappend $parsed_args $arg]
        }

with:

        } elseif {[regexp {^(.*)=(.*)$} $arg => name value]} {
            set ::$name $value
        } else {
            lappend parsed_args $arg
        }

The first branch allows for VAR=value on the command line. For multiple targets you also need to change (towards the end of smake.tcl)

    set target [lindex $argv 0]

to:

    set target $argv

2004-08-04 VI another bug. If a name exists as a target, then any file depending on this target is not rebuilt, unless this is rebuilt. An example is clearer

 target a {
   depend {b} {
      exec cp b a
   }
 }

 target b {
    depend {c} {
       exec cp c b
    }
 }

.

 % touch c
 % smake
 cp c b
 cp b a
 % touch b
 % smake

Nothing happens here, I'd have expected a "cp b a" to happen, but

 % touch c
 % smake
 cp c b
 cp b a

That is because c isn't a target, but b is. The fix is to add these in the proc depend. The first three lines below already exist. the next 7 are new

            set update [expr $update || [execTargetCode
                                             [convertTargetToProc $target]
                                             $target]]

            if {!$update && [file exists $thistarget]} {
                upvar target thistarget
                if {[file mtime $thistarget] < [file mtime $target]} {
                    dputs 2 "target $target wasn't rebuilt but is newer than $thistarget"
                    set update 1
                }
            }

OCZ (5 aout 2004) I suggere to look at another method of construction of make which I have program on the site: [L2 ]


2004-08-06 VI Thanks for the pointer. Nice. Even with my limited french. But I think smake is still cool. And the makefiles look so much like tcl. so much like tcl.


AM I have taken an interest in smake - for various reasons, mostly having to do with a wish to automate all manner of things in a project. I read the source code, wonderfully compact, and I came up with a bunch of ideas to enhance it:

  • The implementation of the compile and link commands is a bit UNIX-centric. A port to Windows (using MSVC as the compiler/linker tool chain) would involve changing the command lines. Perhaps via a configuration file?
  • You could easily use smake in a Tcl-based IDE - but then the procedures should be tucked away in a namespace of their own (or perhap a slave interpreter) to prevent interaction with the rest of the code.
  • As it is basically an implementation of logical inferences, you could also adapt it to search "knowledge bases". This is just a wild, unelaborated idea, but look at this:

I have the following facts:

  • A mammal is an animal
  • A cow is a mammal
  • A cow has four legs
  • A "red cow" is a cow (I do not know the proper English term for "roodbont") [AMG: Hereford, perhaps? AM Not sure, but I guess it is brown and it is a cow, so that will do :)]
  • A "red cow" has a brown colour
  • Brown is a colour

We can now ask the following questions:

  • Is a "red cow" an animal?
  • Does a "red cow" have a colour?

The processing of these questions looks a lot like the dependency tree of a program ...

(Oh, one more remark: the examples in the documentation are a trifle out of touch with the text)


Setok I just created a git repository of the stuff I had (doesn't include any of these patches yet). See the smake page for details.