aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2018-07-18 17:11:57 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2018-08-27 23:35:29 +0300
commitfb770e1e3d17d548c34817001733f2a13f24ce9f (patch)
tree7e9bf40d6ff7cc23256ec913fb831b35be126906
parent1ffc8de5e8cc79dbaa54fd1ac02b6b4c5edac7a1 (diff)
downloadmeson-fb770e1e3d17d548c34817001733f2a13f24ce9f.zip
meson-fb770e1e3d17d548c34817001733f2a13f24ce9f.tar.gz
meson-fb770e1e3d17d548c34817001733f2a13f24ce9f.tar.bz2
Add support for custom dist scripts.
-rw-r--r--docs/markdown/Reference-manual.md9
-rw-r--r--docs/markdown/Reference-tables.md2
-rw-r--r--docs/markdown/snippets/distscript.md12
-rw-r--r--mesonbuild/build.py1
-rw-r--r--mesonbuild/interpreter.py10
-rw-r--r--mesonbuild/scripts/dist.py29
-rwxr-xr-xrun_unittests.py50
-rw-r--r--test cases/unit/35 dist script/meson.build7
-rw-r--r--test cases/unit/35 dist script/prog.c7
-rwxr-xr-xtest cases/unit/35 dist script/replacer.py12
10 files changed, 123 insertions, 16 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 6d2b2da..e830557 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1416,6 +1416,15 @@ The `meson` object allows you to introspect various properties of the
system. This object is always mapped in the `meson` variable. It has
the following methods.
+- `add_dist_script` causes the script given as argument to run during
+ `dist` operation after the distribution source has been generated
+ but before it is archived. Note that this runs the script file that
+ is in the _staging_ directory, not the one in the source
+ directory. If the script file can not be found in the staging
+ directory, it is a hard error. This command can only invoked from
+ the main project, calling it from a subproject is a hard
+ error. Available since 0.48.0.
+
- `add_install_script(script_name, arg1, arg2, ...)` causes the script
given as an argument to be run during the install step, this script
will have the environment variables `MESON_SOURCE_ROOT`,
diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md
index 9aa8609..46bcc3d 100644
--- a/docs/markdown/Reference-tables.md
+++ b/docs/markdown/Reference-tables.md
@@ -33,6 +33,8 @@ These are return values of the `get_id` method in a compiler object.
| MESON_BUILD_ROOT | Absolute path to the build dir |
| MESONINTROSPECT | Command to run to run the introspection command, may be of the form `python /path/to/meson introspect`, user is responsible for splitting the path if necessary. |
| MESON_SUBDIR | Current subdirectory, only set for `run_command` |
+| MESON_DIST_ROOT | Points to the root of the staging directory, only set when running `dist` scripts |
+
## CPU families
diff --git a/docs/markdown/snippets/distscript.md b/docs/markdown/snippets/distscript.md
new file mode 100644
index 0000000..37d05fe
--- /dev/null
+++ b/docs/markdown/snippets/distscript.md
@@ -0,0 +1,12 @@
+## Dist scripts
+
+You can now specify scripts that are run as part of the `dist`
+target. An example usage would go like this:
+
+```meson
+project('foo', 'c')
+
+# other stuff here
+
+meson.add_dist_script('dist_cleanup.py')
+```
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index b34ae2f..d35c697 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -123,6 +123,7 @@ class Build:
self.subproject_dir = ''
self.install_scripts = []
self.postconf_scripts = []
+ self.dist_scripts = []
self.install_dirs = []
self.dep_manifest_name = None
self.dep_manifest = {}
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index e001232..7dd24c5 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1619,6 +1619,7 @@ class MesonMain(InterpreterObject):
'build_root': self.build_root_method,
'add_install_script': self.add_install_script_method,
'add_postconf_script': self.add_postconf_script_method,
+ 'add_dist_script': self.add_dist_script_method,
'install_dependency_manifest': self.install_dependency_manifest_method,
'override_find_program': self.override_find_program_method,
'project_version': self.project_version_method,
@@ -1661,6 +1662,15 @@ class MesonMain(InterpreterObject):
script = self._find_source_script(args[0], args[1:])
self.build.postconf_scripts.append(script)
+ @permittedKwargs({})
+ def add_dist_script_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('add_dist_script takes exactly one argument')
+ check_stringlist(args, 'add_dist_script argument must be a string')
+ if self.interpreter.subproject != '':
+ raise InterpreterException('add_dist_script may not be used in a subproject.')
+ self.build.dist_scripts.append(os.path.join(self.interpreter.subdir, args[0]))
+
@noPosargs
@permittedKwargs({})
def current_source_dir_method(self, args, kwargs):
diff --git a/mesonbuild/scripts/dist.py b/mesonbuild/scripts/dist.py
index cb3bbe2..6fa10ff 100644
--- a/mesonbuild/scripts/dist.py
+++ b/mesonbuild/scripts/dist.py
@@ -15,6 +15,7 @@
import lzma
import os
+import sys
import shutil
import subprocess
import pickle
@@ -23,6 +24,7 @@ import tarfile, zipfile
import tempfile
from glob import glob
from mesonbuild.environment import detect_ninja
+from mesonbuild.dependencies import ExternalProgram
from mesonbuild.mesonlib import windows_proof_rmtree
def create_hash(fname):
@@ -73,7 +75,23 @@ def process_submodules(dirname):
del_gitfiles(os.path.join(dirname, v))
-def create_dist_git(dist_name, src_root, bld_root, dist_sub):
+def run_dist_scripts(dist_root, dist_scripts):
+ assert(os.path.isabs(dist_root))
+ env = os.environ.copy()
+ env['MESON_DIST_ROOT'] = dist_root
+ for d in dist_scripts:
+ print('Processing dist script %s.' % d)
+ ddir, dname = os.path.split(d)
+ ep = ExternalProgram(dname,
+ search_dir=os.path.join(dist_root, ddir),
+ silent=True)
+ if not ep.found():
+ sys.exit('Script %s could not be found in dist directory.' % d)
+ pc = subprocess.run(ep.command, env=env)
+ if pc.returncode != 0:
+ sys.exit('Dist script errored out.')
+
+def create_dist_git(dist_name, src_root, bld_root, dist_sub, dist_scripts):
distdir = os.path.join(dist_sub, dist_name)
if os.path.exists(distdir):
shutil.rmtree(distdir)
@@ -81,6 +99,7 @@ def create_dist_git(dist_name, src_root, bld_root, dist_sub):
subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])
process_submodules(distdir)
del_gitfiles(distdir)
+ run_dist_scripts(distdir, dist_scripts)
xzname = distdir + '.tar.xz'
# Should use shutil but it got xz support only in 3.5.
with tarfile.open(xzname, 'w:xz') as tf:
@@ -92,12 +111,14 @@ def create_dist_git(dist_name, src_root, bld_root, dist_sub):
return (xzname, )
-def create_dist_hg(dist_name, src_root, bld_root, dist_sub):
+def create_dist_hg(dist_name, src_root, bld_root, dist_sub, dist_scripts):
os.makedirs(dist_sub, exist_ok=True)
tarname = os.path.join(dist_sub, dist_name + '.tar')
xzname = tarname + '.xz'
subprocess.check_call(['hg', 'archive', '-R', src_root, '-S', '-t', 'tar', tarname])
+ if len(dist_scripts) > 0:
+ print('WARNING: dist scripts not supported in Mercurial projects.')
with lzma.open(xzname, 'wb') as xf, open(tarname, 'rb') as tf:
shutil.copyfileobj(tf, xf)
os.unlink(tarname)
@@ -152,9 +173,9 @@ def run(args):
dist_name = build.project_name + '-' + build.project_version
if os.path.isdir(os.path.join(src_root, '.git')):
- names = create_dist_git(dist_name, src_root, bld_root, dist_sub)
+ names = create_dist_git(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)
elif os.path.isdir(os.path.join(src_root, '.hg')):
- names = create_dist_hg(dist_name, src_root, bld_root, dist_sub)
+ names = create_dist_hg(dist_name, src_root, bld_root, dist_sub, build.dist_scripts)
else:
print('Dist currently only works with Git or Mercurial repos.')
return 1
diff --git a/run_unittests.py b/run_unittests.py
index ebead66..c1aa8de 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -85,6 +85,17 @@ def is_ci():
return True
return False
+def _git_init(project_dir):
+ subprocess.check_call(['git', 'init'], cwd=project_dir, stdout=subprocess.DEVNULL)
+ subprocess.check_call(['git', 'config',
+ 'user.name', 'Author Person'], cwd=project_dir)
+ subprocess.check_call(['git', 'config',
+ 'user.email', 'teh_coderz@example.com'], cwd=project_dir)
+ subprocess.check_call('git add *', cwd=project_dir, shell=True,
+ stdout=subprocess.DEVNULL)
+ subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir,
+ stdout=subprocess.DEVNULL)
+
def skipIfNoPkgconfig(f):
'''
Skip this test if no pkg-config is found, unless we're on Travis or
@@ -690,11 +701,19 @@ class DataTests(unittest.TestCase):
if f.parts[-1].endswith('~'):
continue
if f.suffix == '.md':
+ in_code_block = False
with f.open() as snippet:
for line in snippet:
+ if line.startswith(' '):
+ continue
+ if line.startswith('```'):
+ in_code_block = not in_code_block
+ if in_code_block:
+ continue
m = re.match(hashcounter, line)
if m:
self.assertEqual(len(m.group(0)), 2, 'All headings in snippets must have two hash symbols: ' + f.name)
+ self.assertFalse(in_code_block, 'Unclosed code block.')
else:
if f.name != 'add_release_note_snippets_here':
self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name)
@@ -1712,19 +1731,8 @@ class AllPlatformTests(BasePlatformTests):
if not shutil.which('git'):
raise unittest.SkipTest('Git not found')
- def git_init(project_dir):
- subprocess.check_call(['git', 'init'], cwd=project_dir, stdout=subprocess.DEVNULL)
- subprocess.check_call(['git', 'config',
- 'user.name', 'Author Person'], cwd=project_dir)
- subprocess.check_call(['git', 'config',
- 'user.email', 'teh_coderz@example.com'], cwd=project_dir)
- subprocess.check_call(['git', 'add', 'meson.build', 'distexe.c'], cwd=project_dir,
- stdout=subprocess.DEVNULL)
- subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir,
- stdout=subprocess.DEVNULL)
-
try:
- self.dist_impl(git_init)
+ self.dist_impl(_git_init)
except PermissionError:
# When run under Windows CI, something (virus scanner?)
# holds on to the git files so cleaning up the dir
@@ -1753,6 +1761,24 @@ class AllPlatformTests(BasePlatformTests):
# fails sometimes.
pass
+ def test_dist_git_script(self):
+ if not shutil.which('git'):
+ raise unittest.SkipTest('Git not found')
+
+ try:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ project_dir = os.path.join(tmpdir, 'a')
+ shutil.copytree(os.path.join(self.unit_test_dir, '35 dist script'),
+ project_dir)
+ _git_init(project_dir)
+ self.init(project_dir)
+ self.build('dist')
+ except PermissionError:
+ # When run under Windows CI, something (virus scanner?)
+ # holds on to the git files so cleaning up the dir
+ # fails sometimes.
+ pass
+
def dist_impl(self, vcs_init):
# Create this on the fly because having rogue .git directories inside
# the source tree leads to all kinds of trouble.
diff --git a/test cases/unit/35 dist script/meson.build b/test cases/unit/35 dist script/meson.build
new file mode 100644
index 0000000..3415ec4
--- /dev/null
+++ b/test cases/unit/35 dist script/meson.build
@@ -0,0 +1,7 @@
+project('dist script', 'c',
+ version : '1.0.0')
+
+exe = executable('comparer', 'prog.c')
+test('compare', exe)
+
+meson.add_dist_script('replacer.py')
diff --git a/test cases/unit/35 dist script/prog.c b/test cases/unit/35 dist script/prog.c
new file mode 100644
index 0000000..1bb6b05
--- /dev/null
+++ b/test cases/unit/35 dist script/prog.c
@@ -0,0 +1,7 @@
+#include<string.h>
+
+#define REPLACEME "incorrect"
+
+int main(int argc, char **argv) {
+ return strcmp(REPLACEME, "correct");
+}
diff --git a/test cases/unit/35 dist script/replacer.py b/test cases/unit/35 dist script/replacer.py
new file mode 100755
index 0000000..92bcef0
--- /dev/null
+++ b/test cases/unit/35 dist script/replacer.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import os, sys
+import pathlib
+
+source_root = pathlib.Path(os.environ['MESON_DIST_ROOT'])
+
+modfile = source_root / 'prog.c'
+
+contents = modfile.read_text()
+contents = contents.replace('"incorrect"', '"correct"')
+modfile.write_text(contents)