Boost logo

Boost-Build :

Subject: Re: [Boost-build] feature, properties, variants, and all the rest
From: Stefan Seefeld (stefan_at_[hidden])
Date: 2017-08-06 02:56:57


On 08/05/2017 07:44 PM, Steven Watanabe via Boost-build wrote:
> AMDG
>
> On 08/05/2017 04:46 PM, Stefan Seefeld via Boost-build wrote:
>> 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 /
>> properties.
>>
> An alias in Boost.Build can have both usage-requirements
> and requirements.
>
>>> 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.
>> OK.
>>> (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.
>>
> Of course, you can do it the same way in Jam:
> local sources = 1.cpp 2.cpp 3.cpp ;
> The reason for using a target is polymorphism.
> The person using it doesn't have to care whether
> it's a list of sources, some object files, a library,
> or whatever. Also, this is only the simplest
> use of alias. For example, choose sources based
> on properties:
> alias sources : 1.cpp : <variant>debug ;
> alias sources : 2.cpp : <variant>release ;
> Create a standardized name for a target:
> run test.cpp /boost//unit_test_framework ;
> # /boost//unit_test_framework is actually an
> # alias for path/to/boost/libs/test/build//unit_test_framework
> A header only library:
> alias mylib : : : <include>./include ;

Yeah, I can see this being powerful. But arguably, having a statically
typed object model (with introspection) is even more powerful. So I'm
not convinced that a user not making a difference between a list of
sources and a library is a good thing.
>> <snip>
>>> 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 ?)
> The need for propagated features stems from the fact that
> Boost.Build allows multiple build configurations to be
> handled simultaneously. They're basically used for anything
> that would be global in other build systems, such as the toolset,
> debug vs. release, static vs. shared, etc.

Ah, right. Well, right now I don't support multiple build
configurations. (You can clone artefacts and apply different sets of
features to them all you want, such as a static and a shared version of
a library. But still, both are represented as two distinct objects, and
I haven't encountered the need to propagate features across the entire
graph.)

>
>>>> (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.
>>
> Do you try to issue an error for cycles, or do
> you just hope for the best?

I don't know yet. (I'd definitely like to raise an error, but I'm not
sure how hard it is to detect them.

> Also, I don't know
> how you handle #include scanning, but the way Jam
> handles scanners causes cyclic #includes to create
> a cyclic dependency in the internal nodes, which
> is a total nightmare in the face of a dependency
> graph that changes dynamically.

I haven't encountered any issues yet. The include-scanning-step is its
own intermediate step that injects additional dependencies into the
graph, and as long as they lead to existing files (which themselves
don't need to be updated), I don't see issues. Where would that create
problems ?

Thanks,
        Stefan

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

Boost-Build list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk