We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
.hgtags included in dist archive
**Describe the bug**

When running `ninja dist` the resulting `tar.xz` includes the `.hgtags` files that distros (namely debian) will mark as a packaging error.

**To Reproduce**

Run `ninja dist` on a mercurial repository that has at least 1 tag.

**Expected behavior**
That the archive does not have the `.hgtags` file in it.  I just did an `hg archive` and unfortunately saw that `.hgtags` was there as well.

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)? Native
* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.) Linux, Debian Unstable
* what Python version are you using e.g. 3.8.0: 3.7.6
* what `meson --version`: 0.53.1
* what `ninja --version` if it's a Ninja build: 1.9.0


--- 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/mdist.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 gzip
17 import os
18 import sys
19 import shutil
20 import subprocess
21 import hashlib
22 import json
23 from glob import glob
24 from pathlib import Path
25 from mesonbuild.environment import detect_ninja
26 from mesonbuild.mesonlib import windows_proof_rmtree, MesonException
27 from mesonbuild.wrap import wrap
28 from mesonbuild import mlog, build
29 
30 archive_choices = ['gztar', 'xztar', 'zip']
31 archive_extension = {'gztar': '.tar.gz',
32                      'xztar': '.tar.xz',
33                      'zip': '.zip'}
34 
35 def add_arguments(parser):
36     parser.add_argument('-C', default='.', dest='wd',
37                         help='directory to cd into before running')
38     parser.add_argument('--formats', default='xztar',
39                         help='Comma separated list of archive types to create.')
40     parser.add_argument('--include-subprojects', action='store_true',
41                         help='Include source code of subprojects that have been used for the build.')
42 
43 
44 def create_hash(fname):
45     hashname = fname + '.sha256sum'
46     m = hashlib.sha256()
47     m.update(open(fname, 'rb').read())
48     with open(hashname, 'w') as f:
49         f.write('%s %s\n' % (m.hexdigest(), os.path.basename(fname)))
50 
51 
52 def del_gitfiles(dirname):
53     for f in glob(os.path.join(dirname, '.git*')):
54         if os.path.isdir(f) and not os.path.islink(f):
55             windows_proof_rmtree(f)
56         else:
57             os.unlink(f)
58 
59 def process_submodules(dirname):
60     module_file = os.path.join(dirname, '.gitmodules')
61     if not os.path.exists(module_file):
62         return
63     subprocess.check_call(['git', 'submodule', 'update', '--init', '--recursive'], cwd=dirname)
64     for line in open(module_file):
65         line = line.strip()
66         if '=' not in line:
67             continue
68         k, v = line.split('=', 1)
69         k = k.strip()
70         v = v.strip()
71         if k != 'path':
72             continue
73         del_gitfiles(os.path.join(dirname, v))
74 
75 
76 def run_dist_scripts(src_root, bld_root, dist_root, dist_scripts):
77     assert(os.path.isabs(dist_root))
78     env = os.environ.copy()
79     env['MESON_DIST_ROOT'] = dist_root
80     env['MESON_SOURCE_ROOT'] = src_root
81     env['MESON_BUILD_ROOT'] = bld_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 def is_git(src_root):
96     _git = os.path.join(src_root, '.git')
97     return os.path.isdir(_git) or os.path.isfile(_git)
98 
99 def git_have_dirty_index(src_root):
100     '''Check whether there are uncommitted changes in git'''
101     ret = subprocess.call(['git', '-C', src_root, 'diff-index', '--quiet', 'HEAD'])
102     return ret == 1
103 
104 def git_clone(src_root, distdir):
105     if git_have_dirty_index(src_root):
106         mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')
107     if os.path.exists(distdir):
108         shutil.rmtree(distdir)
109     os.makedirs(distdir)
110     subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])
111     process_submodules(distdir)
112     del_gitfiles(distdir)
113 
114 def create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, dist_scripts, subprojects):
115     distdir = os.path.join(dist_sub, dist_name)
116     git_clone(src_root, distdir)
117     for path in subprojects:
118         sub_src_root = os.path.join(src_root, path)
119         sub_distdir = os.path.join(distdir, path)
120         if os.path.exists(sub_distdir):
121             continue
122         if is_git(sub_src_root):
123             git_clone(sub_src_root, sub_distdir)
124         else:
125             shutil.copytree(sub_src_root, sub_distdir)
126     run_dist_scripts(src_root, bld_root, distdir, dist_scripts)
127     output_names = []
128     for a in archives:
129         compressed_name = distdir + archive_extension[a]
130         shutil.make_archive(distdir, a, root_dir=dist_sub, base_dir=dist_name)
131         output_names.append(compressed_name)
132     shutil.rmtree(distdir)
133     return output_names
134 
135 def is_hg(src_root):
136     return os.path.isdir(os.path.join(src_root, '.hg'))
137 
138 def hg_have_dirty_index(src_root):
139     '''Check whether there are uncommitted changes in hg'''
140     out = subprocess.check_output(['hg', '-R', src_root, 'summary'])
141     return b'commit: (clean)' not in out
142 
143 def create_dist_hg(dist_name, archives, src_root, bld_root, dist_sub, dist_scripts):
144     if hg_have_dirty_index(src_root):
145         mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')
146     if dist_scripts:
147         mlog.warning('dist scripts are not supported in Mercurial projects')
148 
149     os.makedirs(dist_sub, exist_ok=True)
150     tarname = os.path.join(dist_sub, dist_name + '.tar')
151     xzname = tarname + '.xz'
152     gzname = tarname + '.gz'
153     zipname = os.path.join(dist_sub, dist_name + '.zip')
154     subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])
155     output_names = []
156     if 'xztar' in archives:
157         import lzma
158         with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:
159             shutil.copyfileobj(tf, xf)
160         output_names.append(xzname)
161     if 'gztar' in archives:
162         with gzip.open(gzname, 'wb') as zf, open(tarname, 'rb') as tf:
163             shutil.copyfileobj(tf, zf)
164         output_names.append(gzname)
165     os.unlink(tarname)
166     if 'zip' in archives:
167         subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])
168         output_names.append(zipname)
169     return output_names
170 
171 
172 def check_dist(packagename, meson_command, extra_meson_args, bld_root, privdir):
173     print('Testing distribution package %s' % packagename)
174     unpackdir = os.path.join(privdir, 'dist-unpack')
175     builddir = os.path.join(privdir, 'dist-build')
176     installdir = os.path.join(privdir, 'dist-install')
177     for p in (unpackdir, builddir, installdir):
178         if os.path.exists(p):
179             shutil.rmtree(p)
180         os.mkdir(p)
181     ninja_bin = detect_ninja()
182     try:
183         shutil.unpack_archive(packagename, unpackdir)
184         unpacked_files = glob(os.path.join(unpackdir, '*'))
185         assert(len(unpacked_files) == 1)
186         unpacked_src_dir = unpacked_files[0]
187         with open(os.path.join(bld_root, 'meson-info', 'intro-buildoptions.json')) as boptions:
188             meson_command += ['-D{name}={value}'.format(**o) for o in json.load(boptions)
189                               if o['name'] not in ['backend', 'install_umask']]
190         meson_command += extra_meson_args
191         if subprocess.call(meson_command + ['--backend=ninja', unpacked_src_dir, builddir]) != 0:
192             print('Running Meson on distribution package failed')
193             return 1
194         if subprocess.call([ninja_bin], cwd=builddir) != 0:
195             print('Compiling the distribution package failed')
196             return 1
197         if subprocess.call([ninja_bin, 'test'], cwd=builddir) != 0:
198             print('Running unit tests on the distribution package failed')
199             return 1
200         myenv = os.environ.copy()
201         myenv['DESTDIR'] = installdir
202         if subprocess.call([ninja_bin, 'install'], cwd=builddir, env=myenv) != 0:
203             print('Installing the distribution package failed')
204             return 1
205     finally:
206         shutil.rmtree(unpackdir)
207         shutil.rmtree(builddir)
208         shutil.rmtree(installdir)
209     print('Distribution package %s tested' % packagename)
210     return 0
211 
212 def determine_archives_to_generate(options):
213     result = []
214     for i in options.formats.split(','):
215         if i not in archive_choices:
216             sys.exit('Value "{}" not one of permitted values {}.'.format(i, archive_choices))
217         result.append(i)
218     if len(i) == 0:
219         sys.exit('No archive types specified.')
220     return result
221 
222 def run(options):
223     options.wd = os.path.abspath(options.wd)
224     buildfile = Path(options.wd) / 'meson-private' / 'build.dat'
225     if not buildfile.is_file():
226         raise MesonException('Directory {!r} does not seem to be a Meson build directory.'.format(options.wd))
227     b = build.load(options.wd)
228     # This import must be load delayed, otherwise it will get the default
229     # value of None.
230     from mesonbuild.mesonlib import meson_command
231     src_root = b.environment.source_dir
232     bld_root = b.environment.build_dir
233     priv_dir = os.path.join(bld_root, 'meson-private')
234     dist_sub = os.path.join(bld_root, 'meson-dist')
235 
236     dist_name = b.project_name + '-' + b.project_version
237 
238     archives = determine_archives_to_generate(options)
239 
240     subprojects = []
241     extra_meson_args = []
242     if options.include_subprojects:
243         subproject_dir = os.path.join(src_root, b.subproject_dir)
244         for sub in b.subprojects:
245             _, directory = wrap.get_directory(subproject_dir, sub)
246             subprojects.append(os.path.join(b.subproject_dir, directory))
247         extra_meson_args.append('-Dwrap_mode=nodownload')
248 
249     if is_git(src_root):
250         names = create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, b.dist_scripts, subprojects)
251     elif is_hg(src_root):
252         if subprojects:
253             print('--include-subprojects option currently not supported with Mercurial')
254             return 1
255         names = create_dist_hg(dist_name, archives, src_root, bld_root, dist_sub, b.dist_scripts)
256     else:
257         print('Dist currently only works with Git or Mercurial repos')
258         return 1
259     if names is None:
260         return 1
261     # Check only one.
262     rc = check_dist(names[0], meson_command, extra_meson_args, bld_root, priv_dir)
263     if rc == 0:
264         for name in names:
265             create_hash(name)
266     return rc
267
```
--- 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/mdist.py b/mesonbuild/mdist.py
--- a/mesonbuild/mdist.py
+++ b/mesonbuild/mdist.py
@@ -151,7 +151,16 @@
     xzname = tarname + '.xz'
     gzname = tarname + '.gz'
     zipname = os.path.join(dist_sub, dist_name + '.zip')
-    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])
+    # Note that -X interprets relative paths using the current working
+    # directory, not the repository root, so this must be an absolute path:
+    # https://bz.mercurial-scm.org/show_bug.cgi?id=6267
+    #
+    # .hg[a-z]* is used instead of .hg* to keep .hg_archival.txt, which may
+    # be useful to link the tarball to the Mercurial revision for either
+    # manual inspection or in case any code interprets it for a --version or
+    # similar.
+    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar',
+                           '-X', src_root + '/.hg[a-z]*', tarname])
     output_names = []
     if 'xztar' in archives:
         import lzma

{"golden_diff": "diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py\n--- a/mesonbuild/mdist.py\n+++ b/mesonbuild/mdist.py\n@@ -151,7 +151,16 @@\n     xzname = tarname + '.xz'\n     gzname = tarname + '.gz'\n     zipname = os.path.join(dist_sub, dist_name + '.zip')\n-    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])\n+    # Note that -X interprets relative paths using the current working\n+    # directory, not the repository root, so this must be an absolute path:\n+    # https://bz.mercurial-scm.org/show_bug.cgi?id=6267\n+    #\n+    # .hg[a-z]* is used instead of .hg* to keep .hg_archival.txt, which may\n+    # be useful to link the tarball to the Mercurial revision for either\n+    # manual inspection or in case any code interprets it for a --version or\n+    # similar.\n+    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar',\n+                           '-X', src_root + '/.hg[a-z]*', tarname])\n     output_names = []\n     if 'xztar' in archives:\n         import lzma\n", "issue": ".hgtags included in dist archive\n**Describe the bug**\r\n\r\nWhen running `ninja dist` the resulting `tar.xz` includes the `.hgtags` files that distros (namely debian) will mark as a packaging error.\r\n\r\n**To Reproduce**\r\n\r\nRun `ninja dist` on a mercurial repository that has at least 1 tag.\r\n\r\n**Expected behavior**\r\nThat the archive does not have the `.hgtags` file in it.  I just did an `hg archive` and unfortunately saw that `.hgtags` was there as well.\r\n\r\n**system parameters**\r\n* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)? Native\r\n* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.) Linux, Debian Unstable\r\n* what Python version are you using e.g. 3.8.0: 3.7.6\r\n* what `meson --version`: 0.53.1\r\n* what `ninja --version` if it's a Ninja build: 1.9.0\r\n\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 gzip\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport hashlib\nimport json\nfrom glob import glob\nfrom pathlib import Path\nfrom mesonbuild.environment import detect_ninja\nfrom mesonbuild.mesonlib import windows_proof_rmtree, MesonException\nfrom mesonbuild.wrap import wrap\nfrom mesonbuild import mlog, build\n\narchive_choices = ['gztar', 'xztar', 'zip']\narchive_extension = {'gztar': '.tar.gz',\n                     'xztar': '.tar.xz',\n                     'zip': '.zip'}\n\ndef add_arguments(parser):\n    parser.add_argument('-C', default='.', dest='wd',\n                        help='directory to cd into before running')\n    parser.add_argument('--formats', default='xztar',\n                        help='Comma separated list of archive types to create.')\n    parser.add_argument('--include-subprojects', action='store_true',\n                        help='Include source code of subprojects that have been used for the build.')\n\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 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(src_root, bld_root, dist_root, dist_scripts):\n    assert(os.path.isabs(dist_root))\n    env = os.environ.copy()\n    env['MESON_DIST_ROOT'] = dist_root\n    env['MESON_SOURCE_ROOT'] = src_root\n    env['MESON_BUILD_ROOT'] = bld_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\ndef is_git(src_root):\n    _git = os.path.join(src_root, '.git')\n    return os.path.isdir(_git) or os.path.isfile(_git)\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 git_clone(src_root, distdir):\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    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\ndef create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, dist_scripts, subprojects):\n    distdir = os.path.join(dist_sub, dist_name)\n    git_clone(src_root, distdir)\n    for path in subprojects:\n        sub_src_root = os.path.join(src_root, path)\n        sub_distdir = os.path.join(distdir, path)\n        if os.path.exists(sub_distdir):\n            continue\n        if is_git(sub_src_root):\n            git_clone(sub_src_root, sub_distdir)\n        else:\n            shutil.copytree(sub_src_root, sub_distdir)\n    run_dist_scripts(src_root, bld_root, distdir, dist_scripts)\n    output_names = []\n    for a in archives:\n        compressed_name = distdir + archive_extension[a]\n        shutil.make_archive(distdir, a, root_dir=dist_sub, base_dir=dist_name)\n        output_names.append(compressed_name)\n    shutil.rmtree(distdir)\n    return output_names\n\ndef is_hg(src_root):\n    return os.path.isdir(os.path.join(src_root, '.hg'))\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, archives, 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    if dist_scripts:\n        mlog.warning('dist scripts are not supported in Mercurial projects')\n\n    os.makedirs(dist_sub, exist_ok=True)\n    tarname = os.path.join(dist_sub, dist_name + '.tar')\n    xzname = tarname + '.xz'\n    gzname = tarname + '.gz'\n    zipname = os.path.join(dist_sub, dist_name + '.zip')\n    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])\n    output_names = []\n    if 'xztar' in archives:\n        import lzma\n        with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:\n            shutil.copyfileobj(tf, xf)\n        output_names.append(xzname)\n    if 'gztar' in archives:\n        with gzip.open(gzname, 'wb') as zf, open(tarname, 'rb') as tf:\n            shutil.copyfileobj(tf, zf)\n        output_names.append(gzname)\n    os.unlink(tarname)\n    if 'zip' in archives:\n        subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])\n        output_names.append(zipname)\n    return output_names\n\n\ndef check_dist(packagename, meson_command, extra_meson_args, bld_root, 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        shutil.unpack_archive(packagename, unpackdir)\n        unpacked_files = glob(os.path.join(unpackdir, '*'))\n        assert(len(unpacked_files) == 1)\n        unpacked_src_dir = unpacked_files[0]\n        with open(os.path.join(bld_root, 'meson-info', 'intro-buildoptions.json')) as boptions:\n            meson_command += ['-D{name}={value}'.format(**o) for o in json.load(boptions)\n                              if o['name'] not in ['backend', 'install_umask']]\n        meson_command += extra_meson_args\n        if subprocess.call(meson_command + ['--backend=ninja', unpacked_src_dir, 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 determine_archives_to_generate(options):\n    result = []\n    for i in options.formats.split(','):\n        if i not in archive_choices:\n            sys.exit('Value \"{}\" not one of permitted values {}.'.format(i, archive_choices))\n        result.append(i)\n    if len(i) == 0:\n        sys.exit('No archive types specified.')\n    return result\n\ndef run(options):\n    options.wd = os.path.abspath(options.wd)\n    buildfile = Path(options.wd) / 'meson-private' / 'build.dat'\n    if not buildfile.is_file():\n        raise MesonException('Directory {!r} does not seem to be a Meson build directory.'.format(options.wd))\n    b = build.load(options.wd)\n    # This import must be load delayed, otherwise it will get the default\n    # value of None.\n    from mesonbuild.mesonlib import meson_command\n    src_root = b.environment.source_dir\n    bld_root = b.environment.build_dir\n    priv_dir = os.path.join(bld_root, 'meson-private')\n    dist_sub = os.path.join(bld_root, 'meson-dist')\n\n    dist_name = b.project_name + '-' + b.project_version\n\n    archives = determine_archives_to_generate(options)\n\n    subprojects = []\n    extra_meson_args = []\n    if options.include_subprojects:\n        subproject_dir = os.path.join(src_root, b.subproject_dir)\n        for sub in b.subprojects:\n            _, directory = wrap.get_directory(subproject_dir, sub)\n            subprojects.append(os.path.join(b.subproject_dir, directory))\n        extra_meson_args.append('-Dwrap_mode=nodownload')\n\n    if is_git(src_root):\n        names = create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, b.dist_scripts, subprojects)\n    elif is_hg(src_root):\n        if subprojects:\n            print('--include-subprojects option currently not supported with Mercurial')\n            return 1\n        names = create_dist_hg(dist_name, archives, src_root, bld_root, dist_sub, b.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    # Check only one.\n    rc = check_dist(names[0], meson_command, extra_meson_args, bld_root, priv_dir)\n    if rc == 0:\n        for name in names:\n            create_hash(name)\n    return rc\n", "path": "mesonbuild/mdist.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 gzip\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport hashlib\nimport json\nfrom glob import glob\nfrom pathlib import Path\nfrom mesonbuild.environment import detect_ninja\nfrom mesonbuild.mesonlib import windows_proof_rmtree, MesonException\nfrom mesonbuild.wrap import wrap\nfrom mesonbuild import mlog, build\n\narchive_choices = ['gztar', 'xztar', 'zip']\narchive_extension = {'gztar': '.tar.gz',\n                     'xztar': '.tar.xz',\n                     'zip': '.zip'}\n\ndef add_arguments(parser):\n    parser.add_argument('-C', default='.', dest='wd',\n                        help='directory to cd into before running')\n    parser.add_argument('--formats', default='xztar',\n                        help='Comma separated list of archive types to create.')\n    parser.add_argument('--include-subprojects', action='store_true',\n                        help='Include source code of subprojects that have been used for the build.')\n\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 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(src_root, bld_root, dist_root, dist_scripts):\n    assert(os.path.isabs(dist_root))\n    env = os.environ.copy()\n    env['MESON_DIST_ROOT'] = dist_root\n    env['MESON_SOURCE_ROOT'] = src_root\n    env['MESON_BUILD_ROOT'] = bld_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\ndef is_git(src_root):\n    _git = os.path.join(src_root, '.git')\n    return os.path.isdir(_git) or os.path.isfile(_git)\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 git_clone(src_root, distdir):\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    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\ndef create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, dist_scripts, subprojects):\n    distdir = os.path.join(dist_sub, dist_name)\n    git_clone(src_root, distdir)\n    for path in subprojects:\n        sub_src_root = os.path.join(src_root, path)\n        sub_distdir = os.path.join(distdir, path)\n        if os.path.exists(sub_distdir):\n            continue\n        if is_git(sub_src_root):\n            git_clone(sub_src_root, sub_distdir)\n        else:\n            shutil.copytree(sub_src_root, sub_distdir)\n    run_dist_scripts(src_root, bld_root, distdir, dist_scripts)\n    output_names = []\n    for a in archives:\n        compressed_name = distdir + archive_extension[a]\n        shutil.make_archive(distdir, a, root_dir=dist_sub, base_dir=dist_name)\n        output_names.append(compressed_name)\n    shutil.rmtree(distdir)\n    return output_names\n\ndef is_hg(src_root):\n    return os.path.isdir(os.path.join(src_root, '.hg'))\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, archives, 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    if dist_scripts:\n        mlog.warning('dist scripts are not supported in Mercurial projects')\n\n    os.makedirs(dist_sub, exist_ok=True)\n    tarname = os.path.join(dist_sub, dist_name + '.tar')\n    xzname = tarname + '.xz'\n    gzname = tarname + '.gz'\n    zipname = os.path.join(dist_sub, dist_name + '.zip')\n    # Note that -X interprets relative paths using the current working\n    # directory, not the repository root, so this must be an absolute path:\n    # https://bz.mercurial-scm.org/show_bug.cgi?id=6267\n    #\n    # .hg[a-z]* is used instead of .hg* to keep .hg_archival.txt, which may\n    # be useful to link the tarball to the Mercurial revision for either\n    # manual inspection or in case any code interprets it for a --version or\n    # similar.\n    subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar',\n                           '-X', src_root + '/.hg[a-z]*', tarname])\n    output_names = []\n    if 'xztar' in archives:\n        import lzma\n        with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:\n            shutil.copyfileobj(tf, xf)\n        output_names.append(xzname)\n    if 'gztar' in archives:\n        with gzip.open(gzname, 'wb') as zf, open(tarname, 'rb') as tf:\n            shutil.copyfileobj(tf, zf)\n        output_names.append(gzname)\n    os.unlink(tarname)\n    if 'zip' in archives:\n        subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'zip', zipname])\n        output_names.append(zipname)\n    return output_names\n\n\ndef check_dist(packagename, meson_command, extra_meson_args, bld_root, 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        shutil.unpack_archive(packagename, unpackdir)\n        unpacked_files = glob(os.path.join(unpackdir, '*'))\n        assert(len(unpacked_files) == 1)\n        unpacked_src_dir = unpacked_files[0]\n        with open(os.path.join(bld_root, 'meson-info', 'intro-buildoptions.json')) as boptions:\n            meson_command += ['-D{name}={value}'.format(**o) for o in json.load(boptions)\n                              if o['name'] not in ['backend', 'install_umask']]\n        meson_command += extra_meson_args\n        if subprocess.call(meson_command + ['--backend=ninja', unpacked_src_dir, 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 determine_archives_to_generate(options):\n    result = []\n    for i in options.formats.split(','):\n        if i not in archive_choices:\n            sys.exit('Value \"{}\" not one of permitted values {}.'.format(i, archive_choices))\n        result.append(i)\n    if len(i) == 0:\n        sys.exit('No archive types specified.')\n    return result\n\ndef run(options):\n    options.wd = os.path.abspath(options.wd)\n    buildfile = Path(options.wd) / 'meson-private' / 'build.dat'\n    if not buildfile.is_file():\n        raise MesonException('Directory {!r} does not seem to be a Meson build directory.'.format(options.wd))\n    b = build.load(options.wd)\n    # This import must be load delayed, otherwise it will get the default\n    # value of None.\n    from mesonbuild.mesonlib import meson_command\n    src_root = b.environment.source_dir\n    bld_root = b.environment.build_dir\n    priv_dir = os.path.join(bld_root, 'meson-private')\n    dist_sub = os.path.join(bld_root, 'meson-dist')\n\n    dist_name = b.project_name + '-' + b.project_version\n\n    archives = determine_archives_to_generate(options)\n\n    subprojects = []\n    extra_meson_args = []\n    if options.include_subprojects:\n        subproject_dir = os.path.join(src_root, b.subproject_dir)\n        for sub in b.subprojects:\n            _, directory = wrap.get_directory(subproject_dir, sub)\n            subprojects.append(os.path.join(b.subproject_dir, directory))\n        extra_meson_args.append('-Dwrap_mode=nodownload')\n\n    if is_git(src_root):\n        names = create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, b.dist_scripts, subprojects)\n    elif is_hg(src_root):\n        if subprojects:\n            print('--include-subprojects option currently not supported with Mercurial')\n            return 1\n        names = create_dist_hg(dist_name, archives, src_root, bld_root, dist_sub, b.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    # Check only one.\n    rc = check_dist(names[0], meson_command, extra_meson_args, bld_root, priv_dir)\n    if rc == 0:\n        for name in names:\n            create_hash(name)\n    return rc\n", "path": "mesonbuild/mdist.py"}]}