Expose a way for distributors to override default directories

This allows placing a `distro_directories.py` file in the meson
module which exports a `default_directories` dict to override
all of the defaults similar to config.site in autoconf.

Fixes #2557
diff --git a/docs/markdown/Distributors.md b/docs/markdown/Distributors.md
new file mode 100644
index 0000000..2966abe
--- /dev/null
+++ b/docs/markdown/Distributors.md
@@ -0,0 +1,85 @@
+---
+title: Distributors
+short-description: Guide for distributors using meson
+...
+
+# Using Meson
+
+Distro packagers usually want total control on the build flags
+used. Meson supports this use case natively. The commands needed to
+build and install Meson projects are the following.
+
+```console
+$ cd /path/to/source/root
+$ CFLAGS=... CXXFLAGS=... LDFLAGS=.. meson --prefix /usr --buildtype=plain builddir
+$ ninja -v -C builddir
+$ ninja -C builddir test
+$ DESTDIR=/path/to/staging/root ninja -C builddir install
+```
+
+The command line switch `--buildtype=plain` tells Meson not to add its
+own flags to the command line. This gives the packager total control
+on used flags.
+
+This is very similar to other build systems. The only difference is
+that the `DESTDIR` variable is passed as an environment variable
+rather than as an argument to `ninja install`.
+
+As distro builds happen always from scratch, we recommend you to
+enable [unity builds](Unity-builds.md) whenever possible on your
+packages because they are faster and produce better code.
+
+## Overriding Default Directories
+
+Many distributions differ in their directory layout so as of Meson 0.45.0 we expose
+an easy method of overriding the default behavior. The reason you would want to
+override these is *not* for packages it is for users building software on the distro
+as they expect `meson` without any specified arguments to go into the right location.
+In build scripts for packages it should explicitly set any directories especially `prefix`.
+
+Inside the `mesonbuild` python module you can create a `distro_directories.py` file
+and `meson` will import the `default_directories` dictionary from that and
+override the internal defaults. As such it can do any arbitrary task such as
+checking the architecture at runtime.
+
+The list of these directories as well as their descriptions and defaults can be found
+in `meson --help` or alternatively in `default_directories` in `mesonlib.py`.
+
+Some simple examples:
+
+```python
+default_directories = {
+  'libdir': 'lib',  # Avoid the lib64 heuristics in meson
+  'libexecdir': 'lib',  # Some distros don't have a dedicated libexec
+}
+```
+
+```python
+import platform
+
+if platform.machine() in ('x86_64', 'aarch64'):
+    libdir = 'lib64'
+else:
+    libdir = 'lib'
+
+
+default_directories = {
+  'libdir': libdir,
+}
+```
+
+```python
+import subprocess
+
+pc = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'],
+                      stdout=subprocess.PIPE,
+                      stderr=subprocess.DEVNULL)
+(stdo, _) = pc.communicate()
+assert pc.returncode == 0
+archpath = stdo.decode().strip()
+libdir = 'lib/' + archpath
+
+default_directories = {
+  'libdir': libdir,
+}
+```
\ No newline at end of file
diff --git a/docs/markdown/Quick-guide.md b/docs/markdown/Quick-guide.md
index 938c3ed..0d46dc0 100644
--- a/docs/markdown/Quick-guide.md
+++ b/docs/markdown/Quick-guide.md
@@ -76,26 +76,4 @@
 Using Meson as a distro packager
 --
 
-Distro packagers usually want total control on the build flags
-used. Meson supports this use case natively. The commands needed to
-build and install Meson projects are the following.
-
-```console
-$ cd /path/to/source/root
-$ CFLAGS=... CXXFLAGS=... LDFLAGS=.. meson --prefix /usr --buildtype=plain builddir
-$ ninja -v -C builddir
-$ ninja -C builddir test
-$ DESTDIR=/path/to/staging/root ninja -C builddir install
-```
-
-The command line switch `--buildtype=plain` tells Meson not to add its
-own flags to the command line. This gives the packager total control
-on used flags.
-
-This is very similar to other build systems. The only difference is
-that the `DESTDIR` variable is passed as an environment variable
-rather than as an argument to `ninja install`.
-
-As distro builds happen always from scratch, we recommend you to
-enable [unity builds](Unity-builds.md) whenever possible on your
-packages because they are faster and produce better code.
+See [the distributors page](Distributors.md) for more information.
diff --git a/docs/markdown/Release-notes-for-0.45.0.md b/docs/markdown/Release-notes-for-0.45.0.md
index b3df71c..96a66c7 100644
--- a/docs/markdown/Release-notes-for-0.45.0.md
+++ b/docs/markdown/Release-notes-for-0.45.0.md
@@ -5,12 +5,7 @@
 
 # New features
 
-This page is a placeholder for the eventual release notes.
+## Added ability for distributors to easily override default directories
 
-Notable new features should come with release note updates. This is
-done by creating a file snippet called `snippets/featurename.md` and
-whose contents should look like this:
-
-    ## Feature name
-
-    A short description explaining the new feature and how it should be used.
+Distributors can now install a `distro_directories.py` file to override
+the Meson defaults. See [the distributors page](Distributors.md) for more details.
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 0fdac8b..44ea83f 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -19,7 +19,7 @@
 from pathlib import PurePath
 from collections import OrderedDict
 from .mesonlib import MesonException
-from .mesonlib import default_libdir, default_libexecdir, default_prefix
+from .mesonlib import default_directories
 import ast
 
 version = '0.45.0.dev1'
@@ -392,19 +392,19 @@
     'buildtype':  [UserComboOption, 'Build type to use.', ['plain', 'debug', 'debugoptimized', 'release', 'minsize'], 'debug'],
     'strip':      [UserBooleanOption, 'Strip targets on install.', False],
     'unity':      [UserComboOption, 'Unity build.', ['on', 'off', 'subprojects'], 'off'],
-    'prefix':     [UserStringOption, 'Installation prefix.', default_prefix()],
-    'libdir':     [UserStringOption, 'Library directory.', default_libdir()],
-    'libexecdir': [UserStringOption, 'Library executable directory.', default_libexecdir()],
-    'bindir':     [UserStringOption, 'Executable directory.', 'bin'],
-    'sbindir':    [UserStringOption, 'System executable directory.', 'sbin'],
-    'includedir': [UserStringOption, 'Header file directory.', 'include'],
-    'datadir':    [UserStringOption, 'Data file directory.', 'share'],
-    'mandir':     [UserStringOption, 'Manual page directory.', 'share/man'],
-    'infodir':    [UserStringOption, 'Info page directory.', 'share/info'],
-    'localedir':  [UserStringOption, 'Locale data directory.', 'share/locale'],
-    'sysconfdir':      [UserStringOption, 'Sysconf data directory.', 'etc'],
-    'localstatedir':   [UserStringOption, 'Localstate data directory.', 'var'],
-    'sharedstatedir':  [UserStringOption, 'Architecture-independent data directory.', 'com'],
+    'prefix':     [UserStringOption, 'Installation prefix.', default_directories['prefix']],
+    'libdir':     [UserStringOption, 'Library directory.', default_directories['libdir']],
+    'libexecdir': [UserStringOption, 'Library executable directory.', default_directories['libexecdir']],
+    'bindir':     [UserStringOption, 'Executable directory.', default_directories['bindir']],
+    'sbindir':    [UserStringOption, 'System executable directory.', default_directories['sbindir']],
+    'includedir': [UserStringOption, 'Header file directory.', default_directories['includedir']],
+    'datadir':    [UserStringOption, 'Data file directory.', default_directories['datadir']],
+    'mandir':     [UserStringOption, 'Manual page directory.', default_directories['mandir']],
+    'infodir':    [UserStringOption, 'Info page directory.', default_directories['infodir']],
+    'localedir':  [UserStringOption, 'Locale data directory.', default_directories['localedir']],
+    'sysconfdir':      [UserStringOption, 'Sysconf data directory.', default_directories['sysconfdir']],
+    'localstatedir':   [UserStringOption, 'Localstate data directory.', default_directories['localstatedir']],
+    'sharedstatedir':  [UserStringOption, 'Architecture-independent data directory.', default_directories['sharedstatedir']],
     'werror':          [UserBooleanOption, 'Treat warnings as errors.', False],
     'warning_level':   [UserComboOption, 'Compiler warning level to use.', ['1', '2', '3'], '1'],
     'layout':          [UserComboOption, 'Build directory layout.', ['mirror', 'flat'], 'mirror'],
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 9614f1f..05adf52 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -390,7 +390,7 @@
         if self.libdir:
             libdirs = [self.libdir]
         elif self.boost_root is None:
-            libdirs = mesonlib.get_library_dirs()
+            libdirs = mesonlib.default_directories['librarydirs']
         else:
             libdirs = [os.path.join(self.boost_root, 'lib')]
         for libdir in libdirs:
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 4871bf7..9d71c7e 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -449,7 +449,8 @@
             found.append(req)
     return not_found == [], not_found, found
 
-def default_libdir():
+
+def _default_libdir():
     if is_debianlike():
         try:
             pc = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'],
@@ -465,14 +466,8 @@
         return 'lib64'
     return 'lib'
 
-def default_libexecdir():
-    # There is no way to auto-detect this, so it must be set at build time
-    return 'libexec'
 
-def default_prefix():
-    return 'c:/' if is_windows() else '/usr/local'
-
-def get_library_dirs():
+def _default_library_dirs():
     if is_windows():
         return ['C:/mingw/lib'] # Fixme
     if is_osx():
@@ -500,6 +495,33 @@
     return unixdirs
 
 
+# These are mostly documented in builtin_options in coredata.py
+default_directories = {
+    'prefix': 'c:/' if is_windows() else '/usr/local',
+    'bindir': 'bin',
+    'sbindir': 'sbin',
+    'libdir': _default_libdir(),
+    'libexecdir': 'libexec',
+    'datadir': 'share',
+    'includedir': 'include',
+    'mandir': 'share/man',
+    'infodir': 'share/info',
+    'localedir': 'share/locale',
+    'sysconfdir': 'etc',
+    'localstatedir': 'var',
+    'sharedstatedir': 'com',
+    'librarydirs': _default_library_dirs(),
+}
+
+
+try:
+    # Allow distros to insert a file that sets its own directories
+    from .distro_directories import default_directories as overrides
+    default_directories.update(overrides)
+except ImportError:
+    pass
+
+
 def do_replacement(regex, line, confdata):
     missing_variables = set()