Wrong Jamfile in workspace

Added by Steven Craft almost 2 years ago

Until recently, our basic Jam set up has been to have one Jamfile in each projects directory, the application itself is made up of a few dozen projects. The main project sub includes top level Jamfiles (for engine/base game) which in turn sub include the various dependencies. Each Jamfile ultimately has a C.Library or C.Application in, and sets up the correct dependencies so all the libraries are built and then linked into the final application.

So, we have now changed this, as part of our move to JamPlus3. Specifically what we have changed is we no longer globally call C.Library/A.Application in each Jamfile. The reason behind it is we want to use the multi toolchain support (this is required for iOS submissions now as they must contain arm64 architecture on top of a 32 bit i.e. armv7). This change means we want to build each library/application per tool chain, so our basic top level structure now loops over the various architecture and calls 'BuildGame' which is a rule which ultimately calls C.Application but will call BuildEngine first. BuildEngine is from another Jamfile, and will ultimately call C.Library after calling BuildLibraryX (which again eventually calls C.Library). This means we can make all the required C.Application/C.Library calls for each architecture (which is what we need, we build an arm64 exe, and armv7 exe, then combine these into a 'fat binary' which is part of the final bundle).

This all seems to work well, I have managed to build an iOS application (.app file) which I can zip and successfully upload to iTunes Connect. However, when the workspace is generated, I have found that the wrong Jamfiles are being added to each project. From looking at the code that handles this, it appears to set the CURRENT_JAMFILE when a SubInclude call is made, so for instance:

Subinclude ROOT Engine Renderer : OpenGL.jam ;

The above would include the $(ROOT)/Engine/Renderer/OpenGL.jam file, but before doing so will set CURRENT_JAMFILE to $(ROOT)/Engine/Renderer/OpenGL.jam (and reset it back once the include completes). This worked perfectly well when we globally defined C.Library/C.Application in the Jamfile (as C.Library/C.Application will dump the project during workspace generation, at which point the workspace project is created and CURRENT_JAMFILE is added to the project). However, now we don't call C.Library/C.Application globally in each Jamfile, instead these calls are made from other Jamfiles. So we have a set up like:

  1. # $(ROOT)/Engine/Jamfile.jam #######################
    SubDir ROOT Engine;
    rule BuildEngine {
    SubDir ROOT Engine ;
    C.Library [... bunch of stuff here] ;
    }
  2. # $(ROOT)/Game/Jamfile.jam #######################
    rule BuildGame {
    SubDir ROOT Game ;
    SubInclude ROOT Engine ;
    BuildEngine ;
    C.Application [... bunch of stuff here] ;
    }
  3. # We actually look the architectures here, but for simplicity...
    BuildGame ;

So Jam starts by processing ROOT/Game/Jamfile.jam, the BuildGame rule is called, the $(ROOT)/Engine/Jamfile.jam is imported (during import CURRENT_JAMFILE is set, but then returned to its previous value once the import completes). Then BuildEngine is called, this rule sets the SubDir (this is required, otherwise the Engine would build into the Game's output directory), and ultimately the Engine calls C.Library. During the workspace generation this C.Library call will add the relevant source files to the project, it will also add CURRENT_JAMFILE, unfortunately this will not be set correctly (it is only set correctly during the Sub Include).

As a workaround for now, I have added the following to the end of the the SubDir rule:

local jamfile ;
if $(>) {
jamfile = $(>:S=.jam) ;
}
else {
jamfile = $(JAMFILE) ;
}
CURRENT_JAMFILE = $(jamfile:R=$(SUBDIR)) ;

And have created my own SubDir rule like this:

rule SetSubDir PATH : FILE {
if $(TARGETINFO_LOCATE) {
SubDir $(PATH) : $(FILE) ;
}
else {
SubDir $(PATH) ;
}
}

What this means is when I call SetSubDir ROOT Engine Renderer : OpenGL.jam ; during workspace generation it will set CURRENT_JAMFILE to $(ROOT)/Engine/Renderer/OpenGL.jam and during actual build, it will call SubDir ROOT Engine Renderer (during the build we don't want to pass the Jamfile name here, otherwise it'll locate the output into a directory called OpenGL.jam). This means that when C.Library is called (as it is preceded by a SetSubDir call) the value of CURRENT_JAMFILE is correct so the correct Jamfile gets added to the project.

This is all working for me, however it strikes me this probably isn't quite right! Either I should be structuring things differently, or perhaps what I am doing is completely valid, and the logic for CURRENT_JAMFILE can be improved?

I am experiencing this issue on both the NextGen and JamPlus3 branches.

Steve