Meson: Differentiate global variables from local variables

Created on 11 Nov 2017  Â·  17Comments  Â·  Source: mesonbuild/meson

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.

enhancement

Most helpful comment

Meson's build file language is not a programming language

There is:

  • variable assignment
  • function/method calls
  • logical operators
  • conditions
  • loops
  • executing external commands
  • …

Code is code. It is important to write clean and maintainable code. It is no different for the build system.

All 17 comments

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 #2606 ).

OK so since this is not a bug, it makes the whole thing much more fragile.

I've adopted this convention now:

  • Local variables in lower_case.
  • Global variables in UPPER_CASE.

support local keyword before local variable maybe a good choice

do this is a good idea, but it will broken lots of existed meson projects.

Local variables in lower_case.
Global variables in UPPER_CASE.

To not break the Meson API, there could also be a new setting in the project() command to enable the feature. And this could become the default for Meson version 2.

That way the feature can be implemented the other way around: local variables by default, and for a variable to be global, a keyword must be added, like "global" or "export". I find it more natural like this.

There is no such thing as 'global' or 'local' scope in Meson since the concept of scopes does not exist, and does not make sense since there are no functions. The ability to place build files in subdirs is provided for ease of use, and it is essential for variables to be accessible between build files in different subdirs. For example, you create a library in one directory and use it in your tools, tests, examples, and other library directories.

However, it is a real problem that people can clobber variables without meaning to. I think we can add a qualifier like const that causes an error when variables are reassigned. Alternatively, we can warn by default on clobbers and have a qualifier like var that suppresses it.

I like the const way though because it makes it very explicit that some variables are special and that their value will be reused elsewhere, say via a subproject or in other subdirs.

In Object-Oriented Programming, there is this concept called encapsulation, also known as information hiding. A code following that practice is easier to reason about, because it is possible to understand a piece of code without the need to understand all the rest. A class exports a public interface and hides its implementation details, so when using that class in another part of the codebase, we can just look at its public interface to know how to use it and what features it provides.

For a meson.build file, its public interface would just be its list of global variables, i.e. the variables that are meant to be used in other meson.build files.

If you have read any sort of programming best-practices guide, you should know that global variables should be avoided if possible. In Meson, all variables are global… Don't you see anything wrong with that? In order to understand a meson.build file, you must potentially look at all the other meson.build files to know where is the relevant variable assignment (with mutable variables it makes things worse since a variable can be re-assigned in any other meson.build file). So it makes things harder to reason about.

Even if the type system doesn't enforce it, it is useful to distinguish global variables from local variables when reading the code. With my above convention with global variables in UPPER_CASE, I directly know if a variable is meant to be used in other meson.build files. In C, you can look at a *.h file to know what the "class" exports in its public interface. With Autotools the "global variables" are exported explicitly with the AC_SUBST macro. Currently in most projects using Meson as its build system, when reading a meson.build file there is no way to know the list of variables that are meant to be used in other meson.build files, i.e. its public interface.

Something that would be useful is preventing a variable from being re-assigned in another meson.build file. So a variable would be defined in a certain meson.build file, and all other meson.build files could use that variable, but not assign it a different value. A little the same as AC_SUBST.

Meson's build file language is not a programming language, and build files in general have totally different use-cases compared to programming languages. It is more useful to look at specific use-cases than trying to reason by analogy.

Sometimes it can indeed be difficult to figure out where a variable is defined if it's used across subdir(), but the fact that it works like that is also a useful feature that is used by all non-trivial build files that I have seen.

A good rule of thumb is to define the variable in the parent build file of whichever build files use it, unless it points to a target or something else that is entirely contained within a subdir. In general, I think this problem is better solved via your IDE.

I also don't agree with the Autotools comparison at all because AC_SUBST is about autoconf substituting variables in .in files, so the equivalent in Meson is actually configure_file() + configuration_data().

Something that would be useful is preventing a variable from being re-assigned in another meson.build file

I agree, that is a user error that we can easily design to avoid, which is why I suggested const.

When this was originally designed there was a very difficult balancing act between ease of use and security. The current approach was chosen due to objects being immutable and most projects needing to have variables visible. The biggest isolator is subprojects, where variables are fully separated and can only be accessed in limited ways and explicitly.

Like most things in life this is not a perfect solution but a compromise between many different competing requirements.

One workaround would be to prefix variables that you wish were local with the subdir they are in. It looks like the glib meson build system sometimes does this.

With all variables being global and objects being immutable, there will be a proliferation in variable names. Perhaps if there were a switch to get warnings about clobbering, especially across subdirs?

I am personally not a fan of using all caps names, that could quickly make meson build files look like CMake.

Meson's build file language is not a programming language

There is:

  • variable assignment
  • function/method calls
  • logical operators
  • conditions
  • loops
  • executing external commands
  • …

Code is code. It is important to write clean and maintainable code. It is no different for the build system.

@jibsen I think that is just best practice in general and mostly avoids any accidental conflicts.

that could quickly make meson build files look like CMake.

It's not that easy. CMake is 18 years effort. :)

Seriously, does Python code look like CMake? They also have a convention about all caps global variable names. Separating local and global variables with a different naming scheme is a good thing. It makes the code more maintainable/readable. And this could be just a convention. That's my opinion.

So, what's the decision for this ticket? I'm curious because I'm considering meson for using in a big proprietary project and this issue is important to me.

I too think local variables are crucial for any bigger project.

FWIW I would prefer the restrictions of something like https://github.com/dhall-lang/dhall-lang rather than no functions/scope to keep the language decidable. Some day...

Literally everyone:

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).

Meson authors:

God forbid! If we allow this, people will start using it to circumvent Meson's opinionatedness!

Not only I support the idea of having local variables but I suggest that local variables should have been the default from the beginning, with a global qualifier to make them global. CMake devs understood correctly the importance of scoping, but their final design is also not fantastic:

  • CMake regular variables are never global, they are always local to the current scope but you can access parent scope with set(var value PARENT_SCOPE), possibly creating an encapsulation violation;
  • You can define global properties, which are something different from regular variables and harder to access.

Meson scoping is only restricted to dependencies and/or subprojects. Within the project you can clutter the namespace and introduce encapsulation problems (eg. reassigning variables with wrong content). The minimum fix that keeps compatibility would be introduce scopes, dictated by directories or by other means, and a local qualifier for variables. A radical breaking change that makes all variables local by default and allow to qualify them with global would also be appreciated on my side.

local variables should have been the default from the beginning, with a global qualifier to make them global.

Unfortunately, we do not have a time machine...

A radical breaking change that makes all variables local by default and allow to qualify them with global would also be appreciated on my side.

This won't happen since this would break almost all projects that use multiple meson.build files.

However, here are some of my ideas that could work (without breaking backward compatibility):

  • add const (should be fairly easy)
  • warn when variables are reassigned with a different type
  • add scoping

    • local and global modifiers

    • the default is global for all variables (may be changed to local with a project() parameter)

    • optionally: explicit scopes with someting like block, endblock

  • add const (should be fairly easy)
  • warn when variables are reassigned with a different type
  • add scoping

    • local and global modifiers
    • the default is global for all variables (may be changed to local with a project() parameter)
    • optionally: explicit scopes with someting like block, endblock

All of this would be fantastic (block, endblock is actually a feature that I wanted to suggest also to CMake, as a complement to named function). I also thought the same interventions but I didn't say to this extent because it's fairly too easy only suggesting when there is lot of work involved. You didn't mention implicit directories scoping by I think you also meant that.

Was this page helpful?
0 / 5 - 0 ratings