Jam - Using a single target in multiple directories

Sometimes, desire to use an object file in multiple directories arises. Assume the following directory structure.

  • src/common/tests
  • src/unittest

A test in common/tests need only one object in unittest to be linked, but one does not want to make it as a library for that.

Simple method

Most simple method is to just use it. In Jamfile of common/tests, refer to the file in unittest directory as below.

Main common_test : common_test.c ../../unittest/unittest.c ;

This brings a small problem. Because unittest.c of Jamfile in unittest directory and ../../unittest/unittest.c above are different targets, unittest.c compiles twice for an update.

Sometimes this is a feature rather than a problem, like when different CFLAGS (e.g., -DIN_TEST) is needed for a testing purpose. But even in that situation, there’s a problem. Because those two targets are bound to the same file, if unittest.o is already built in unittest directory, an object without intented CFLAGS applied could be used for testing instead. In this case, do as below.

Main common_test : common_test.c unittest.c ;
SEARCH on [ FGristFiles unittest.c ] = $(SUBDIR)/../../unittest ;
CFLAGS on [ FGristFiles unittest.o ] += -DIN_TEST ;

Personally, I prefer not to use different compiler flags for real and test object because that could give a different result.

Solution

Define a rule in Jamrules, as below.

rule RemoteFiles files : top_tokens {
    local _path = [ FRelPath $(SUBDIR_TOKENS) : $(top_tokens[2-]) ] ;
    local _file ;
    for _file in $(files) {
        local _gristed = [ FGristFiles $(_file:R=$(_path)) ] ;
        local _remote_grist = [ FGrist $(top_tokens[2-]) ] ;
        Depends $(_gristed) : $(_file:BSG=$(_remote_grist:E)) ;
        LOCATE on $(_gristed) = $(SUBDIR) ;
        SEARCH on $(_gristed) = $(SUBDIR) ;
    }

    return [ FGristFiles $(files:R=$(_path)) ] ;
}

And use in Jamfile as below.

MainFromObjects common_test.o [ RemoteFiles unittest.o : TOP unittest ] ;
Objects common_tests.c ;

[ RemoteFiles unittest.o : TOP unittest ] returns ../../unittest/unittest.o with common grist which depends on unittest.o with unittest grist. Advantage of this method is that the rule and variable settings of the Jamfile in the directory which the object resides in is consistently used everywhere.

Make it a library

In most cases, this is the correct approach. Make it a library with Library and link with LinkLibraries.