Using Makefiles in Python projects

Marko Rauhamaa marko at pacujo.net
Tue Nov 12 09:46:30 EST 2019


Rhodri James <rhodri at kynesim.co.uk>:
> On 11/11/2019 19:05, Bill Deegan wrote:
>> You could use SCons (native python... )
>
> I could.  But I'd have to learn how to first, and particularly for
> complex cross-platform working that involves learning a lot of stuff I
> already know how to do in Make.  The time investment has never seemed
> that worthwhile.

SCons can be learned by reading its man page in a single afternoon.
That's how I learned it.

The toughest part is to decide *how* to use it as SCons entices you with
a plugin architecture. The temptation is to create a sentient monster
out of your build specification.

I have successfully applied a minimalistic style that is plain to a
random bystander who doesn't know either Python or SCons.

Here's a synopsis...

At the root of your repository, you write a (Python) file called
"SConstruct". In each source directory, you write a (Python) file called
"SConscript". The SConstruct uses SCons primitives to call the
individual SConscript files.

The SCons primitives in the SCons* files don't execute build commands.
Rather, they construct a dependency graph between all source and target
files across the repository. (I favor a non-globbing style where every
file is specified explicitly.)

Each SConscript file starts with the command:

    Import('env')

where "env" is short for "environment". An "environment" does *not*
refer to environment variables but to a collection of build parameters
(include paths, libraries, compilation flags etc).

The SConscript file might contain this line:

    env.Program("hello", [ "hello.c", "world.c" ])

meaning:

    Using the build parameters stored in "env", compile the executable
    program "hello" out of two C source code files.

SCons has builtin knowledge of some programming languages. So SCons
knows how to preprocess the source files and can deduct the
dependencies.

Note that the above "env.Program()" command does not yet execute
anything; it simply specifies a build node with associated explicit and
implicit dependencies.

Ad-hoc build rules are expressed using "env.Command()":

    env.Command("productivity.txt", [ "hello.c", "world.c" ],
                r"""cat $SOURCES | wc -l >$TARGET""")


The tricky part is writing SConstruct. At its simplest, it could be
something like this:

    def construct():
        env = Environment()
        SConscript("src/SConscript", exports="env")

    if __name__ == "SCons.Script":
        construct()


In my experience, all kinds of cross-compilation and variant-directory
logic is placed in SConstruct.


Marko


More information about the Python-list mailing list