I definitely end up with similar issues, due to using headers
generated by Google protocol buffers. I've had to add explicit
dependencies on the .h. just as you mention. It hasn't bothered me
enough to fix it (yet), but I've thought about how I *think* I should
fix this. I've tried to explain this below. You could also look at how
Chrome deals with this via gyp, since they must run into this issue. I
couldn't figure it out from looking at the gyp manual for 5 minutes
right now though.
The way that I was thinking about fixing this: when something depends
on a rule that generates a .h, I need to "propagate" that .h
dependency to any compile rules that depend on it. As a quick example,
defined in a pseudo-ninja format:
# Generate the .cc and .h; compile the .cc into a library.
build libmessage: pb_library message.proto
# Generate a library that depends on message_protocolbuffer
build libsomething: library message_protocolbuffer libsomething.cc
other_file.cc
# Create an EXE using libsomething
build someexe: executable libsomething executable.cc some_other_file.cc
In this case, what I *think* should happen is that the compile rules
generated by the "executable someexe" rule should include explicit
dependencies on the generated header file. In other words, the compile
executable.cc rule will depend on message.pb.h. Note: It is important
that this is an "order-only dependency." This has the advantage that
it will ensure the correct build order, but if some_other_file.cc
doesn't actually include the header file, when the header file is
regenerated, it won't trigger a recompile. It will only trigger a
recompile if some_other_file includes the header, since it will then
be "upgraded" to an implicit dependency.
Evan
You make a good point. In my case, anything that generates code
generates a .c or a .cc that needs to be linked, so I need an explicit
dependency anyway.
There is a design trade-off here: should the user explicitly define
dependencies, or should they be automatically determined?
Ninja's "philosophy" documentation states "Ninja contains the barest
functionality necessary to describe arbitrary dependency graphs". In
the case of these generated header files, we've both discussed the
fact that it already *can* express the dependencies correctly: either
explicit or order-only dependencies do the "right thing". So in my
opinion, this functionality belongs in the .ninja generator, and not
in Ninja itself.
> It's entirely possible to have rules which just generate .h files,
> and to have source code which includes those files. Leaving it to
> the developer to add an explicit dependency whenever they add
> #include leaves too much room for human error.
However this problem also happens with linking: Whenever you #include
something, you need to figure out what library you need to link, and
manually add a dependency. You could probably make a build system that
automatically figures out what libraries to link: take the errors
generated by the linker, search for the appropriate symbols, and
automatically add the correct linker dependencies.
> Given the line "build foo.o: cc foo.cc": on the first build,
> everything that starts with "build/" and ends with ".pb.h" would be
> brought up to date.
Doesn't this permit circular dependencies? What if in order to build
"build/foo.pb.h" I need to build "protoc" using a "build protoc.o: cc
protoc.cc" rule? At any rate: I believe that rules of this form could
be implemented as a pre-processor that generates the
appropriate .ninja files.
Evan
Right. I use a primitive script to generate .ninja files.
> Glad to see that you agree we need to exprss the dependency on
> generated files explicitly in ninja files. But what do you think
> about the consistent checking process I mentioned in my first mail?
This is actually an interesting idea: It might always be a "bug" if a
rule has an implicit dependency on a generated file. It probably
always should be an explicit or order-only dependency. It probably
might be possible to add this as a ninja tool, so it can re-use
the .ninja and dependency file parsers?
> Btw, I don't quite follow why an "order-only dependency" is important.
> Do you mean you make all cpp files depend on generated header files,
> and expect it to do the correct thing by the semantics of "order-only
> dependency"? And you are using this method to avoid doing any
> consitent checking?
You don't *need* to use order-only dependencies, but it makes my life
easier: I add order-only dependencies on anything that might depend on
a generated header file. Then, only if it *actually* #includes the
header does it get rebuilt when the header is updated.
Evan
This should also include indirect dependencies. To give an example of
this scenario, consider an executable which uses a library. Some of
the executable's source files include the library's generated header
files, said generated header files being declared as order-only
dependencies of some of the library's object files. Since the
executable's source files include the generated header files, there
should be a dependency specified between the executable's object
files and the generated header files. But if the executable's object
files only declare an order-only dependency on the library, there
is no error, because the dependency is specified, albeit indirectly
(via the library and its object files). I have seen this in practice
with some CMake-based build systems.
Thanks,
--
Peter
However this problem also happens with linking: Whenever you #include something, you need to figure out what library you need to link, and manually add a dependency. You could probably make a build system that automatically figures out what libraries to link: take the errors generated by the linker, search for the appropriate symbols, and automatically add the correct linker dependencies.It's entirely possible to have rules which just generate .h files, and to have source code which includes those files. Leaving it to the developer to add an explicit dependency whenever they add #include leaves too much room for human error.
Doesn't this permit circular dependencies? What if in order to build "build/foo.pb.h" I need to build "protoc" using a "build protoc.o: cc protoc.cc" rule? At any rate: I believe that rules of this form could be implemented as a pre-processor that generates the appropriate .ninja files.
Given the line "build foo.o: cc foo.cc": on the first build, everything that starts with "build/" and ends with ".pb.h" would be brought up to date.
FYI, I'm a CMake developer and have dealt extensively with all kinds of
dependency problems. The problem discussed in this thread touches on
one of the reasons CMake generates what appear to be recursive
Makefiles. I just added an entry to our FAQ with a justification:
http://www.cmake.org/Wiki/CMake_FAQ#Why_does_CMake_generate_recursive_Makefiles.3F
On 9/26/2011 12:15 PM, Evan Jones wrote:
> The way that I was thinking about fixing this: when something
> depends on a rule that generates a .h, I need to "propagate"
> that .h dependency to any compile rules that depend on it. As a
> quick example, defined in a pseudo-ninja format:
>
> # Generate the .cc and .h; compile the .cc into a library.
> build libmessage: pb_library message.proto
>
> # Generate a library that depends on message_protocolbuffer
> build libsomething: library message_protocolbuffer libsomething.cc other_file.cc
>
> # Create an EXE using libsomething
> build someexe: executable libsomething executable.cc some_other_file.cc
>
>
> In this case, what I *think* should happen is that the compile
> rules generated by the "executable someexe" rule should include
> explicit dependencies on the generated header file. In other
> words, the compile executable.cc rule will depend on
> message.pb.h. Note: It is important that this is an "order-only
> dependency."
I don't think you need to explicitly propagate the dependency on
the generated header to other targets. Your point about order-only
dependencies is the key. All you need to do is ensure that the rules
for libmessage and libsomething have been fully evaluated and are
up to date before you even start to evaluate the rules for someexe.
By the time ninja even considers dependency generation for someexe
the header generated as part of building libmessage should exist.
You don't need any special handling in someexe for it.
CMake is able to handle cases when the rules for generating a header
or source file are specified in the same target in which they are
included or compiled. The reason is that we evaluate the generation
rules before we do dependency scanning. Ninja should be able to do
this without multiple levels of new processes because it can just
load the new dependencies that scanning produces directly into the
running process (since it is not make, after all).
-Brad
I agree. Unfortunately, the CMake language does not permit declaring
a library dependency from a target (e.g., an executable or shared
library) without also (implicitly) declaring an order-only dependency
from that target's object files to the library, and a number of
projects are now relying on this behaviour. I believe that one reason
for the implicit order-only dependency is that the more accurate
dependency graph cannot be easily represented using recursive Make,
which CMake must remain compatible with.
One idea is to teach the CMake Ninja generator to emit order-only
dependencies on libraries as order-only dependencies on the header
files generated for the library. But I haven't completely thought this
through, and there is the possibility that it might break some projects.
> Anyway, if/when we get this checking into ninja, we will have to make
> it controllable by a command line option, or make it a ninja tool
> instead.
Right, I think it should certainly be a tool. It would certainly be a
very valuable tool for CMake users. I have already used Ninja to (by
chance) find a couple of dependency bugs in another project I work on.
Thanks,
--
Peter
I don't understand what you mean by declaring dependencies on the rules.
CMake does this in part to handle the case that the library's build
rules generate files that may then be used by the executable's sources
during their compilation. We also have to make the behavior consistent
across all the target build environments, and this is how Xcode and the
VS IDE build tools work.
-Brad
The way header generation works in gyp is as follows:
gyp has a notion of a "target", which is a named entity that other
targets can depend on. The most obvious kind of target is a static
library. If executable target A depends on library B which depends on
library C, gyp implicitly translates that into the appropriate build
instructions (where library B and C can be built in parallel, and both
are both linked into executable A).
If a given target generates headers (aside from building libraries,
targets may also run commands that generate additional files) it must
be marked specially in gyp as a "hard dependency": that indicates that
any other target that depends on the hard dependency target has an
order-only dependency on it.
Translating the above rules into ninja rules is pretty simple, which
maybe explains why the ninja rules behave as they do.
Evan Jones's case of protocol buffers is the most common example of
this in Chrome as well.
At the gyp level, say you have
- target 'message_pb' which runs the generator to generate headers
- target 'libmessage' which builds libmessage.a with the generated source
- target 'foobar' which makes use of the above library
.
Concretely you build it as something like:
# generate the relevant headers
build message.cc message.h: pb_library message.proto
# build the library
build message.o: ...
build libmessage.a: ...
# build something that uses the library
build foobar.o: foobar.cc || message.h
# two options for linking
# 1) if you just put libmessage.a on the link line, explicit dep
build foobar: link foobar.o libmessage.a
# or 2) if you have a more complicated link line, implicit dep
build foobar: link foobar.o | libmessage.a
extra_link_flags = -lmessage
Those rules are sufficient to make builds both correct and minimal.
Unfortunately, generating them all is difficult if you're not
generating your ninja files, as you need to write down *somewhere*
that foobar.cc depends on message.h.
I'm reluctant to get too much of how headers work in C as ninja
builtins as my hope is that ninja remains small (it already feels a
bit large to me). With that said, I'm always interested in ideas
about general rules that can work for many people.
I'll take Dmitry's suggestion and extend the manual to better describe
how this is intended to work.
At the moment, we have only a few special cases where cpp files depend
on generated header files. So we maintain them in the script that
generate ninja files. In order to check that all such dependencies are
expressed in ninja files, I've made a change to ninja to check, when
loading depfiles for a target, that all dependencies discovered from
the dep file is already known to ninja. If a violation if found, ninja
will fail with a fatal message.
If you are interested, I'd happy to send the patch to you. I believe
it is general useful for cases where cpp files (or any target files
that use depfile) may depend on generated files. It has some known
limitations that it is currently not controlled by a command line
option, and it does not handle the indirect dependencies that Peter C
has pointed out, it does not handle indirect dependencies yet.
Qingning