We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ccache cannot work on dist builds
Due to how the dist build is implemented in Meson, it will always result in ccache being unable to work.

The reason for this is the forced use of `mkdtemp` for directories within the `check_dist` function (in `dist.py`). The source is unpacked into `$tmpdir/tmp$string` which is random and thus unable to produce cache hits for subsequent builds. If I am trying to do e.g., a CI dist build, this is not ideal since it makes the build very long.

There are two possible solutions I've come up with which could solve the issue:
* Add a `DISTBUILDDIR` (or similar) environment variable which is used instead of `mkdtemp` during a dist build; this directory would take the place of the `mkdtemp` directories and would not be deleted when the build stopped
* Add a `TMPBUILDSTRING` (or similar) environment variable which is used in place of the random part of the tempdir string (resulting in e.g., /tmp/tmp$string); this directory can still be created and deleted normally, or even deleted if it already exists since we can assume this should only be used for meson dist builds

Other solutions would be great too, as long as ccache becomes usable.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mesonbuild/scripts/dist.py`
Content:
```
1 # Copyright 2017 The Meson development team
2 
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 
16 import lzma
17 import os
18 import sys
19 import shutil
20 import subprocess
21 import pickle
22 import hashlib
23 import tarfile, zipfile
24 import tempfile
25 from glob import glob
26 from mesonbuild.environment import detect_ninja
27 from mesonbuild.mesonlib import windows_proof_rmtree
28 from mesonbuild import mlog
29 
30 def create_hash(fname):
31     hashname = fname + '.sha256sum'
32     m = hashlib.sha256()
33     m.update(open(fname, 'rb').read())
34     with open(hashname, 'w') as f:
35         f.write('%s %s\n' % (m.hexdigest(), os.path.basename(fname)))
36 
37 
38 def create_zip(zipfilename, packaging_dir):
39     prefix = os.path.dirname(packaging_dir)
40     removelen = len(prefix) + 1
41     with zipfile.ZipFile(zipfilename,
42                          'w',
43                          compression=zipfile.ZIP_DEFLATED,
44                          allowZip64=True) as zf:
45         zf.write(packaging_dir, packaging_dir[removelen:])
46         for root, dirs, files in os.walk(packaging_dir):
47             for d in dirs:
48                 dname = os.path.join(root, d)
49                 zf.write(dname, dname[removelen:])
50             for f in files:
51                 fname = os.path.join(root, f)
52                 zf.write(fname, fname[removelen:])
53 
54 def del_gitfiles(dirname):
55     for f in glob(os.path.join(dirname, '.git*')):
56         if os.path.isdir(f) and not os.path.islink(f):
57             windows_proof_rmtree(f)
58         else:
59             os.unlink(f)
60 
61 def process_submodules(dirname):
62     module_file = os.path.join(dirname, '.gitmodules')
63     if not os.path.exists(module_file):
64         return
65     subprocess.check_call(['git', 'submodule', 'update', '--init', '--recursive'], cwd=dirname)
66     for line in open(module_file):
67         line = line.strip()
68         if '=' not in line:
69             continue
70         k, v = line.split('=', 1)
71         k = k.strip()
72         v = v.strip()
73         if k != 'path':
74             continue
75         del_gitfiles(os.path.join(dirname, v))
76 
77 
78 def run_dist_scripts(dist_root, dist_scripts):
79     assert(os.path.isabs(dist_root))
80     env = os.environ.copy()
81     env['MESON_DIST_ROOT'] = dist_root
82     for d in dist_scripts:
83         script = d['exe']
84         args = d['args']
85         name = ' '.join(script + args)
86         print('Running custom dist script {!r}'.format(name))
87         try:
88             rc = subprocess.call(script + args, env=env)
89             if rc != 0:
90                 sys.exit('Dist script errored out')
91         except OSError:
92             print('Failed to run dist script {!r}'.format(name))
93             sys.exit(1)
94 
95 
96 def git_have_dirty_index(src_root):
97     '''Check whether there are uncommitted changes in git'''
98     ret = subprocess.call(['git', '-C', src_root, 'diff-index', '--quiet', 'HEAD'])
99     return ret == 1
100 
101 def create_dist_git(dist_name, src_root, bld_root, dist_sub, dist_scripts):
102     if git_have_dirty_index(src_root):
103         mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')
104     distdir = os.path.join(dist_sub, dist_name)
105     if os.path.exists(distdir):
106         shutil.rmtree(distdir)
107     os.makedirs(distdir)
108     subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])
109     process_submodules(distdir)
110     del_gitfiles(distdir)
111     run_dist_scripts(distdir, dist_scripts)
112     xzname = distdir + '.tar.xz'
113     # Should use shutil but it got xz support only in 3.5.
114     with tarfile.open(xzname, 'w:xz') as tf:
115         tf.add(distdir, dist_name)
116     # Create only .tar.xz for now.
117     # zipname = distdir + '.zip'
118     # create_zip(zipname, distdir)
119     shutil.rmtree(distdir)
120     return (xzname, )
121 
122 
123 def hg_have_dirty_index(src_root):
124     '''Check whether there are uncommitted changes in hg'''
125     out = subprocess.check_output(['hg', '-R', src_root, 'summary'])
126     return b'commit: (clean)' not in out
127 
128 def create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):
129     if hg_have_dirty_index(src_root):
130         mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')
131 
132     os.makedirs(dist_sub, exist_ok=True)
133     tarname = os.path.join(dist_sub, dist_name + '.tar')
134     xzname = tarname + '.xz'
135     subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])
136     if dist_scripts:
137         mlog.warning('dist scripts are not supported in Mercurial projects')
138     with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:
139         shutil.copyfileobj(tf, xf)
140     os.unlink(tarname)
141     # Create only .tar.xz for now.
142     # zipname = os.path.join(dist_sub, dist_name + '.zip')
143     # subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])
144     return (xzname, )
145 
146 
147 def check_dist(packagename, meson_command):
148     print('Testing distribution package %s' % packagename)
149     unpackdir = tempfile.mkdtemp()
150     builddir = tempfile.mkdtemp()
151     installdir = tempfile.mkdtemp()
152     ninja_bin = detect_ninja()
153     try:
154         tf = tarfile.open(packagename)
155         tf.extractall(unpackdir)
156         srcdir = glob(os.path.join(unpackdir, '*'))[0]
157         if subprocess.call(meson_command + ['--backend=ninja', srcdir, builddir]) != 0:
158             print('Running Meson on distribution package failed')
159             return 1
160         if subprocess.call([ninja_bin], cwd=builddir) != 0:
161             print('Compiling the distribution package failed')
162             return 1
163         if subprocess.call([ninja_bin, 'test'], cwd=builddir) != 0:
164             print('Running unit tests on the distribution package failed')
165             return 1
166         myenv = os.environ.copy()
167         myenv['DESTDIR'] = installdir
168         if subprocess.call([ninja_bin, 'install'], cwd=builddir, env=myenv) != 0:
169             print('Installing the distribution package failed')
170             return 1
171     finally:
172         shutil.rmtree(unpackdir)
173         shutil.rmtree(builddir)
174         shutil.rmtree(installdir)
175     print('Distribution package %s tested' % packagename)
176     return 0
177 
178 def run(args):
179     src_root = args[0]
180     bld_root = args[1]
181     meson_command = args[2:]
182     priv_dir = os.path.join(bld_root, 'meson-private')
183     dist_sub = os.path.join(bld_root, 'meson-dist')
184 
185     buildfile = os.path.join(priv_dir, 'build.dat')
186 
187     build = pickle.load(open(buildfile, 'rb'))
188 
189     dist_name = build.project_name + '-' + build.project_version
190 
191     _git = os.path.join(src_root, '.git')
192     if os.path.isdir(_git) or os.path.isfile(_git):
193         names = create_dist_git(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)
194     elif os.path.isdir(os.path.join(src_root, '.hg')):
195         names = create_dist_hg(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)
196     else:
197         print('Dist currently only works with Git or Mercurial repos')
198         return 1
199     if names is None:
200         return 1
201     error_count = 0
202     for name in names:
203         rc = check_dist(name, meson_command) # Check only one.
204         if rc == 0:
205             create_hash(name)
206         error_count += rc
207     return 1 if error_count else 0
208
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py
--- a/mesonbuild/scripts/dist.py
+++ b/mesonbuild/scripts/dist.py
@@ -144,11 +144,15 @@
     return (xzname, )
 
 
-def check_dist(packagename, meson_command):
+def check_dist(packagename, meson_command, privdir):
     print('Testing distribution package %s' % packagename)
-    unpackdir = tempfile.mkdtemp()
-    builddir = tempfile.mkdtemp()
-    installdir = tempfile.mkdtemp()
+    unpackdir = os.path.join(privdir, 'dist-unpack')
+    builddir = os.path.join(privdir, 'dist-build')
+    installdir = os.path.join(privdir, 'dist-install')
+    for p in (unpackdir, builddir, installdir):
+        if os.path.exists(p):
+            shutil.rmtree(p)
+        os.mkdir(p)
     ninja_bin = detect_ninja()
     try:
         tf = tarfile.open(packagename)
@@ -200,7 +204,7 @@
         return 1
     error_count = 0
     for name in names:
-        rc = check_dist(name, meson_command) # Check only one.
+        rc = check_dist(name, meson_command, priv_dir) # Check only one.
         if rc == 0:
             create_hash(name)
         error_count += rc

{"golden_diff": "diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py\n--- a/mesonbuild/scripts/dist.py\n+++ b/mesonbuild/scripts/dist.py\n@@ -144,11 +144,15 @@\n     return (xzname, )\n \n \n-def check_dist(packagename, meson_command):\n+def check_dist(packagename, meson_command, privdir):\n     print('Testing distribution package %s' % packagename)\n-    unpackdir = tempfile.mkdtemp()\n-    builddir = tempfile.mkdtemp()\n-    installdir = tempfile.mkdtemp()\n+    unpackdir = os.path.join(privdir, 'dist-unpack')\n+    builddir = os.path.join(privdir, 'dist-build')\n+    installdir = os.path.join(privdir, 'dist-install')\n+    for p in (unpackdir, builddir, installdir):\n+        if os.path.exists(p):\n+            shutil.rmtree(p)\n+        os.mkdir(p)\n     ninja_bin = detect_ninja()\n     try:\n         tf = tarfile.open(packagename)\n@@ -200,7 +204,7 @@\n         return 1\n     error_count = 0\n     for name in names:\n-        rc = check_dist(name, meson_command) # Check only one.\n+        rc = check_dist(name, meson_command, priv_dir) # Check only one.\n         if rc == 0:\n             create_hash(name)\n         error_count += rc\n", "issue": "ccache cannot work on dist builds\nDue to how the dist build is implemented in Meson, it will always result in ccache being unable to work.\r\n\r\nThe reason for this is the forced use of `mkdtemp` for directories within the `check_dist` function (in `dist.py`). The source is unpacked into `$tmpdir/tmp$string` which is random and thus unable to produce cache hits for subsequent builds. If I am trying to do e.g., a CI dist build, this is not ideal since it makes the build very long.\r\n\r\nThere are two possible solutions I've come up with which could solve the issue:\r\n* Add a `DISTBUILDDIR` (or similar) environment variable which is used instead of `mkdtemp` during a dist build; this directory would take the place of the `mkdtemp` directories and would not be deleted when the build stopped\r\n* Add a `TMPBUILDSTRING` (or similar) environment variable which is used in place of the random part of the tempdir string (resulting in e.g., /tmp/tmp$string); this directory can still be created and deleted normally, or even deleted if it already exists since we can assume this should only be used for meson dist builds\r\n\r\nOther solutions would be great too, as long as ccache becomes usable.\n", "before_files": [{"content": "# Copyright 2017 The Meson development team\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport lzma\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport pickle\nimport hashlib\nimport tarfile, zipfile\nimport tempfile\nfrom glob import glob\nfrom mesonbuild.environment import detect_ninja\nfrom mesonbuild.mesonlib import windows_proof_rmtree\nfrom mesonbuild import mlog\n\ndef create_hash(fname):\n    hashname = fname + '.sha256sum'\n    m = hashlib.sha256()\n    m.update(open(fname, 'rb').read())\n    with open(hashname, 'w') as f:\n        f.write('%s %s\\n' % (m.hexdigest(), os.path.basename(fname)))\n\n\ndef create_zip(zipfilename, packaging_dir):\n    prefix = os.path.dirname(packaging_dir)\n    removelen = len(prefix) + 1\n    with zipfile.ZipFile(zipfilename,\n                         'w',\n                         compression=zipfile.ZIP_DEFLATED,\n                         allowZip64=True) as zf:\n        zf.write(packaging_dir, packaging_dir[removelen:])\n        for root, dirs, files in os.walk(packaging_dir):\n            for d in dirs:\n                dname = os.path.join(root, d)\n                zf.write(dname, dname[removelen:])\n            for f in files:\n                fname = os.path.join(root, f)\n                zf.write(fname, fname[removelen:])\n\ndef del_gitfiles(dirname):\n    for f in glob(os.path.join(dirname, '.git*')):\n        if os.path.isdir(f) and not os.path.islink(f):\n            windows_proof_rmtree(f)\n        else:\n            os.unlink(f)\n\ndef process_submodules(dirname):\n    module_file = os.path.join(dirname, '.gitmodules')\n    if not os.path.exists(module_file):\n        return\n    subprocess.check_call(['git', 'submodule', 'update', '--init', '--recursive'], cwd=dirname)\n    for line in open(module_file):\n        line = line.strip()\n        if '=' not in line:\n            continue\n        k, v = line.split('=', 1)\n        k = k.strip()\n        v = v.strip()\n        if k != 'path':\n            continue\n        del_gitfiles(os.path.join(dirname, v))\n\n\ndef run_dist_scripts(dist_root, dist_scripts):\n    assert(os.path.isabs(dist_root))\n    env = os.environ.copy()\n    env['MESON_DIST_ROOT'] = dist_root\n    for d in dist_scripts:\n        script = d['exe']\n        args = d['args']\n        name = ' '.join(script + args)\n        print('Running custom dist script {!r}'.format(name))\n        try:\n            rc = subprocess.call(script + args, env=env)\n            if rc != 0:\n                sys.exit('Dist script errored out')\n        except OSError:\n            print('Failed to run dist script {!r}'.format(name))\n            sys.exit(1)\n\n\ndef git_have_dirty_index(src_root):\n    '''Check whether there are uncommitted changes in git'''\n    ret = subprocess.call(['git', '-C', src_root, 'diff-index', '--quiet', 'HEAD'])\n    return ret == 1\n\ndef create_dist_git(dist_name, src_root, bld_root, dist_sub, dist_scripts):\n    if git_have_dirty_index(src_root):\n        mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')\n    distdir = os.path.join(dist_sub, dist_name)\n    if os.path.exists(distdir):\n        shutil.rmtree(distdir)\n    os.makedirs(distdir)\n    subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])\n    process_submodules(distdir)\n    del_gitfiles(distdir)\n    run_dist_scripts(distdir, dist_scripts)\n    xzname = distdir + '.tar.xz'\n    # Should use shutil but it got xz support only in 3.5.\n    with tarfile.open(xzname, 'w:xz') as tf:\n        tf.add(distdir, dist_name)\n    # Create only .tar.xz for now.\n    # zipname = distdir + '.zip'\n    # create_zip(zipname, distdir)\n    shutil.rmtree(distdir)\n    return (xzname, )\n\n\ndef hg_have_dirty_index(src_root):\n    '''Check whether there are uncommitted changes in hg'''\n    out = subprocess.check_output(['hg', '-R', src_root, 'summary'])\n    return b'commit: (clean)' not in out\n\ndef create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):\n    if hg_have_dirty_index(src_root):\n        mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')\n\n    os.makedirs(dist_sub, exist_ok=True)\n    tarname = os.path.join(dist_sub, dist_name + '.tar')\n    xzname = tarname + '.xz'\n    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])\n    if dist_scripts:\n        mlog.warning('dist scripts are not supported in Mercurial projects')\n    with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:\n        shutil.copyfileobj(tf, xf)\n    os.unlink(tarname)\n    # Create only .tar.xz for now.\n    # zipname = os.path.join(dist_sub, dist_name + '.zip')\n    # subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])\n    return (xzname, )\n\n\ndef check_dist(packagename, meson_command):\n    print('Testing distribution package %s' % packagename)\n    unpackdir = tempfile.mkdtemp()\n    builddir = tempfile.mkdtemp()\n    installdir = tempfile.mkdtemp()\n    ninja_bin = detect_ninja()\n    try:\n        tf = tarfile.open(packagename)\n        tf.extractall(unpackdir)\n        srcdir = glob(os.path.join(unpackdir, '*'))[0]\n        if subprocess.call(meson_command + ['--backend=ninja', srcdir, builddir]) != 0:\n            print('Running Meson on distribution package failed')\n            return 1\n        if subprocess.call([ninja_bin], cwd=builddir) != 0:\n            print('Compiling the distribution package failed')\n            return 1\n        if subprocess.call([ninja_bin, 'test'], cwd=builddir) != 0:\n            print('Running unit tests on the distribution package failed')\n            return 1\n        myenv = os.environ.copy()\n        myenv['DESTDIR'] = installdir\n        if subprocess.call([ninja_bin, 'install'], cwd=builddir, env=myenv) != 0:\n            print('Installing the distribution package failed')\n            return 1\n    finally:\n        shutil.rmtree(unpackdir)\n        shutil.rmtree(builddir)\n        shutil.rmtree(installdir)\n    print('Distribution package %s tested' % packagename)\n    return 0\n\ndef run(args):\n    src_root = args[0]\n    bld_root = args[1]\n    meson_command = args[2:]\n    priv_dir = os.path.join(bld_root, 'meson-private')\n    dist_sub = os.path.join(bld_root, 'meson-dist')\n\n    buildfile = os.path.join(priv_dir, 'build.dat')\n\n    build = pickle.load(open(buildfile, 'rb'))\n\n    dist_name = build.project_name + '-' + build.project_version\n\n    _git = os.path.join(src_root, '.git')\n    if os.path.isdir(_git) or os.path.isfile(_git):\n        names = create_dist_git(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)\n    elif os.path.isdir(os.path.join(src_root, '.hg')):\n        names = create_dist_hg(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)\n    else:\n        print('Dist currently only works with Git or Mercurial repos')\n        return 1\n    if names is None:\n        return 1\n    error_count = 0\n    for name in names:\n        rc = check_dist(name, meson_command) # Check only one.\n        if rc == 0:\n            create_hash(name)\n        error_count += rc\n    return 1 if error_count else 0\n", "path": "mesonbuild/scripts/dist.py"}], "after_files": [{"content": "# Copyright 2017 The Meson development team\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport lzma\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport pickle\nimport hashlib\nimport tarfile, zipfile\nimport tempfile\nfrom glob import glob\nfrom mesonbuild.environment import detect_ninja\nfrom mesonbuild.mesonlib import windows_proof_rmtree\nfrom mesonbuild import mlog\n\ndef create_hash(fname):\n    hashname = fname + '.sha256sum'\n    m = hashlib.sha256()\n    m.update(open(fname, 'rb').read())\n    with open(hashname, 'w') as f:\n        f.write('%s %s\\n' % (m.hexdigest(), os.path.basename(fname)))\n\n\ndef create_zip(zipfilename, packaging_dir):\n    prefix = os.path.dirname(packaging_dir)\n    removelen = len(prefix) + 1\n    with zipfile.ZipFile(zipfilename,\n                         'w',\n                         compression=zipfile.ZIP_DEFLATED,\n                         allowZip64=True) as zf:\n        zf.write(packaging_dir, packaging_dir[removelen:])\n        for root, dirs, files in os.walk(packaging_dir):\n            for d in dirs:\n                dname = os.path.join(root, d)\n                zf.write(dname, dname[removelen:])\n            for f in files:\n                fname = os.path.join(root, f)\n                zf.write(fname, fname[removelen:])\n\ndef del_gitfiles(dirname):\n    for f in glob(os.path.join(dirname, '.git*')):\n        if os.path.isdir(f) and not os.path.islink(f):\n            windows_proof_rmtree(f)\n        else:\n            os.unlink(f)\n\ndef process_submodules(dirname):\n    module_file = os.path.join(dirname, '.gitmodules')\n    if not os.path.exists(module_file):\n        return\n    subprocess.check_call(['git', 'submodule', 'update', '--init', '--recursive'], cwd=dirname)\n    for line in open(module_file):\n        line = line.strip()\n        if '=' not in line:\n            continue\n        k, v = line.split('=', 1)\n        k = k.strip()\n        v = v.strip()\n        if k != 'path':\n            continue\n        del_gitfiles(os.path.join(dirname, v))\n\n\ndef run_dist_scripts(dist_root, dist_scripts):\n    assert(os.path.isabs(dist_root))\n    env = os.environ.copy()\n    env['MESON_DIST_ROOT'] = dist_root\n    for d in dist_scripts:\n        script = d['exe']\n        args = d['args']\n        name = ' '.join(script + args)\n        print('Running custom dist script {!r}'.format(name))\n        try:\n            rc = subprocess.call(script + args, env=env)\n            if rc != 0:\n                sys.exit('Dist script errored out')\n        except OSError:\n            print('Failed to run dist script {!r}'.format(name))\n            sys.exit(1)\n\n\ndef git_have_dirty_index(src_root):\n    '''Check whether there are uncommitted changes in git'''\n    ret = subprocess.call(['git', '-C', src_root, 'diff-index', '--quiet', 'HEAD'])\n    return ret == 1\n\ndef create_dist_git(dist_name, src_root, bld_root, dist_sub, dist_scripts):\n    if git_have_dirty_index(src_root):\n        mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')\n    distdir = os.path.join(dist_sub, dist_name)\n    if os.path.exists(distdir):\n        shutil.rmtree(distdir)\n    os.makedirs(distdir)\n    subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])\n    process_submodules(distdir)\n    del_gitfiles(distdir)\n    run_dist_scripts(distdir, dist_scripts)\n    xzname = distdir + '.tar.xz'\n    # Should use shutil but it got xz support only in 3.5.\n    with tarfile.open(xzname, 'w:xz') as tf:\n        tf.add(distdir, dist_name)\n    # Create only .tar.xz for now.\n    # zipname = distdir + '.zip'\n    # create_zip(zipname, distdir)\n    shutil.rmtree(distdir)\n    return (xzname, )\n\n\ndef hg_have_dirty_index(src_root):\n    '''Check whether there are uncommitted changes in hg'''\n    out = subprocess.check_output(['hg', '-R', src_root, 'summary'])\n    return b'commit: (clean)' not in out\n\ndef create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):\n    if hg_have_dirty_index(src_root):\n        mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')\n\n    os.makedirs(dist_sub, exist_ok=True)\n    tarname = os.path.join(dist_sub, dist_name + '.tar')\n    xzname = tarname + '.xz'\n    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])\n    if dist_scripts:\n        mlog.warning('dist scripts are not supported in Mercurial projects')\n    with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:\n        shutil.copyfileobj(tf, xf)\n    os.unlink(tarname)\n    # Create only .tar.xz for now.\n    # zipname = os.path.join(dist_sub, dist_name + '.zip')\n    # subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])\n    return (xzname, )\n\n\ndef check_dist(packagename, meson_command, privdir):\n    print('Testing distribution package %s' % packagename)\n    unpackdir = os.path.join(privdir, 'dist-unpack')\n    builddir = os.path.join(privdir, 'dist-build')\n    installdir = os.path.join(privdir, 'dist-install')\n    for p in (unpackdir, builddir, installdir):\n        if os.path.exists(p):\n            shutil.rmtree(p)\n        os.mkdir(p)\n    ninja_bin = detect_ninja()\n    try:\n        tf = tarfile.open(packagename)\n        tf.extractall(unpackdir)\n        srcdir = glob(os.path.join(unpackdir, '*'))[0]\n        if subprocess.call(meson_command + ['--backend=ninja', srcdir, builddir]) != 0:\n            print('Running Meson on distribution package failed')\n            return 1\n        if subprocess.call([ninja_bin], cwd=builddir) != 0:\n            print('Compiling the distribution package failed')\n            return 1\n        if subprocess.call([ninja_bin, 'test'], cwd=builddir) != 0:\n            print('Running unit tests on the distribution package failed')\n            return 1\n        myenv = os.environ.copy()\n        myenv['DESTDIR'] = installdir\n        if subprocess.call([ninja_bin, 'install'], cwd=builddir, env=myenv) != 0:\n            print('Installing the distribution package failed')\n            return 1\n    finally:\n        shutil.rmtree(unpackdir)\n        shutil.rmtree(builddir)\n        shutil.rmtree(installdir)\n    print('Distribution package %s tested' % packagename)\n    return 0\n\ndef run(args):\n    src_root = args[0]\n    bld_root = args[1]\n    meson_command = args[2:]\n    priv_dir = os.path.join(bld_root, 'meson-private')\n    dist_sub = os.path.join(bld_root, 'meson-dist')\n\n    buildfile = os.path.join(priv_dir, 'build.dat')\n\n    build = pickle.load(open(buildfile, 'rb'))\n\n    dist_name = build.project_name + '-' + build.project_version\n\n    _git = os.path.join(src_root, '.git')\n    if os.path.isdir(_git) or os.path.isfile(_git):\n        names = create_dist_git(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)\n    elif os.path.isdir(os.path.join(src_root, '.hg')):\n        names = create_dist_hg(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)\n    else:\n        print('Dist currently only works with Git or Mercurial repos')\n        return 1\n    if names is None:\n        return 1\n    error_count = 0\n    for name in names:\n        rc = check_dist(name, meson_command, priv_dir) # Check only one.\n        if rc == 0:\n            create_hash(name)\n        error_count += rc\n    return 1 if error_count else 0\n", "path": "mesonbuild/scripts/dist.py"}]}