6.7. Order-Only Dependencies: the Requires Function

Occasionally, it may be useful to specify that a certain file or directory must, if necessary, be built or created before some other target is built, but that changes to that file or directory do not require that the target itself be rebuilt. Such a relationship is called an order-only dependency because it only affects the order in which things must be built--the dependency before the target--but it is not a strict dependency relationship because the target should not change in response to changes in the dependent file.

For example, suppose that you want to create a file every time you run a build that identifies the time the build was performed, the version number, etc., and which is included in every program that you build. The version file's contents will change every build. If you specify a normal dependency relationship, then every program that depends on that file would be rebuilt every time you ran SCons. For example, we could use some Python code in a SConstruct file to create a new version.c file with a string containing the current date every time we run SCons, and then link a program with the resulting object file by listing version.c in the sources:


      import time

      version_c_text = """
      char *date = "%s";
      """ % time.ctime(time.time())
      open('version.c', 'w').write(version_c_text)

      hello = Program(['hello.c', 'version.c'])
    

If we list version.c as an actual source file, though, then version.o will get rebuilt every time we run SCons (because the SConstruct file itself changes the contents of version.c) and the hello executable will get re-linked every time (because the version.o file changes):


      % scons -Q
      gcc -o hello.o -c hello.c
      gcc -o version.o -c version.c
      gcc -o hello hello.o version.o
      % scons -Q
      gcc -o version.o -c version.c
      gcc -o hello hello.o version.o
      % scons -Q
      gcc -o version.o -c version.c
      gcc -o hello hello.o version.o
    

One solution is to use the Requires function to specify that the version.o must be rebuilt before it is used by the link step, but that changes to version.o should not actually cause the hello executable to be re-linked:


      import time

      version_c_text = """
      char *date = "%s";
      """ % time.ctime(time.time())
      open('version.c', 'w').write(version_c_text)

      version_obj = Object('version.c')

      hello = Program('hello.c',
                      LINKFLAGS = str(version_obj[0]))

      Requires(hello, version_obj)
    

Notice that because we can no longer list version.c as one of the sources for the hello program, we have to find some other way to get it into the link command line. For this example, we're cheating a bit and stuffing the object file name (extracted from version_obj list returned by the Object call) into the $LINKFLAGS variable, because $LINKFLAGS is already included in the $LINKCOM command line.

With these changes, we get the desired behavior of re-building the version.o file, and therefore re-linking the hello executable, only when the hello.c has changed:


      % scons -Q
      cc -o hello.o -c hello.c
      cc -o version.o -c version.c
      cc -o hello version.o hello.o
      % scons -Q
      scons: `.' is up to date.
      % edit hello.c
          [CHANGE THE CONTENTS OF hello.c]
      % scons -Q
      cc -o hello.o -c hello.c
      cc -o hello version.o hello.o
      % scons -Q
      scons: `.' is up to date.