Search code examples
sconsliterate-programmingnoweb

using scons for literate programming


Using noweb, I would either like to generate a document file (or a source file) from a noweb input file **.nw

From hand I would do something like that:

notangle my_program.nw > my_program.cpp
g++ -c my_program.o my_program.cpp
ln -o myapp ... my_program.o ...

Now I like to ask whether I can use scons to automate this.

Imagine, my project directory is on $MYPROJECT. THere we have "$MYPROJECT/SConstruct". Now I defined a scons tool "tangle.py" (simplified from "noweb.py). Here we have "$MYPROJECT/site_scons/site_tools/tangle.py"

import SCons.Builder

def cpp_emit (target,source, env):
    # I dont know what to do here ... please help
    return (target,source)

# Tangle to .cpp
__noweb_tangle_builder = SCons.Builder.Builder(
    action='/usr/bin/notangle $SOURCES >$TARGET',
    suffix='.cpp',
    src_suffix='.nw',
    emitter=cpp_emit)

# -----------------------
def generate(env):
    env['BUILDERS']['tangle']= __noweb_tangle_builder

def exists(env):
    return 1

This tool generates a cpp-file from a nw-file.

But if I do something like

def cpp_emit (target,source, env):
    new_source=target[0].name
    new_target=new_source.rstrip(".cpp")+".o"
    target.append(new_target)
    source.append(new_source)
    return (target, source)

I get into a dependency circle. SCons will find and abort with an error message.

Doing ...

def cpp_emit (target,source, env):
    new_source=target[0].name

    # someprogram.cpp -> someprogram.o
    new_target=new_source.rstrip(".cpp")+".o" 

    # lets avoid dependency cycle
    t = [] 
    t.append(new_target)
    source.append(new_source)
    # oops, we dropped target test.cpp. It wont be generated.
    return (t, source) 

... the tool would stop generating a cpp file from a nw file. (Cpp target dropped)

Do you know a working way to do use scons for literate programming?

thank you for reading.


Leonard


Solution

  • Here is the tool that I created. Note the use of env['BUILDERS']['Object'].src_builder to allow env.Program() to accept noweb files.

    # site_cons/site_tools/tangle.py
    import SCons.Builder
    
    __all__=['generate', 'exists']
    
    tangle_builder = SCons.Builder.Builder(
        action='$NOTANGLE $SOURCES > $TARGET',
        suffix = '.cpp',
        src_suffix = '.nw')
    
    def generate(env):
        env['NOTANGLE'] = exists(env)
        env['BUILDERS']['Tangle'] = tangle_builder
        if 'Object' in env['BUILDERS']:
            env['BUILDERS']['Object'].src_builder.append('Tangle')
    
    def exists(env):
        if 'NOTANGLE' in env:
            return env['NOTANGLE']
        return env.WhereIs('notangle')
    

    And its use:

    # SConstruct
    env = Environment(tools=['default', 'tangle'])
    env.Program('my_program.nw')
    

    Here is the output of the above SConstruct:

    $ scons -Q
    /usr/bin/notangle my_program.nw > my_program.cpp
    g++ -o my_program.o -c my_program.cpp
    g++ -o my_program my_program.o
    $ scons -c
    scons: Reading SConscript files ...
    scons: done reading SConscript files.
    scons: Cleaning targets ...
    Removed my_program.cpp
    Removed my_program.o
    Removed my_program
    scons: done cleaning targets.