Hi Hans, let me get right through to the points not yet discussed elsewhere. On 30.11.2017 05:51, Hans Dembinski wrote:
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/...
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. (no special interpreter involved here, it's all standard Python)
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')
Ah, well, now that's a fundamental limitation of Python. In the line greet = library('greet.cpp') you get a variable ("greet") that holds a reference to a "library" object. Take note that that object has no notion of it being referred to by the name "greet", and thus it doesn't know what name it needs to assign to the compiled and linked library, unless I provide that explicitly. Thus hello = binary(['hello.cpp', greet]) likewise wouldn't know how to name the binary file, and the 'greet' library object still having no name, the compiler couldn't refer to it either. I was actually thinking of ways to merge the two (the name of the variable, and the intrinsic names of the library and binary artefacts) to avoid that perceived redundancy, but got in all kinds of additional complexities trying that. The main point being that I really want to take advantage of this being written in a well established language, and using its idioms. SCons is a very good (or bad, actually) example of what happens if you technically use an established language without paying tribute to its idioms and established practices. I don't want to repeat that error.
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.
Perhaps you should think this through for a while longer (the way I have thought about it for months :-) ). I believe you will come to a very similar conclusion as I have.
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.
That's actually a good point: I can change the definition of these functions (`rule`, artefact constructors, etc.) to explicitly forbid positional arguments for anything but the required arguments. I agree that would prevent certain errors. Thanks for the tip !
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.
OK, great to hear. I'm taking it in a constructive way. Stefan -- ...ich hab' noch einen Koffer in Berlin...