aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/scripts
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-01-16 17:35:29 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2016-01-16 17:35:29 +0200
commit23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585 (patch)
treee349597556abe3d22578cfb1f9529f4626ceb5aa /mesonbuild/scripts
parent1510522b1b9970376a1e1cc5f39e00d8749ec19a (diff)
downloadmeson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.zip
meson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.tar.gz
meson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.tar.bz2
Renamed meson package to mesonbuild so that we can have a script named meson in the same toplevel dir.
Diffstat (limited to 'mesonbuild/scripts')
-rw-r--r--mesonbuild/scripts/commandrunner.py59
-rw-r--r--mesonbuild/scripts/delwithsuffix.py37
-rw-r--r--mesonbuild/scripts/depfixer.py302
-rw-r--r--mesonbuild/scripts/dirchanger.py30
-rw-r--r--mesonbuild/scripts/gtkdochelper.py122
-rw-r--r--mesonbuild/scripts/meson_benchmark.py97
-rw-r--r--mesonbuild/scripts/meson_install.py215
-rw-r--r--mesonbuild/scripts/meson_test.py233
-rw-r--r--mesonbuild/scripts/regen_checker.py45
-rw-r--r--mesonbuild/scripts/symbolextractor.py106
-rw-r--r--mesonbuild/scripts/vcstagger.py36
11 files changed, 1282 insertions, 0 deletions
diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py
new file mode 100644
index 0000000..f5a2fff
--- /dev/null
+++ b/mesonbuild/scripts/commandrunner.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+
+# Copyright 2014 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This program is a wrapper to run external commands. It determines
+what to run, sets up the environment and executes the command."""
+
+import sys, os, subprocess, shutil
+
+def run_command(source_dir, build_dir, subdir, command, arguments):
+ env = {'MESON_SOURCE_ROOT' : source_dir,
+ 'MESON_BUILD_ROOT' : build_dir,
+ 'MESON_SUBDIR' : subdir
+ }
+ cwd = os.path.join(source_dir, subdir)
+ child_env = os.environ.copy()
+ child_env.update(env)
+
+ # Is the command an executable in path?
+ exe = shutil.which(command)
+ if exe is not None:
+ command_array = [exe] + arguments
+ return subprocess.Popen(command_array, env=child_env, cwd=cwd)
+ # No? Maybe it is a script in the source tree.
+ fullpath = os.path.join(source_dir, subdir, command)
+ command_array = [fullpath] + arguments
+ try:
+ return subprocess.Popen(command_array,env=child_env, cwd=cwd)
+ except FileNotFoundError:
+ print('Could not execute command "%s".' % command)
+ sys.exit(1)
+
+def run(args):
+ if len(sys.argv) < 4:
+ print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]')
+ sys.exit(1)
+ src_dir = sys.argv[1]
+ build_dir = sys.argv[2]
+ subdir = sys.argv[3]
+ command = sys.argv[4]
+ arguments = sys.argv[5:]
+ pc = run_command(src_dir, build_dir, subdir, command, arguments)
+ pc.wait()
+ return pc.returncode
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/delwithsuffix.py b/mesonbuild/scripts/delwithsuffix.py
new file mode 100644
index 0000000..38ab406
--- /dev/null
+++ b/mesonbuild/scripts/delwithsuffix.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+
+# Copyright 2013 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os, sys
+
+def run(args):
+ if len(sys.argv) != 2:
+ print('delwithsuffix.py <root of subdir to process> <suffix to delete>')
+ sys.exit(1)
+
+ topdir = sys.argv[1]
+ suffix = sys.argv[2]
+ if suffix[0] != '.':
+ suffix = '.' + suffix
+
+ for (root, _, files) in os.walk(topdir):
+ for f in files:
+ if f.endswith(suffix):
+ fullname = os.path.join(root, f)
+ os.unlink(fullname)
+ return 0
+
+if __name__ == '__main__':
+ run(sys.argv[1:])
diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py
new file mode 100644
index 0000000..1ab83b6
--- /dev/null
+++ b/mesonbuild/scripts/depfixer.py
@@ -0,0 +1,302 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-2014 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import sys, struct
+
+SHT_STRTAB = 3
+DT_NEEDED = 1
+DT_RPATH = 15
+DT_STRTAB = 5
+DT_SONAME = 14
+
+class DataSizes():
+ def __init__(self, ptrsize, is_le):
+ if is_le:
+ p = '<'
+ else:
+ p = '>'
+ self.Half = p+'h'
+ self.HalfSize = 2
+ self.Word = p+'I'
+ self.WordSize = 4
+ self.Sword = p+'i'
+ self.SwordSize = 4
+ if ptrsize == 64:
+ self.Addr = p+'Q'
+ self.AddrSize = 8
+ self.Off = p+'Q'
+ self.OffSize = 8
+ self.XWord = p+'Q'
+ self.XWordSize = 8
+ self.Sxword = p+'q'
+ self.SxwordSize = 8
+ else:
+ self.Addr = p+'I'
+ self.AddrSize = 4
+ self.Off = p+'I'
+ self.OffSize = 4
+
+class DynamicEntry(DataSizes):
+ def __init__(self, ifile, ptrsize, is_le):
+ super().__init__(ptrsize, is_le)
+ self.ptrsize = ptrsize
+ if ptrsize == 64:
+ self.d_tag = struct.unpack(self.Sxword, ifile.read(self.SxwordSize))[0];
+ self.val = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0];
+ else:
+ self.d_tag = struct.unpack(self.Sword, ifile.read(self.SwordSize))[0]
+ self.val = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+
+ def write(self, ofile):
+ if self.ptrsize == 64:
+ ofile.write(struct.pack(self.Sxword, self.d_tag))
+ ofile.write(struct.pack(self.XWord, self.val))
+ else:
+ ofile.write(struct.pack(self.Sword, self.d_tag))
+ ofile.write(struct.pack(self.Word, self.val))
+
+class SectionHeader(DataSizes):
+ def __init__(self, ifile, ptrsize, is_le):
+ super().__init__(ptrsize, is_le)
+ if ptrsize == 64:
+ is_64 = True
+ else:
+ is_64 = False
+#Elf64_Word
+ self.sh_name = struct.unpack(self.Word, ifile.read(self.WordSize))[0];
+#Elf64_Word
+ self.sh_type = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+#Elf64_Xword
+ if is_64:
+ self.sh_flags = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.sh_flags = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+#Elf64_Addr
+ self.sh_addr = struct.unpack(self.Addr, ifile.read(self.AddrSize))[0];
+#Elf64_Off
+ self.sh_offset = struct.unpack(self.Off, ifile.read(self.OffSize))[0]
+#Elf64_Xword
+ if is_64:
+ self.sh_size = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.sh_size = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+#Elf64_Word
+ self.sh_link = struct.unpack(self.Word, ifile.read(self.WordSize))[0];
+#Elf64_Word
+ self.sh_info = struct.unpack(self.Word, ifile.read(self.WordSize))[0];
+#Elf64_Xword
+ if is_64:
+ self.sh_addralign = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.sh_addralign = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+#Elf64_Xword
+ if is_64:
+ self.sh_entsize = struct.unpack(self.XWord, ifile.read(self.XWordSize))[0]
+ else:
+ self.sh_entsize = struct.unpack(self.Word, ifile.read(self.WordSize))[0]
+
+class Elf(DataSizes):
+ def __init__(self, bfile):
+ self.bfile = bfile
+ self.bf = open(bfile, 'r+b')
+ (self.ptrsize, self.is_le) = self.detect_elf_type()
+ super().__init__(self.ptrsize, self.is_le)
+ self.parse_header()
+ self.parse_sections()
+ self.parse_dynamic()
+
+ def detect_elf_type(self):
+ data = self.bf.read(6)
+ if data[1:4] != b'ELF':
+ # This script gets called to non-elf targets too
+ # so just ignore them.
+ print('File "%s" is not an ELF file.' % self.bfile)
+ sys.exit(0)
+ if data[4] == 1:
+ ptrsize = 32
+ elif data[4] == 2:
+ ptrsize = 64
+ else:
+ print('File "%s" has unknown ELF class.' % self.bfile)
+ sys.exit(1)
+ if data[5] == 1:
+ is_le = True
+ elif data[5] == 2:
+ is_le = False
+ else:
+ print('File "%s" has unknown ELF endianness.' % self.bfile)
+ sys.exit(1)
+ return (ptrsize, is_le)
+
+ def parse_header(self):
+ self.bf.seek(0)
+ self.e_ident = struct.unpack('16s', self.bf.read(16))[0]
+ self.e_type = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_machine = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_version = struct.unpack(self.Word, self.bf.read(self.WordSize))[0]
+ self.e_entry = struct.unpack(self.Addr, self.bf.read(self.AddrSize))[0]
+ self.e_phoff = struct.unpack(self.Off, self.bf.read(self.OffSize))[0]
+ self.e_shoff = struct.unpack(self.Off, self.bf.read(self.OffSize))[0]
+ self.e_flags = struct.unpack(self.Word, self.bf.read(self.WordSize))[0]
+ self.e_ehsize = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_phentsize = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_phnum = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_shentsize = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_shnum = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+ self.e_shstrndx = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0]
+
+ def parse_sections(self):
+ self.bf.seek(self.e_shoff)
+ self.sections = []
+ for i in range(self.e_shnum):
+ self.sections.append(SectionHeader(self.bf, self.ptrsize, self.is_le))
+
+ def read_str(self):
+ arr = []
+ x = self.bf.read(1)
+ while x != b'\0':
+ arr.append(x)
+ x = self.bf.read(1)
+ if x == b'':
+ raise RuntimeError('Tried to read past the end of the file')
+ return b''.join(arr)
+
+ def find_section(self, target_name):
+ section_names = self.sections[self.e_shstrndx]
+ for i in self.sections:
+ self.bf.seek(section_names.sh_offset + i.sh_name)
+ name = self.read_str()
+ if name == target_name:
+ return i
+
+ def parse_dynamic(self):
+ sec = self.find_section(b'.dynamic')
+ self.dynamic = []
+ self.bf.seek(sec.sh_offset)
+ while True:
+ e = DynamicEntry(self.bf, self.ptrsize, self.is_le)
+ self.dynamic.append(e)
+ if e.d_tag == 0:
+ break
+
+ def print_section_names(self):
+ section_names = self.sections[self.e_shstrndx]
+ for i in self.sections:
+ self.bf.seek(section_names.sh_offset + i.sh_name)
+ name = self.read_str()
+ print(name.decode())
+
+ def print_soname(self):
+ soname = None
+ strtab = None
+ for i in self.dynamic:
+ if i.d_tag == DT_SONAME:
+ soname = i
+ if i.d_tag == DT_STRTAB:
+ strtab = i
+ self.bf.seek(strtab.val + soname.val)
+ print(self.read_str())
+
+ def get_rpath_offset(self):
+ sec = self.find_section(b'.dynstr')
+ for i in self.dynamic:
+ if i.d_tag == DT_RPATH:
+ return sec.sh_offset + i.val
+ return None
+
+ def print_rpath(self):
+ offset = self.get_rpath_offset()
+ if offset is None:
+ print("This file does not have an rpath.")
+ else:
+ self.bf.seek(offset)
+ print(self.read_str())
+
+ def print_deps(self):
+ sec = self.find_section(b'.dynstr')
+ deps = []
+ for i in self.dynamic:
+ if i.d_tag == DT_NEEDED:
+ deps.append(i)
+ for i in deps:
+ offset = sec.sh_offset + i.val
+ self.bf.seek(offset)
+ name = self.read_str()
+ print(name)
+
+ def fix_deps(self, prefix):
+ sec = self.find_section(b'.dynstr')
+ deps = []
+ for i in self.dynamic:
+ if i.d_tag == DT_NEEDED:
+ deps.append(i)
+ for i in deps:
+ offset = sec.sh_offset + i.val
+ self.bf.seek(offset)
+ name = self.read_str()
+ if name.startswith(prefix):
+ basename = name.split(b'/')[-1]
+ padding = b'\0'*(len(name) - len(basename))
+ newname = basename + padding
+ assert(len(newname) == len(name))
+ self.bf.seek(offset)
+ self.bf.write(newname)
+
+ def fix_rpath(self, new_rpath):
+ rp_off = self.get_rpath_offset()
+ if rp_off is None:
+ print('File does not have rpath. It should be a fully static executable.')
+ return
+ self.bf.seek(rp_off)
+ old_rpath = self.read_str()
+ if len(old_rpath) < len(new_rpath):
+ print("New rpath must not be longer than the old one.")
+ self.bf.seek(rp_off)
+ self.bf.write(new_rpath)
+ self.bf.write(b'\0'*(len(old_rpath) - len(new_rpath) + 1))
+ if len(new_rpath) == 0:
+ self.remove_rpath_entry()
+
+ def remove_rpath_entry(self):
+ sec = self.find_section(b'.dynamic')
+ for (i, entry) in enumerate(self.dynamic):
+ if entry.d_tag == DT_RPATH:
+ rpentry = self.dynamic[i]
+ rpentry.d_tag = 0
+ self.dynamic = self.dynamic[:i] + self.dynamic[i+1:] + [rpentry]
+ break;
+ self.bf.seek(sec.sh_offset)
+ for entry in self.dynamic:
+ entry.write(self.bf)
+ return None
+
+def run(args):
+ if len(args) < 1 or len(args) > 2:
+ print('This application resets target rpath.')
+ print('Don\'t run this unless you know what you are doing.')
+ print('%s: <binary file> <prefix>' % sys.argv[0])
+ exit(1)
+ e = Elf(args[0])
+ if len(args) == 1:
+ e.print_rpath()
+ else:
+ new_rpath = args[1]
+ e.fix_rpath(new_rpath.encode('utf8'))
+ return 0
+
+if __name__ == '__main__':
+ run(sys.argv[1:])
diff --git a/mesonbuild/scripts/dirchanger.py b/mesonbuild/scripts/dirchanger.py
new file mode 100644
index 0000000..93a901d
--- /dev/null
+++ b/mesonbuild/scripts/dirchanger.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+# Copyright 2015-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''CD into dir given as first argument and execute
+the command given in the rest of the arguments.'''
+
+import os, subprocess, sys
+
+def run(args):
+ dirname = args[0]
+ command = args[1:]
+
+ os.chdir(dirname)
+ return subprocess.call(command)
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py
new file mode 100644
index 0000000..68be8f2
--- /dev/null
+++ b/mesonbuild/scripts/gtkdochelper.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# Copyright 2015-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, os
+import subprocess
+import shutil
+import argparse
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('--sourcedir', dest='sourcedir')
+parser.add_argument('--builddir', dest='builddir')
+parser.add_argument('--subdir', dest='subdir')
+parser.add_argument('--headerdir', dest='headerdir')
+parser.add_argument('--mainfile', dest='mainfile')
+parser.add_argument('--modulename', dest='modulename')
+parser.add_argument('--htmlargs', dest='htmlargs', default='')
+parser.add_argument('--scanargs', dest='scanargs', default='')
+
+def build_gtkdoc(source_root, build_root, doc_subdir, src_subdir,
+ main_file, module, html_args, scan_args):
+ abs_src = os.path.join(source_root, src_subdir)
+ abs_out = os.path.join(build_root, doc_subdir)
+ htmldir = os.path.join(abs_out, 'html')
+ scan_cmd = ['gtkdoc-scan',
+ '--module=' + module,
+ '--source-dir=' + abs_src] + scan_args
+# print(scan_cmd)
+# sys.exit(1)
+ subprocess.check_call(scan_cmd,
+ cwd=abs_out)
+ if main_file.endswith('sgml'):
+ modeflag = '--sgml-mode'
+ else:
+ modeflag = '--xml-mode'
+ mkdb_cmd = ['gtkdoc-mkdb',
+ '--module=' + module,
+ '--output-format=xml',
+ modeflag,
+ '--source-dir=' + abs_src]
+ main_abs = os.path.join(source_root, doc_subdir, main_file)
+ if len(main_file) > 0:
+ # Yes, this is the flag even if the file is in xml.
+ mkdb_cmd.append('--main-sgml-file=' + main_file)
+# print(mkdb_cmd)
+# sys.exit(1)
+ subprocess.check_call(mkdb_cmd, cwd=abs_out)
+ shutil.rmtree(htmldir, ignore_errors=True)
+ try:
+ os.mkdir(htmldir)
+ except Exception:
+ pass
+ mkhtml_cmd = ['gtkdoc-mkhtml',
+ '--path=' + abs_src,
+ module,
+ ] + html_args
+ if len(main_file) > 0:
+ mkhtml_cmd.append('../' + main_file)
+ else:
+ mkhtml_cmd.append('%s-docs.xml' % module)
+ # html gen must be run in the HTML dir
+# print(mkhtml_cmd)
+# sys.exit(1)
+ subprocess.check_call(mkhtml_cmd, cwd=os.path.join(abs_out, 'html'), shell=False)
+ fixref_cmd = ['gtkdoc-fixxref',
+ '--module=' + module,
+ '--module-dir=html']
+# print(fixref_cmd)
+# sys.exit(1)
+ subprocess.check_call(fixref_cmd, cwd=abs_out)
+
+def install_gtkdoc(build_root, doc_subdir, install_prefix, datadir, module):
+ source = os.path.join(build_root, doc_subdir, 'html')
+ final_destination = os.path.join(install_prefix, datadir, module)
+ shutil.rmtree(final_destination, ignore_errors=True)
+ shutil.copytree(source, final_destination)
+
+def run(args):
+ options = parser.parse_args(args)
+ if len(options.htmlargs) > 0:
+ htmlargs = options.htmlargs.split('@@')
+ else:
+ htmlargs = []
+ if len(options.scanargs) > 0:
+ scanargs = options.scanargs.split('@@')
+ else:
+ scanargs = []
+ build_gtkdoc(options.sourcedir,
+ options.builddir,
+ options.subdir,
+ options.headerdir,
+ options.mainfile,
+ options.modulename,
+ htmlargs,
+ scanargs)
+
+ if 'MESON_INSTALL_PREFIX' in os.environ:
+ if 'DESTDIR' in os.environ:
+ installdir = os.environ['DESTDIR'] + os.environ['MESON_INSTALL_PREFIX']
+ else:
+ installdir = os.environ['MESON_INSTALL_PREFIX']
+ install_gtkdoc(options.builddir,
+ options.subdir,
+ installdir,
+ 'share/gtk-doc/html',
+ options.modulename)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/meson_benchmark.py b/mesonbuild/scripts/meson_benchmark.py
new file mode 100644
index 0000000..26f1f95
--- /dev/null
+++ b/mesonbuild/scripts/meson_benchmark.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+
+# Copyright 2015 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import subprocess, sys, os, argparse
+import pickle, statistics, json
+from . import meson_test
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--wd', default=None, dest='wd',
+ help='directory to cd into before running')
+parser.add_argument('args', nargs='+')
+
+def print_stats(numlen, num_tests, name, res, i, duration, stdev):
+ startpad = ' '*(numlen - len('%d' % (i+1)))
+ num = '%s%d/%d' % (startpad, i+1, num_tests)
+ padding1 = ' '*(38-len(name))
+ padding2 = ' '*(8-len(res))
+ result_str = '%s %s %s%s%s%5.5f s +- %5.5f s' % \
+ (num, name, padding1, res, padding2, duration, stdev)
+ print(result_str)
+# write_json_log(jsonlogfile, name, result)
+
+def print_json_log(jsonlogfile, rawruns, test_name, i):
+ jsonobj = {'name' : test_name}
+ runs = []
+ for r in rawruns:
+ runobj = {'duration': r.duration,
+ 'stdout': r.stdo,
+ 'stderr': r.stde,
+ 'returncode' : r.returncode,
+ 'duration' : r.duration}
+ runs.append(runobj)
+ jsonobj['runs'] = runs
+ jsonlogfile.write(json.dumps(jsonobj) + '\n')
+ jsonlogfile.flush()
+
+def run_benchmarks(options, datafile):
+ failed_tests = 0
+ logfile_base = 'meson-logs/benchmarklog'
+ jsonlogfilename = logfile_base+ '.json'
+ jsonlogfile = open(jsonlogfilename, 'w')
+ tests = pickle.load(open(datafile, 'rb'))
+ num_tests = len(tests)
+ if num_tests == 0:
+ print('No benchmarks defined.')
+ return 0
+ iteration_count = 5
+ wrap = [] # Benchmarks on cross builds are pointless so don't support them.
+ for i, test in enumerate(tests):
+ runs = []
+ durations = []
+ failed = False
+ for _ in range(iteration_count):
+ res = meson_test.run_single_test(wrap, test)
+ runs.append(res)
+ durations.append(res.duration)
+ if res.returncode != 0:
+ failed = True
+ mean = statistics.mean(durations)
+ stddev = statistics.stdev(durations)
+ if failed:
+ resultstr = 'FAIL'
+ failed_tests += 1
+ else:
+ resultstr = 'OK'
+ print_stats(3, num_tests, test.name, resultstr, i, mean, stddev)
+ print_json_log(jsonlogfile, runs, test.name, i)
+ print('\nFull log written to meson-logs/benchmarklog.json.')
+ return failed_tests
+
+def run(args):
+ global failed_tests
+ options = parser.parse_args(args)
+ if len(options.args) != 1:
+ print('Benchmark runner for Meson. Do not run on your own, mmm\'kay?')
+ print('%s [data file]' % sys.argv[0])
+ if options.wd is not None:
+ os.chdir(options.wd)
+ datafile = options.args[0]
+ returncode = run_benchmarks(options, datafile)
+ return returncode
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
new file mode 100644
index 0000000..1ede757
--- /dev/null
+++ b/mesonbuild/scripts/meson_install.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-2014 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, pickle, os, shutil, subprocess, gzip, platform
+from glob import glob
+
+def do_install(datafilename):
+ ifile = open(datafilename, 'rb')
+ d = pickle.load(ifile)
+ destdir_var = 'DESTDIR'
+ if destdir_var in os.environ:
+ d.destdir = os.environ[destdir_var]
+ else:
+ d.destdir = ''
+ d.fullprefix = d.destdir + d.prefix
+
+ install_subdirs(d) # Must be first, because it needs to delete the old subtree.
+ install_targets(d)
+ install_headers(d)
+ install_man(d)
+ install_data(d)
+ install_po(d)
+ run_install_script(d)
+
+def install_subdirs(d):
+ for (src_dir, dst_dir) in d.install_subdirs:
+ if os.path.isabs(dst_dir):
+ dst_dir = d.destdir + dst_dir
+ else:
+ dst_dir = d.fullprefix + dst_dir
+ # Python's copytree works in strange ways.
+ last_level = os.path.split(src_dir)[-1]
+ final_dst = os.path.join(dst_dir, last_level)
+# Don't do rmtree because final_dst might point to e.g. /var/www
+# We might need to revert to walking the directory tree by hand.
+# shutil.rmtree(final_dst, ignore_errors=True)
+ shutil.copytree(src_dir, final_dst, symlinks=True)
+ print('Installing subdir %s to %s.' % (src_dir, dst_dir))
+
+def install_po(d):
+ packagename = d.po_package_name
+ for f in d.po:
+ srcfile = f[0]
+ localedir = f[1]
+ languagename = f[2]
+ outfile = os.path.join(d.fullprefix, localedir, languagename, 'LC_MESSAGES',
+ packagename + '.mo')
+ os.makedirs(os.path.split(outfile)[0], exist_ok=True)
+ shutil.copyfile(srcfile, outfile)
+ shutil.copystat(srcfile, outfile)
+ print('Installing %s to %s.' % (srcfile, outfile))
+
+def install_data(d):
+ for i in d.data:
+ fullfilename = i[0]
+ outfilename = i[1]
+ if os.path.isabs(outfilename):
+ outdir = d.destdir + os.path.split(outfilename)[0]
+ outfilename = d.destdir + outfilename
+ else:
+ outdir = os.path.join(d.fullprefix, os.path.split(outfilename)[0])
+ outfilename = os.path.join(outdir, os.path.split(outfilename)[1])
+ os.makedirs(outdir, exist_ok=True)
+ print('Installing %s to %s.' % (fullfilename, outdir))
+ shutil.copyfile(fullfilename, outfilename)
+ shutil.copystat(fullfilename, outfilename)
+
+def install_man(d):
+ for m in d.man:
+ outfileroot = m[1]
+ outfilename = os.path.join(d.fullprefix, outfileroot)
+ full_source_filename = m[0]
+ outdir = os.path.split(outfilename)[0]
+ os.makedirs(outdir, exist_ok=True)
+ print('Installing %s to %s.' % (full_source_filename, outdir))
+ if outfilename.endswith('.gz') and not full_source_filename.endswith('.gz'):
+ open(outfilename, 'wb').write(gzip.compress(open(full_source_filename, 'rb').read()))
+ else:
+ shutil.copyfile(full_source_filename, outfilename)
+ shutil.copystat(full_source_filename, outfilename)
+
+def install_headers(d):
+ for t in d.headers:
+ fullfilename = t[0]
+ outdir = os.path.join(d.fullprefix, t[1])
+ fname = os.path.split(fullfilename)[1]
+ outfilename = os.path.join(outdir, fname)
+ print('Installing %s to %s' % (fname, outdir))
+ os.makedirs(outdir, exist_ok=True)
+ shutil.copyfile(fullfilename, outfilename)
+ shutil.copystat(fullfilename, outfilename)
+
+def run_install_script(d):
+ env = {'MESON_SOURCE_ROOT' : d.source_dir,
+ 'MESON_BUILD_ROOT' : d.build_dir,
+ 'MESON_INSTALL_PREFIX' : d.prefix
+ }
+ child_env = os.environ.copy()
+ child_env.update(env)
+
+ for i in d.install_scripts:
+ script = i.cmd_arr[0]
+ print('Running custom install script %s' % script)
+ suffix = os.path.splitext(script)[1].lower()
+ if platform.system().lower() == 'windows' and suffix != '.bat':
+ first_line = open(script).readline().strip()
+ if first_line.startswith('#!'):
+ commands = first_line[2:].split('#')[0].strip().split()
+ commands[0] = shutil.which(commands[0].split('/')[-1])
+ if commands[0] is None:
+ raise RuntimeError("Don't know how to run script %s." % script)
+ final_command = commands + [script] + i.cmd_arr[1:]
+ else:
+ final_command = i.cmd_arr
+ subprocess.check_call(final_command, env=child_env)
+
+def is_elf_platform():
+ platname = platform.system().lower()
+ if platname == 'darwin' or platname == 'windows':
+ return False
+ return True
+
+def check_for_stampfile(fname):
+ '''Some languages e.g. Rust have output files
+ whose names are not known at configure time.
+ Check if this is the case and return the real
+ file instead.'''
+ if fname.endswith('.so') or fname.endswith('.dll'):
+ if os.stat(fname).st_size == 0:
+ (base, suffix) = os.path.splitext(fname)
+ files = glob(base + '-*' + suffix)
+ if len(files) > 1:
+ print("Stale dynamic library files in build dir. Can't install.")
+ sys.exit(1)
+ if len(files) == 1:
+ return files[0]
+ elif fname.endswith('.a') or fname.endswith('.lib'):
+ if os.stat(fname).st_size == 0:
+ (base, suffix) = os.path.splitext(fname)
+ files = glob(base + '-*' + '.rlib')
+ if len(files) > 1:
+ print("Stale static library files in build dir. Can't install.")
+ sys.exit(1)
+ if len(files) == 1:
+ return files[0]
+ return fname
+
+def install_targets(d):
+ for t in d.targets:
+ fname = check_for_stampfile(t[0])
+ outdir = os.path.join(d.fullprefix, t[1])
+ aliases = t[2]
+ outname = os.path.join(outdir, os.path.split(fname)[-1])
+ should_strip = t[3]
+ install_rpath = t[4]
+ print('Installing %s to %s' % (fname, outname))
+ os.makedirs(outdir, exist_ok=True)
+ shutil.copyfile(fname, outname)
+ shutil.copystat(fname, outname)
+ if should_strip:
+ print('Stripping target')
+ ps = subprocess.Popen(['strip', outname], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdo, stde) = ps.communicate()
+ if ps.returncode != 0:
+ print('Could not strip file.\n')
+ print('Stdout:\n%s\n' % stdo.decode())
+ print('Stderr:\n%s\n' % stde.decode())
+ sys.exit(1)
+ printed_symlink_error = False
+ for alias in aliases:
+ try:
+ symlinkfilename = os.path.join(outdir, alias)
+ try:
+ os.unlink(symlinkfilename)
+ except FileNotFoundError:
+ pass
+ os.symlink(os.path.split(fname)[-1], symlinkfilename)
+ except NotImplementedError:
+ if not printed_symlink_error:
+ print("Symlink creation does not work on this platform.")
+ printed_symlink_error = True
+ if is_elf_platform():
+ p = subprocess.Popen(d.depfixer + [outname, install_rpath],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (stdo, stde) = p.communicate()
+ if p.returncode != 0:
+ print('Could not fix dependency info.\n')
+ print('Stdout:\n%s\n' % stdo.decode())
+ print('Stderr:\n%s\n' % stde.decode())
+ sys.exit(1)
+
+def run(args):
+ if len(args) != 1:
+ print('Installer script for Meson. Do not run on your own, mmm\'kay?')
+ print('meson_install.py [install info file]')
+ datafilename = args[0]
+ do_install(datafilename)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/meson_test.py b/mesonbuild/scripts/meson_test.py
new file mode 100644
index 0000000..03fd073
--- /dev/null
+++ b/mesonbuild/scripts/meson_test.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mesonbuild
+import sys, os, subprocess, time, datetime, pickle, multiprocessing, json
+import concurrent.futures as conc
+import argparse
+import platform
+
+def is_windows():
+ platname = platform.system().lower()
+ return platname == 'windows' or 'mingw' in platname
+
+tests_failed = []
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--wrapper', default=None, dest='wrapper',
+ help='wrapper to run tests with (e.g. valgrind)')
+parser.add_argument('--wd', default=None, dest='wd',
+ help='directory to cd into before running')
+parser.add_argument('--suite', default=None, dest='suite',
+ help='Only run tests belonging to this suite.')
+parser.add_argument('args', nargs='+')
+
+
+class TestRun():
+ def __init__(self, res, returncode, duration, stdo, stde, cmd):
+ self.res = res
+ self.returncode = returncode
+ self.duration = duration
+ self.stdo = stdo
+ self.stde = stde
+ self.cmd = cmd
+
+def decode(stream):
+ try:
+ return stream.decode('utf-8')
+ except UnicodeDecodeError:
+ return stream.decode('iso-8859-1', errors='ignore')
+
+def write_log(logfile, test_name, result_str, result):
+ logfile.write(result_str + '\n\n')
+ logfile.write('--- command ---\n')
+ if result.cmd is None:
+ logfile.write('NONE')
+ else:
+ logfile.write(' '.join(result.cmd))
+ logfile.write('\n--- "%s" stdout ---\n' % test_name)
+ logfile.write(result.stdo)
+ logfile.write('\n--- "%s" stderr ---\n' % test_name)
+ logfile.write(result.stde)
+ logfile.write('\n-------\n\n')
+
+def write_json_log(jsonlogfile, test_name, result):
+ result = {'name' : test_name,
+ 'stdout' : result.stdo,
+ 'stderr' : result.stde,
+ 'result' : result.res,
+ 'duration' : result.duration,
+ 'returncode' : result.returncode,
+ 'command' : result.cmd}
+ jsonlogfile.write(json.dumps(result) + '\n')
+
+def run_with_mono(fname):
+ if fname.endswith('.exe') and not is_windows():
+ return True
+ return False
+
+def run_single_test(wrap, test):
+ global tests_failed
+ if test.fname[0].endswith('.jar'):
+ cmd = ['java', '-jar'] + test.fname
+ elif not test.is_cross and run_with_mono(test.fname[0]):
+ cmd = ['mono'] + test.fname
+ else:
+ if test.is_cross:
+ if test.exe_runner is None:
+ # Can not run test on cross compiled executable
+ # because there is no execute wrapper.
+ cmd = None
+ else:
+ cmd = [test.exe_runner] + test.fname
+ else:
+ cmd = test.fname
+ if len(wrap) > 0 and 'valgrind' in wrap[0]:
+ wrap += test.valgrind_args
+ if cmd is None:
+ res = 'SKIP'
+ duration = 0.0
+ stdo = 'Not run because can not execute cross compiled binaries.'
+ stde = ''
+ returncode = -1
+ else:
+ cmd = wrap + cmd + test.cmd_args
+ starttime = time.time()
+ child_env = os.environ.copy()
+ child_env.update(test.env)
+ if len(test.extra_paths) > 0:
+ child_env['PATH'] = child_env['PATH'] + ';'.join([''] + test.extra_paths)
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env=child_env, cwd=test.workdir)
+ timed_out = False
+ try:
+ (stdo, stde) = p.communicate(timeout=test.timeout)
+ except subprocess.TimeoutExpired:
+ timed_out = True
+ p.kill()
+ (stdo, stde) = p.communicate()
+ endtime = time.time()
+ duration = endtime - starttime
+ stdo = decode(stdo)
+ stde = decode(stde)
+ if timed_out:
+ res = 'TIMEOUT'
+ tests_failed.append((test.name, stdo, stde))
+ elif (not test.should_fail and p.returncode == 0) or \
+ (test.should_fail and p.returncode != 0):
+ res = 'OK'
+ else:
+ res = 'FAIL'
+ tests_failed.append((test.name, stdo, stde))
+ returncode = p.returncode
+ return TestRun(res, returncode, duration, stdo, stde, cmd)
+
+def print_stats(numlen, tests, name, result, i, logfile, jsonlogfile):
+ startpad = ' '*(numlen - len('%d' % (i+1)))
+ num = '%s%d/%d' % (startpad, i+1, len(tests))
+ padding1 = ' '*(38-len(name))
+ padding2 = ' '*(8-len(result.res))
+ result_str = '%s %s %s%s%s%5.2f s' % \
+ (num, name, padding1, result.res, padding2, result.duration)
+ print(result_str)
+ write_log(logfile, name, result_str, result)
+ write_json_log(jsonlogfile, name, result)
+
+def drain_futures(futures):
+ for i in futures:
+ (result, numlen, tests, name, i, logfile, jsonlogfile) = i
+ print_stats(numlen, tests, name, result.result(), i, logfile, jsonlogfile)
+
+def filter_tests(suite, tests):
+ if suite is None:
+ return tests
+ return [x for x in tests if suite in x.suite]
+
+def run_tests(options, datafilename):
+ logfile_base = 'meson-logs/testlog'
+ if options.wrapper is None:
+ wrap = []
+ logfilename = logfile_base + '.txt'
+ jsonlogfilename = logfile_base+ '.json'
+ else:
+ wrap = [options.wrapper]
+ logfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.txt'
+ jsonlogfilename = logfile_base + '-' + options.wrapper.replace(' ', '_') + '.json'
+ logfile = open(logfilename, 'w')
+ jsonlogfile = open(jsonlogfilename, 'w')
+ logfile.write('Log of Meson test suite run on %s.\n\n' % datetime.datetime.now().isoformat())
+ tests = pickle.load(open(datafilename, 'rb'))
+ if len(tests) == 0:
+ print('No tests defined.')
+ return
+ numlen = len('%d' % len(tests))
+ varname = 'MESON_TESTTHREADS'
+ if varname in os.environ:
+ try:
+ num_workers = int(os.environ[varname])
+ except ValueError:
+ print('Invalid value in %s, using 1 thread.' % varname)
+ num_workers = 1
+ else:
+ num_workers = multiprocessing.cpu_count()
+ executor = conc.ThreadPoolExecutor(max_workers=num_workers)
+ futures = []
+ filtered_tests = filter_tests(options.suite, tests)
+ for i, test in enumerate(filtered_tests):
+ if test.suite[0] == '':
+ visible_name = test.name
+ else:
+ if options.suite is not None:
+ visible_name = options.suite + ' / ' + test.name
+ else:
+ visible_name = test.suite[0] + ' / ' + test.name
+
+ if not test.is_parallel:
+ drain_futures(futures)
+ futures = []
+ res = run_single_test(wrap, test)
+ print_stats(numlen, filtered_tests, visible_name, res, i, logfile, jsonlogfile)
+ else:
+ f = executor.submit(run_single_test, wrap, test)
+ futures.append((f, numlen, filtered_tests, visible_name, i, logfile, jsonlogfile))
+ drain_futures(futures)
+ return logfilename
+
+def run(args):
+ global tests_failed
+ options = parser.parse_args(args)
+ if len(options.args) != 1:
+ print('Test runner for Meson. Do not run on your own, mmm\'kay?')
+ print('%s [data file]' % sys.argv[0])
+ if options.wd is not None:
+ os.chdir(options.wd)
+ datafile = options.args[0]
+ logfilename = run_tests(options, datafile)
+ returncode = 0
+ if len(tests_failed) > 0:
+ print('\nOutput of failed tests (max 10):')
+ for (name, stdo, stde) in tests_failed[:10]:
+ print("{} stdout:\n".format(name))
+ print(stdo)
+ print('\n{} stderr:\n'.format(name))
+ print(stde)
+ print('\n')
+ returncode = 1
+ print('\nFull log written to %s.' % logfilename)
+ return returncode
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py
new file mode 100644
index 0000000..f360a7c
--- /dev/null
+++ b/mesonbuild/scripts/regen_checker.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright 2015-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, os
+import pickle, subprocess
+
+# This could also be used for XCode.
+
+def need_regen(regeninfo):
+ sln_time = os.stat(os.path.join(regeninfo.build_dir, regeninfo.solutionfile)).st_mtime
+ for i in regeninfo.depfiles:
+ curfile = os.path.join(regeninfo.build_dir, i)
+ curtime = os.stat(curfile).st_mtime
+ if curtime > sln_time:
+ return True
+ return False
+
+def regen(regeninfo):
+ scriptdir = os.path.split(__file__)[0]
+ mesonscript = os.path.join(scriptdir, 'meson.py')
+ cmd = [sys.executable, mesonscript, regeninfo.build_dir, regeninfo.source_dir,
+ '--backend=vs2010', 'secret-handshake']
+ subprocess.check_call(cmd)
+
+def run(args):
+ regeninfo = pickle.load(open(os.path.join(args[0], 'regeninfo.dump'), 'rb'))
+ if need_regen(regeninfo):
+ regen(regeninfo)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ run(sys.argv[1:])
diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py
new file mode 100644
index 0000000..79c1264
--- /dev/null
+++ b/mesonbuild/scripts/symbolextractor.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script extracts the symbols of a given shared library
+# into a file. If the symbols have not changed, the file is not
+# touched. This information is used to skip link steps if the
+# ABI has not changed.
+
+# This file is basically a reimplementation of
+# http://cgit.freedesktop.org/libreoffice/core/commit/?id=3213cd54b76bc80a6f0516aac75a48ff3b2ad67c
+
+import sys, subprocess
+from mesonbuild import mesonlib
+import argparse
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('--cross-host', default=None, dest='cross_host',
+ help='cross compilation host platform')
+parser.add_argument('args', nargs='+')
+
+def dummy_syms(outfilename):
+ """Just touch it so relinking happens always."""
+ open(outfilename, 'w').close()
+
+def write_if_changed(text, outfilename):
+ try:
+ oldtext = open(outfilename, 'r').read()
+ if text == oldtext:
+ return
+ except FileNotFoundError:
+ pass
+ open(outfilename, 'w').write(text)
+
+def linux_syms(libfilename, outfilename):
+ pe = subprocess.Popen(['readelf', '-d', libfilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = pe.communicate()[0].decode()
+ if pe.returncode != 0:
+ raise RuntimeError('Readelf does not work')
+ result = [x for x in output.split('\n') if 'SONAME' in x]
+ assert(len(result) <= 1)
+ pnm = subprocess.Popen(['nm', '--dynamic', '--extern-only', '--defined-only', '--format=posix', libfilename],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = pnm.communicate()[0].decode()
+ if pnm.returncode != 0:
+ raise RuntimeError('nm does not work.')
+ result += [' '.join(x.split()[0:2]) for x in output.split('\n') if len(x) > 0]
+ write_if_changed('\n'.join(result) + '\n', outfilename)
+
+def osx_syms(libfilename, outfilename):
+ pe = subprocess.Popen(['otool', '-l', libfilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = pe.communicate()[0].decode()
+ if pe.returncode != 0:
+ raise RuntimeError('Otool does not work.')
+ arr = output.split('\n')
+ for (i, val) in enumerate(arr):
+ if 'LC_ID_DYLIB' in val:
+ match = i
+ break
+ result = [arr[match+2], arr[match+5]] # Libreoffice stores all 5 lines but the others seem irrelevant.
+ pnm = subprocess.Popen(['nm', '-g', '-P', libfilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = pnm.communicate()[0].decode()
+ if pnm.returncode != 0:
+ raise RuntimeError('nm does not work.')
+ result += [' '.join(x.split()[0:2]) for x in output.split('\n') if len(x) > 0 and not x.endswith('U')]
+ write_if_changed('\n'.join(result) + '\n', outfilename)
+
+def gen_symbols(libfilename, outfilename, cross_host):
+ if cross_host is not None:
+ # In case of cross builds just always relink.
+ # In theory we could determine the correct
+ # toolset but there are more important things
+ # to do.
+ dummy_syms(outfilename)
+ elif mesonlib.is_linux():
+ linux_syms(libfilename, outfilename)
+ elif mesonlib.is_osx():
+ osx_syms(libfilename, outfilename)
+ else:
+ dummy_syms(outfilename)
+
+def run(args):
+ options = parser.parse_args(args)
+ if len(options.args) != 2:
+ print('symbolextractor.py <shared library file> <output file>')
+ sys.exit(1)
+ libfile = options.args[0]
+ outfile = options.args[1]
+ gen_symbols(libfile, outfile, options.cross_host)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/scripts/vcstagger.py b/mesonbuild/scripts/vcstagger.py
new file mode 100644
index 0000000..390e37a
--- /dev/null
+++ b/mesonbuild/scripts/vcstagger.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# Copyright 2015-2016 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, os, subprocess, re
+
+def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, cmd):
+ try:
+ output = subprocess.check_output(cmd, cwd=source_dir)
+ new_string = re.search(regex_selector, output.decode()).group(1).strip()
+ except Exception:
+ new_string = fallback
+
+ new_data = open(infile).read().replace(replace_string, new_string)
+ if (not os.path.exists(outfile)) or (open(outfile).read() != new_data):
+ open(outfile, 'w').write(new_data)
+
+def run(args):
+ infile, outfile, fallback, source_dir, replace_string, regex_selector = args[0:6]
+ command = args[6:]
+ config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command)
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))