Boost logo

Boost :

Subject: Re: [boost] Announcement: Faber, a new build system based on bjam
From: Hans Dembinski (hans.dembinski_at_[hidden])
Date: 2017-11-30 10:51:41


> On 29. Nov 2017, at 14:31, Stefan Seefeld <stefan_at_[hidden]> wrote:
>
> On 29.11.2017 07:59, Hans Dembinski wrote:
>>
>>> from faber.artefacts.binary import binary
>>>
>>> greet = module('greet')
>>>
>>> hello = binary('hello', ['hello.cpp', greet.greet])
>>>
>>> rule(action('test', '$(>)'), 'test', hello, attrs=notfile|always)
>>>
>>> default = hello
>>>
>>> (from
>>> https://github.com/stefanseefeld/faber/blob/develop/examples/modular/fabscript <https://github.com/stefanseefeld/faber/blob/develop/examples/modular/fabscript>),
>>> which uses higher-level artefacts (`binary`, `library`) and doesn't
>>> require the user to define his own actions to build.
>>
>> This example remains cryptic.
>>
>> from faber.artefacts...: artefacts? The term "artefact" is very general and non-descriptive. The first definition provided by Google is essentially "human-made thing".
>
> Right, it's what "faber" generates (using the same stem even).

:) Fair enough, but it is still not very descriptive. Why use an uncommon latin word if you could use a common word from day-to-day language? The purpose of language is to transmit information, so it is usually a good idea to use common words that leave no room for ambiguity.

Ironically, the other meaning of "artefact" is "any error in the *perception or representation of any information*, introduced by the involved equipment or technique(s)" [Wikipedia]

>> Then, I have to type many redundant things here. Note, the many occurrences of greet in these two lines
>>
>> greet = module('greet')
>> hello = binary('hello', ['hello.cpp', greet.greet])
>>
>> It seems like hello is a binary which depends on 'hello.cpp' and the module greet. Why the latter?
>
> "hello" is a binary built from a "hello.cpp" source file and a "greet" library provided from another ("greet") module (thus using Pythonic syntax, we refer to the latter as "greet.greet").
> If the library would have been built by the same module, the above would simply be
>
> greet = library('greet', 'greet.cpp')
> hello = binary('hello', ['hello.cpp', greet])
>
> as is in fact done in this example: https://github.com/stefanseefeld/faber/blob/develop/examples/implicit_rules/fabscript <https://github.com/stefanseefeld/faber/blob/develop/examples/implicit_rules/fabscript>

I think source code is allowed to be verbose, but it should not be redundant, especially if said redundancy could lead to mistakes. I suppose you run the fabscript through a special interpreter, not just the standard Python interpreter. If so, then you can use this shorthand syntax instead:

greet = library('greet.cpp')

That way, one cannot make a mistake like this

greet = library('great', 'greet.cpp')

To make the syntax very consistent (the Zen of Python says: "There should be one - preferably only one - obvious way to do it."), you could define all build items like library and binary in this way:

def binary(*inputs, attribute1=default1, attribute2=default2, …): ...

All positional arguments would always be inputs of any kind, like a source file or a library. If you always use positional arguments consistently like this, then my complaint about ambiguity is gone, because there is a clear rule which is easy to remember.

Attributes would be passed consistently via keywords. They have reasonable defaults that Faber picks for me. Like, if I want another file extension for a library than the default for the platform. For libraries, I could specify whether to build a static or shared one. Or if I really don't want to name the library "greet", I could pass the keyword name="great".

This declaration enforces the use of keywords for attributes, positional arguments are not allowed for attributes, which is a good for clarity.

>> The rule to make a test is very cryptic. The action takes several positional arguments, and I can only guess what each positional argument does.
>
> rules take at least two (positional) arguments (an action and a name for the target artefact). All other arguments have default values, and thus *may* be given as keyword arguments or as positional arguments, depending on your preference. (But given that a "source" argument is still very common, I just chose to not spell it out as "source=hello" for compactness.) As a fabscript author you are of course free to name all your rule arguments, if that helps readability. I not inventing anything here, but rather take the most natural approach possible following Python language rules and idioms.
>
>> I am also critical about this in bjam. By using a syntax that uses a lot of positional arguments, you need to read the documentation to figure out what is going on.
>
> Again, Python allows you to name all arguments. This is up to the caller, not the API designer. As far as the API is concerned, rules have two mandatory arguments, so it wouldn't make sense to make them keyword arguments.

I hope I explained better above what I had in mind. I agree, of course, that writing things like source="bla" all the time is annoying and superfluous.

> But if you prefer some help in drafting your fabscript logic, there are good tools to help interactively editing Python code, including code completion etc.
> That's the beauty of using Python: we can tap into a fabulous ecosystem of tools and modules, including ipython, jupyter, spyder, etc., etc.

Agreed, that's why I am also in favour of using an established scripting language to describe a build tree. I am sorry that I am so critical, but we have some common ground. All this is meant in a constructive way.

Best regards,
Hans


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk