Add support for custom dist scripts.

pull/4092/head
Jussi Pakkanen 6 years ago
parent 1ffc8de5e8
commit fb770e1e3d
  1. 9
      docs/markdown/Reference-manual.md
  2. 2
      docs/markdown/Reference-tables.md
  3. 12
      docs/markdown/snippets/distscript.md
  4. 1
      mesonbuild/build.py
  5. 10
      mesonbuild/interpreter.py
  6. 29
      mesonbuild/scripts/dist.py
  7. 50
      run_unittests.py
  8. 7
      test cases/unit/35 dist script/meson.build
  9. 7
      test cases/unit/35 dist script/prog.c
  10. 12
      test cases/unit/35 dist script/replacer.py

@ -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`,

@ -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

@ -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')
```

@ -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 = {}

@ -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):

@ -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

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

@ -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')

@ -0,0 +1,7 @@
#include<string.h>
#define REPLACEME "incorrect"
int main(int argc, char **argv) {
return strcmp(REPLACEME, "correct");
}

@ -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)
Loading…
Cancel
Save