mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.37k stars 1.54k forks source link

Differentiate global variables from local variables #2607

Open ghost opened 6 years ago

ghost commented 6 years ago

So with Meson all the variables are accessible in all meson.build files. For a big project, this can become a maintenance problem.

Normally the variables are immutable, so writing by mistake the same variable name in two different meson.build files should trigger an error (but this is currently not the case, see https://github.com/mesonbuild/meson/issues/2606 ).

It would be better to be able to differentiate local variables (those used in only one meson.build file) from global variables (those used in several meson.build files). It is possible to use a convention when naming those variables, but it would be better to have support from the type system.

dcbaker commented 9 months ago

@xclaesse off the top of my head are the files() objects from wayland-protocols

but that's also a bit tangential to this discussion anyway, I'm just trying to make the point that there are other valid uses for such modifiers beyond the proposed local, global, and final we have right now

jibsen commented 9 months ago

While I am a big fan of backwards compatibility, my immediate reaction is perhaps slight unease at the idea of adding a keyword that presently does nothing for the sake of a possible future change.

What are your thoughts on users who add kwargs to various function calls in their meson.build, such as required: true when it's already the default for dependency/find_library?

There is a school of thought that says "explicit is better than implicit". I've seen rather a lot of meson.build files which follow that. I wouldn't say it's a majority...

If being explicit helps to document the intent or reduce the risk of error, then I think it can be great. An example would be Python's keyword arguments if a function signature is not obvious.

But in other cases being explicit may be unidiomatic and can be more confusing than helpful. For instance if you declare a variable as signed int in C code.

In your specific example, if I was aware of the default, I would likely not add it myself.

What I was thinking was; if you add both local and global, the only reasonable choice is to explicitly add them to all variables, because if you have a variable without somewhere in your build, it might break silently in an unexpected way if the default changes.

Also, consider being new to Meson and seeing

foo = 1
local bar = 2

you may infer that foo is not local. Analogously if only global were present. But if you have

foo = 1
local bar = 2
global baz = 3

you are now most likely confused.

It is easy to add a keyword, but hard to remove it. I think it is good for developers to worry slightly about adding one, so they make sure to think it through.

dcbaker commented 9 months ago

I understand that having two ways to do the same thing is not ideal. I really do like the python mantra "one, and preferably only one, way to do a thing". I also spent a lot of time porting python2 code to python3, and saw what eventually worked well for them, providing builtin features to make programs work in both 2.x and 3.x.

Because of that, for me, thinking about breaking changes long before you actually pull the switch and break them is super important, and in this case providing a mechanism to straddle the gap while projects reasonably expect to run on both meson 1.x and 2.x without having to have giant blocks of:

is_1x = meson.version().version_compare('< 2')

if is_1x
  x = executable(...)
else
  global x = executable(...)
endif

vs

global x = executable(...)

Is worth the tiny amount of confusion that having x = 'foo' and global x = 'foo' mean the same thing may cause.

jibsen commented 9 months ago

@dcbaker that is a very good argument indeed.

eli-schwartz commented 9 months ago

... and to circle back around to kwargs with default values, sometimes we want to change the default value of a function. See for example the check kwarg to run_command.

eli-schwartz commented 9 months ago

off the top of my head are the files() objects from wayland-protocols

@dcbaker the idiomatic way to do this is by setting a directory as a variable in declare_dependency, just as @xclaesse said, which allows using the same lookup process for wayland-protocols as a subproject or an installed OS component, and looking for files relative to the dependency object's gotten variable.

... and this works because I made it work. Referencing files as children of a directory saved in a declare_dependency() variable, works as long as you don't try to escape the exported location. It is an explicit sandbox exception, specifically to be interchangeable with "protocolsdir=/usr/share/foobar-protocols" style stuff.

lonble commented 5 months ago

So in 2024 we still can’t get "local"?

nils-werner commented 1 month ago

I'd like to propose another idea that allows for more fine-grained control and a clean migration-path: A keyword export that exposes a variable from a subdir to the direct parent subdir (and no furher). If a variable from a child-subdir is supposed to be exported beyond its parent, the parent has to explicitly re-export that variable, too. That way you get a somewhat fine-grained control over the visibility of your variables inside your modules, and not just a simple global/local switch.

For backwards-compatibility, if there is no export anywhere in the current meson.build, all variables are implicitly exported, just like it is done now.

Because of the "export everything"-fallback, people can incrementally start adding export keywords to their subdirs one after one (bottom-up or top-down doesn't matter, both are fine), without having to think about the entire scope of the entire project, and without having to change all their meson.build files at once.