On Sat, 2024-01-20 at 22:18 -0500, Dmitry Goncharov wrote: > > Either we could follow the example of "+=" and say that the assignment > > type in "+:=" only takes effect if the variable doesn't already have a > > type but if it does that type is preserved, so in the above example FOO > > would continue to be recursively expanded in spite of "+:=", so FOO has > > a value of "$(bar) $(biz)" and $(FOO) gives "1 2". > > > > Or we could say that "+=" is a special case where we follow the type of > > the existing variable, but for the other operators we first expand the > > right hand side then append the result regardless of the original type. > > So in the above example FOO is still a recursive variable, BUT $(baz) > > is expanded immediately before appending so the value of FOO would be > > "$(bar) yy" and $(FOO) gives "1 yy". > > > > Neither of these is great, in that they both will cause people to be > > surprised, in different situations. > > i like option one. It is simple to explain in the manual and, as far > as i can tell, option one does what users want. > As long as the manual says something like "when there is no variable > +:= creates a simple variable, otherwise +:= behaves the same as :=" > is there still room for surprises?
I think you wrote that wrong: I think you mean "otherwise +:= behaves the same as +="... ? I think the behavior here would be very surprising in a number of situations as I'll try to show below. > i suspect option two is a lot trickier to use in a makefile. Option > two introduces a doubt in the mind of a makefile author. If the > makefile author knows that all the appends +=, :+=, ::+=+, :::+= Just for clarity, it's "+=", "+:=", "+::=", "+:::=", and "+!="; the plus is the first character in the token, not right before the "=". > follow the variable flavor, once the variable is created, then the > author sees that a variable is recursive at the top of a makefile and > knows anything can be appended to the variable at the bottom of the > makefile and the value will contain that anything at build time. If > we go with option two then the author does not have this knowledge. > In your example, one scenario is that the author misses that the > assignment is +:= and sets biz to 2 and expects that FOO contains "1 > 2" at build time. Another scenario is that 'FOO +:= $(biz)' is > introduced long after 'biz = 2'. Similarly, if the author wants to > introduce a +:= assignment somewhere in a makefile, they have to > check that the makefile does not contain another assignment later. In > our example, if the author wanted to introduce FOO +:= $(biz) they > will have to check that biz is not assigned later. I don't think I agree with you here. Let's take one obviously bad case: the != operator. I don't see any legitimate way that this can be used if we choose option one. Suppose we have: $(X)foo != echo hello foo +:= $(BAR) Now what do we do? We clearly can't say that the RHS of the append assignment should be treated as a shell script! It can't work to say that the value to be appended _might_ be appended using simple expansion if X has a value, _or else_ be run as a shell script and append the result if X is empty. And similarly for the other way around: it won't work if the value was originally assigned with ":=" and then you use "+!=" and instead of expanding the shell command it simply expands and appends the value. The idea behind option two is that you don't need to know what other settings of the variable had previously been created. The only thing you care about is how you want to handle THIS assignment; that is, you expand (or not) the RHS of THIS assignment according to the operator, then append that value. So for example if you see "+!=" you know that the RHS of this assignment will definitely be expanded as a shell script, then the results will be appended to the variable. The question is what happens in this scenario: foo = $(bar) foo +:= $(baz) What we would like to see is that the RHS of the "+:=" is expanded immediately (if the user didn't want it to be expanded immediately then they'd obviously not use "+:=") then appended. But this is kind of gross because we're appending an already-expanded value to a recursive variable, which means that already-expanded value will be expanded AGAIN when the variable is expanded which is not what you want. Similarly, what if we have: foo := $(bar) foo +:::= $(baz) Here the foo variable is marked simple, but we are appending an escaped value to it which won't be expanded later. Maybe we need to consider escaping/expanding values properly for the variable they are to be assigned to, after we expand them based on the current operator. I will try to write down what I'm thinking in a separate email, maybe as a table. The need for such a table of course is depressing because it means we can't just have a simple rule. But I just don't think that a simple rule can work given all the competing requirements. I guess the only "truly correct" behavior would be to keep the actual value of the variable as a list of separate chunks of text each with its own type when append is used, and resolve each chunk according to its particular operator. But I definitely don't want to do that so maybe we can do something mostly equivalent. To be honest things would be simpler if the current behavior of the := operator didn't exist at all, and GNU Make had implemented it as the behavior of the ":::=" operator instead. Then all variables would be "recursive" and we'd handle internal "$" by escaping them. It would make variable expansion slower than the current ":=" but I doubt it would be noticeable. If only Joerg were still with us, he'd be so happy to read that :) -- Paul D. Smith <psm...@gnu.org> Find some GNU make tips at: https://www.gnu.org http://make.mad-scientist.net "Please remain calm...I may be mad, but I am a professional." --Mad Scientist