Boost logo

Boost-Build :

Subject: Re: [Boost-build] feature, properties, variants, and all the rest
From: Stefan Seefeld (stefan_at_[hidden])
Date: 2017-08-05 22:46:47

On 08/05/2017 05:14 PM, Steven Watanabe via Boost-build wrote:
> On 08/04/2017 10:09 AM, Stefan Seefeld via Boost-build wrote:
>> I have outlined the workflow of such a build with three different
>> snapshots of the dependency graph:
>> * one as it is encoded in the fabscript,
>> * one has faber would see it once it has completed parsing the fabscript
>> (and any other input, including command-line options), and
>> * one some time later, once the dependency graph related to the "C"
>> artefact has been expanded by the assembly.
>> This corresponds to my own (mental) model, as well as how it is
>> implemented in faber (some of which is still work in progress).
>> So now I would like to understand whether you see any design flaws (or
>> bugs) in this approach, and how that maps to b2's model.
> I think that trying to represent both C and C.exe in
> the same hierarchy is a bit tricky.
> - Anything that depends on C should ultimately see C.exe.

Yeah, good point.

> - Similarly, this
> alias sources : 1.cpp 2.cpp 3.cpp ;
> exe C : sources ;

Hmm, not sure. I think what you *want* to do is make "sources" literally
a reference to these three files. But an "alias" in b2 (and likewise in
faber) is a reference to a target / artefact, which can be used in the
dependency graph, but not to alias / reference any related features /

> is a bit harder to deal with if you have an
> intermediate `sources` that exists along side 1.cpp etc.
> In Boost.Build, sources is a main target, but it
> completely disappears at the virtual-target layer.

> (This is definitely solvable. It just makes things
> a bit more complicated.)

Yeah. As I'm using Python, I can of course simply define

  sources = ['1.cpp', '2.cpp', '3.cpp']

and then pass that (list) object to the 'rule' call

  C = binary('C', sources)

without 'sources' having to become an artefact. Likewise, I can imagine
the Jam language to allow these kind of references without 'sources'
having to become a 'target' (which the line

  alias sources : 1.cpp 2.cpp 3.cpp ;

seems to suggest.

>> In particular, I don't see any fundamental problems in letting almost
>> arbitrary features (or "usage requirements") be added in the process. Of
>> course, if incompatible values are combined, an error needs to be raised.
> This is somewhat different from usage-requirements in Boost.Build.
> In your example:
> config = try_compile(source, if_=define('HAS_FEATURE'))
> C = binary('C', '', features=config.use)
> what we want is to apply this define when building C.exe.
> We don't intend it to be applied to dependents of C.


> In addition, I don't know if you have something akin to
> propagated features, but if you do, then config checks
> that set such features *must* be evaluated before building
> dependencies and *may* force config checks to be evaluated
> multiple times with different feature sets. This makes
> them very different from normal dependencies.

Yeah. I haven't run into the need for "propagated" features yet. In
fact, I was wondering whether "propagated" really is an attribute of a
feature (which as far as I understand it, is a "class" of properties),
rather than a specific property. In other word, if I define a feature
such as "define", I may not want to associate a "propagate" attribute to
all instances of that feature, but rather specific ones. Anyhow, I
haven't encountered a need for those yet. (Can you describe a use-case
for "propagate" feature ?)
>> (Note that I found a simple way to run the config checks without
>> requiring synchronization: the outcome of an action is fed back into the
>> associated artefact, from which it is available when the next action is
>> performed. So the only call to "update_now" is the global one performed
>> to update the goals from the command line...)
> I think this is a good idea, but you'll need
> to consider very carefully what artifacts should
> be updated when a file is touched.

Well, I think we agree that the dependency graph needs to be a directed
acyclic graph. No cycles, so I don't really see how the "on-the-fly
update" complicates anything, rather than simplifies the overall logic.

>> PS: There are a few terminology changes: I use "artefact" where b2 uses
>> "target", and "assembly" where b2 uses "generators". Furthermore,
>> "feature sets" replace "property sets". I hope that doesn't distract
>> from the overall similarity of the models.
> Why does everyone feel the need to invent their own
> terminology? (I'm not that happy about "features" and
> "properties" either for what it's worth.) The term
> "target," at least, is used by good ol' make.

Haha. I hear you. The "feature" vs. "property" question was a bit
tricky, but I ended up just using "feature value" to avoid ambiguity,
rather than introducing a new term. (Besides, a property is a property
of something, i.e. is normally associated with some object (a target, I
suppose), but in b2 it's not necessarily bound to anything.)

I opted for "artefact" rather than "target" because "target" often is
associated with the target *platform*. And given that I named my project
"faber", I though it would be rather consistent to have "faber" make
"artefacts" :-)

I thought a long time about "generators", looking for something I found
more descriptive. (Generate what ?) What I ended up using is more in
line with make, which calls it "implicit rules", and so does faber. The
logic that combines implicit rules into an actual pipeline of rules is
what I call the faber assembly.

Anyhow, I know naming is hard, and I know this is where people have
strong opinions, as the naming is a direct link to how we mentally
represent things. But there you go, it's what these things are named. :-)


      ...ich hab' noch einen Koffer in Berlin...

Boost-Build list run by bdawes at, david.abrahams at, gregod at, cpdaniel at, john at