Skip to content

UsingCodeGenerators

Mats Wichmann edited this page Sep 3, 2023 · 5 revisions

How to build a program that can then be used to build other files ?

One fairly common requirement in builds is to automate part of the work by writing a code generator tool in source code, and then in the build, compile that tool, run it, and use the generated files as sources to other parts of the build. The challenge here is hooking this kind of relationship into the plumbing of a build.

A simple way to do that is to use an SCons facility called an Emitter. An Emitter is a function which takes a target list and source list from a relationship declared to a Builder, and decides whether either or both need to be modified by whatever criteria that emitter decides. "Modify" can mean adding source or targets - for example, a Fortran module file that will be produced as a result of compiling a source file that contains certain declarations into an object file. "Modify" can also mean changing attributes of sources or targets, such as setting the shared flag on a target.

You can learn more about Emitters in the SCons User Guide. You want to hook an Emitter into a Builder, which is straightforward when writing a new builder. There's another section in the User Guide on the topic of hooking an Emitter into an existing Builder.

In our case we want to create an Emitter that will declare that the files generated by our tool are dependencies building the program itself. For this example, lets use a mk_vds program that can build a .vds file from a .txt input. This tool has to be built from the source file mk_vds.c.

# SConstruct file

env = Environment()

# create the mk_vds generator tool
mk_vds_tool = env.Program(target="mk_vds", source="mk_vds.c")

# create emitter that says that any target (.vds) depends on our mk_vds program
def mk_vds_emitter(target, source, env):
    env.Depends(target, mk_vds_tool)
    return (target, source)


# create a builder (that uses the emitter) to build .vds files from .txt files
# The use of abspath is so that mk_vds's directory doesn't have to be added to the shell path.
bld = Builder(
    action=mk_vds_tool[0].abspath + " < $SOURCE > $TARGET",
    emitter=mk_vds_emitter,
    suffix=".vds",
    src_suffix=".txt",
)

# add the new Builder to the list of builders
env["BUILDERS"]["MK_VDS"] = bld

# generate foo.vds from foo.txt using mk_vds
env.MK_VDS("foo.txt")

If you look at the resulting dependency tree you can see it works:

$ scons --debug=tree foo.vds
+-foo.vds
 +-foo.txt
  +-mk_vds
    +-mk_vds.o
      +-mk_vds.c
Clone this wiki locally