Dependency Breaks Build

562 views Asked by At

I am trying to build a project using Haiku Jam. (The code for the project is available online at https://github.com/Andromeda-OS/LLVM. I recommend at least looking at the directory names in that project so that you know what components I am referring to below.)

I am trying to (re)build the llvm-tblgen utility. llvm-tblgen has a link-time dependency on libLLVMSupport and libLLVMTableGen. If I add a Jam DEPENDS statement so that llvm-tblgen requires that libLLVMSupport be built first, Jam does not build llvm-tblgen at all. Jam outputs don't know how to make ./bin/llvm-tblgen/llvm-tblgen, and gives me no other useful diagnostics, even when running at high verbosity levels.

If I remove every dependency command from llvm-tblgen, then and only then does Jam compile the file. However, if libLLVMSupport.a is not present, linker errors will result as Jam was not told to compile libLLVMSupport first. However, if I tell Jam to build libLLVMSupport first, then llvm-tblgen will not be built at all! Any pointers?

1

There are 1 answers

1
user686249 On BEST ANSWER

To analyze problems like this you can use:

jam -n -dm

This prints the make tree. The error is printed while processing the make tree, so you see it right where Jam encounters it and can easily track back the dependencies that lead to it. In this case "Intrinsics.gen" has the non-existent dependency and grep reveals the following line in "include/llvm/Jamfile":

DEPENDS $(Intrinsics.gen) : $(Intrinsics.gen:D) $(TOP)/bin/llvm-tblgen/llvm-tblgen ;

So, if you're jamming in the top directory, the latter dependency expands to "./bin/llvm-tblgen/llvm-tblgen". Since target names in Jam are just literal strings -- there's no matching for possible paths happing -- this does not match the target "llvm-tblgen" you define in "bin/llvm-tblgen/Jamfile".

The solution is: Never use target names with path components, just use the file name. If two different targets have the file name, add grist to one or both of them (e.g. "foo" and "foo") to make them unique again. If SEARCH or LOCATE is properly set on the target -- which almost all standard rules do -- Jam will automatically resolve the target name to the matching path (aka bind the target) when used in actions. For instance your TableGen rule should rather look like:

rule TableGen
{
  DEPENDS $(<) : llvm-tblgen $(>) ;
  TableGen1 $(<) : llvm-tblgen $(>) ;
}

actions TableGen1
{
  $(2[1]) $(TABLEGEN_FLAGS) -I $(TOP)/generated-include -I $(TOP)/lib/Target -I $(TOP)/include -o $(1) $(2[2-])
}

"llvm-tblgen" is now passed as a target to the actions and thus automatically bound to the correct path.

You can simplify "include/llvm/Jamfile":

SubDir TOP include llvm ;

MakeLocate Intrinsics.gen : $(TOP)/generated-include/llvm ;
SEARCH on Intrinsics.td = $(SUBDIR) ;
TableGen Intrinsics.gen : Intrinsics.td ;
TABLEGEN_FLAGS on Intrinsics.gen = -gen-intrinsic ;

Usually one adds the subdirectory grist to source files (using [ FGristFiles Intrinsics.td ] in this case), so clashes with equally named source files in other directories are avoided preemptively. If you use the TableGen rule elsewhere as well, you may also want to move the above MakeLocate and SEARCH there as well. Instead of setting TABLEGEN_FLAGS here, I would make that a third parameter of TableGen and set the variable there, so the rule becomes even more convenient to use.

A few other things I noted:

  • Your Wildcard rule is unnecessarily complicated. After the first line you can just return $(results:BS) ;. The string/path operations are applied to each element of a list, so there's no need to do that manually.
  • Your LLVMLinkExecutable and _GetLinkerFlags are rather complicated as well. If the "libraries" parameter of LLVMLinkExecutable is always the abbreviated name of a library the build system builds, you can just use LinkLibraries $(1) : lib$(3).a ; instead of the "for" loop. The rule establishes the dependencies and adds the libraries to the link line (with their path instead of "-L... -l...").