aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
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
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')
-rw-r--r--mesonbuild/__init__.py0
-rw-r--r--mesonbuild/backends.py423
-rw-r--r--mesonbuild/build.py969
-rw-r--r--mesonbuild/compilers.py1837
-rw-r--r--mesonbuild/coredata.py222
-rw-r--r--mesonbuild/dependencies.py1120
-rw-r--r--mesonbuild/environment.py673
-rw-r--r--mesonbuild/interpreter.py2259
-rw-r--r--mesonbuild/mconf.py209
-rw-r--r--mesonbuild/mesonlib.py284
-rw-r--r--mesonbuild/mesonmain.py262
-rw-r--r--mesonbuild/mesonmain.ui248
-rw-r--r--mesonbuild/mesonrunner.ui52
-rw-r--r--mesonbuild/mesonstart.ui119
-rw-r--r--mesonbuild/mgui.py565
-rw-r--r--mesonbuild/mintro.py212
-rw-r--r--mesonbuild/mlog.py81
-rw-r--r--mesonbuild/modules/gnome.py330
-rw-r--r--mesonbuild/modules/modtest.py21
-rw-r--r--mesonbuild/modules/pkgconfig.py82
-rw-r--r--mesonbuild/modules/qt4.py155
-rw-r--r--mesonbuild/modules/qt5.py162
-rw-r--r--mesonbuild/modules/rpm.py163
-rw-r--r--mesonbuild/modules/windows.py47
-rw-r--r--mesonbuild/mparser.py565
-rw-r--r--mesonbuild/ninjabackend.py1819
-rw-r--r--mesonbuild/optinterpreter.py148
-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
-rw-r--r--mesonbuild/vs2010backend.py640
-rw-r--r--mesonbuild/wrap/wrap.py212
-rwxr-xr-xmesonbuild/wrap/wraptool.py200
-rw-r--r--mesonbuild/xcodebackend.py775
42 files changed, 16136 insertions, 0 deletions
diff --git a/mesonbuild/__init__.py b/mesonbuild/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mesonbuild/__init__.py
diff --git a/mesonbuild/backends.py b/mesonbuild/backends.py
new file mode 100644
index 0000000..c583a7b
--- /dev/null
+++ b/mesonbuild/backends.py
@@ -0,0 +1,423 @@
+# Copyright 2012-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 os, pickle, re
+from . import build
+from . import dependencies
+from . import mesonlib
+import json
+from .coredata import MesonException
+
+class InstallData():
+ def __init__(self, source_dir, build_dir, prefix, depfixer):
+ self.source_dir = source_dir
+ self.build_dir= build_dir
+ self.prefix = prefix
+ self.targets = []
+ self.depfixer = depfixer
+ self.headers = []
+ self.man = []
+ self.data = []
+ self.po_package_name = ''
+ self.po = []
+ self.install_scripts = []
+ self.install_subdirs = []
+
+class TestSerialisation:
+ def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env,
+ should_fail, valgrind_args, timeout, workdir, extra_paths):
+ self.name = name
+ self.suite = suite
+ self.fname = fname
+ self.is_cross = is_cross
+ self.exe_runner = exe_wrapper
+ self.is_parallel = is_parallel
+ self.cmd_args = cmd_args
+ self.env = env
+ self.should_fail = should_fail
+ self.valgrind_args = valgrind_args
+ self.timeout = timeout
+ self.workdir = workdir
+ self.extra_paths = extra_paths
+
+# This class contains the basic functionality that is needed by all backends.
+# Feel free to move stuff in and out of it as you see fit.
+class Backend():
+ def __init__(self, build):
+ self.build = build
+ self.environment = build.environment
+ self.processed_targets = {}
+ self.dep_rules = {}
+ self.build_to_src = os.path.relpath(self.environment.get_source_dir(),
+ self.environment.get_build_dir())
+ for t in self.build.targets:
+ priv_dirname = self.get_target_private_dir_abs(t)
+ os.makedirs(priv_dirname, exist_ok=True)
+
+ def get_compiler_for_lang(self, lang):
+ for i in self.build.compilers:
+ if i.language == lang:
+ return i
+ raise RuntimeError('No compiler for language ' + lang)
+
+ def get_compiler_for_source(self, src):
+ for i in self.build.compilers:
+ if i.can_compile(src):
+ return i
+ if isinstance(src, mesonlib.File):
+ src = src.fname
+ raise RuntimeError('No specified compiler can handle file ' + src)
+
+ def get_target_filename(self, target):
+ targetdir = self.get_target_dir(target)
+ fname = target.get_filename()
+ if isinstance(fname, list):
+ fname = fname[0] # HORROR, HORROR! Fix this.
+ filename = os.path.join(targetdir, fname)
+ return filename
+
+ def get_target_dir(self, target):
+ if self.environment.coredata.get_builtin_option('layout') == 'mirror':
+ dirname = target.get_subdir()
+ else:
+ dirname = 'meson-out'
+ return dirname
+
+ def get_target_private_dir(self, target):
+ dirname = os.path.join(self.get_target_dir(target), target.get_basename() + target.type_suffix())
+ return dirname
+
+ def get_target_private_dir_abs(self, target):
+ dirname = os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target))
+ return dirname
+
+ def generate_unity_files(self, target, unity_src):
+ langlist = {}
+ abs_files = []
+ result = []
+ for src in unity_src:
+ comp = self.get_compiler_for_source(src)
+ language = comp.get_language()
+ suffix = '.' + comp.get_default_suffix()
+ if language not in langlist:
+ outfilename = os.path.join(self.get_target_private_dir_abs(target), target.name + '-unity' + suffix)
+ outfileabs = os.path.join(self.environment.get_build_dir(), outfilename)
+ outfileabs_tmp = outfileabs + '.tmp'
+ abs_files.append(outfileabs)
+ outfile = open(outfileabs_tmp, 'w')
+ langlist[language] = outfile
+ result.append(outfilename)
+ ofile = langlist[language]
+ ofile.write('#include<%s>\n' % src)
+ [x.close() for x in langlist.values()]
+ [mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files]
+ return result
+
+ def relpath(self, todir, fromdir):
+ return os.path.relpath(os.path.join('dummyprefixdir', todir),\
+ os.path.join('dummyprefixdir', fromdir))
+
+ def flatten_object_list(self, target, proj_dir_to_build_root=''):
+ obj_list = []
+ for obj in target.get_objects():
+ if isinstance(obj, str):
+ o = os.path.join(proj_dir_to_build_root,
+ self.build_to_src, target.get_subdir(), obj)
+ obj_list.append(o)
+ elif isinstance(obj, build.ExtractedObjects):
+ obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root)
+ else:
+ raise MesonException('Unknown data type in object list.')
+ return obj_list
+
+ def serialise_tests(self):
+ test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
+ datafile = open(test_data, 'wb')
+ self.write_test_file(datafile)
+ datafile.close()
+ benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat')
+ datafile = open(benchmark_data, 'wb')
+ self.write_benchmark_file(datafile)
+ datafile.close()
+
+ def has_source_suffix(self, target, suffix):
+ for s in target.get_sources():
+ if s.endswith(suffix):
+ return True
+ return False
+
+ def has_vala(self, target):
+ return self.has_source_suffix(target, '.vala')
+
+ def has_rust(self, target):
+ return self.has_source_suffix(target, '.rs')
+
+ def has_cs(self, target):
+ return self.has_source_suffix(target, '.cs')
+
+ def has_swift(self, target):
+ return self.has_source_suffix(target, '.swift')
+
+ def determine_linker(self, target, src):
+ if isinstance(target, build.StaticLibrary):
+ return self.build.static_linker
+ if len(self.build.compilers) == 1:
+ return self.build.compilers[0]
+ # Currently a bit naive. C++ must
+ # be linked with a C++ compiler, but
+ # otherwise we don't care. This will
+ # become trickier if and when Fortran
+ # and the like become supported.
+ cpp = None
+ for c in self.build.compilers:
+ if c.get_language() == 'cpp':
+ cpp = c
+ break
+ if cpp is not None:
+ for s in src:
+ if c.can_compile(s):
+ return cpp
+ for c in self.build.compilers:
+ if c.get_language() != 'vala':
+ return c
+ raise RuntimeError('Unreachable code')
+
+ def determine_ext_objs(self, extobj, proj_dir_to_build_root=''):
+ result = []
+ targetdir = self.get_target_private_dir(extobj.target)
+ suffix = '.' + self.environment.get_object_suffix()
+ for osrc in extobj.srclist:
+ osrc_base = osrc.fname
+ if not self.source_suffix_in_objs:
+ osrc_base = '.'.join(osrc.split('.')[:-1])
+ # If extracting in a subproject, the subproject
+ # name gets duplicated in the file name.
+ pathsegs = osrc.subdir.split(os.sep)
+ if pathsegs[0] == 'subprojects':
+ pathsegs = pathsegs[2:]
+ fixedpath = os.sep.join(pathsegs)
+ objbase = osrc.fname.replace('/', '_').replace('\\', '_')
+ objname = os.path.join(proj_dir_to_build_root,
+ targetdir, os.path.basename(objbase) + suffix)
+ result.append(objname)
+ return result
+
+ def get_pch_include_args(self, compiler, target):
+ args = []
+ pchpath = self.get_target_private_dir(target)
+ includeargs = compiler.get_include_args(pchpath, False)
+ for lang in ['c', 'cpp']:
+ p = target.get_pch(lang)
+ if len(p) == 0:
+ continue
+ if compiler.can_compile(p[-1]):
+ header = p[0]
+ args += compiler.get_pch_use_args(pchpath, header)
+ if len(args) > 0:
+ args = includeargs + args
+ return args
+
+ def generate_basic_compiler_args(self, target, compiler):
+ commands = []
+ commands += compiler.get_always_args()
+ if self.environment.coredata.get_builtin_option('buildtype') != 'plain':
+ commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level'))
+ commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options)
+ commands += self.build.get_global_args(compiler)
+ commands += self.environment.coredata.external_args[compiler.get_language()]
+ commands += target.get_extra_args(compiler.get_language())
+ commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ if self.environment.coredata.get_builtin_option('coverage'):
+ commands += compiler.get_coverage_args()
+ if self.environment.coredata.get_builtin_option('werror'):
+ commands += compiler.get_werror_args()
+ if isinstance(target, build.SharedLibrary):
+ commands += compiler.get_pic_args()
+ for dep in target.get_external_deps():
+ commands += dep.get_compile_args()
+ if isinstance(target, build.Executable):
+ commands += dep.get_exe_args()
+
+ # Fortran requires extra include directives.
+ if compiler.language == 'fortran':
+ for lt in target.link_targets:
+ priv_dir = os.path.join(self.get_target_dir(lt), lt.get_basename() + lt.type_suffix())
+ incflag = compiler.get_include_args(priv_dir, False)
+ commands += incflag
+ return commands
+
+ def build_target_link_arguments(self, compiler, deps):
+ args = []
+ for d in deps:
+ if not isinstance(d, build.StaticLibrary) and\
+ not isinstance(d, build.SharedLibrary):
+ raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename())
+ fname = self.get_target_filename(d)
+ if compiler.id == 'msvc':
+ if fname.endswith('dll'):
+ fname = fname[:-3] + 'lib'
+ args.append(fname)
+ # If you have executable e that links to shared lib s1 that links to shared library s2
+ # you have to specify s2 as well as s1 when linking e even if e does not directly use
+ # s2. Gcc handles this case fine but Clang does not for some reason. Thus we need to
+ # explictly specify all libraries every time.
+ args += self.build_target_link_arguments(compiler, d.get_dependencies())
+ return args
+
+ def determine_windows_extra_paths(self, target):
+ '''On Windows there is no such thing as an rpath.
+ We must determine all locations of DLLs that this exe
+ links to and return them so they can be used in unit
+ tests.'''
+ if not isinstance(target, build.Executable):
+ return []
+ prospectives = target.get_transitive_link_deps()
+ result = []
+ for ld in prospectives:
+ if ld == '' or ld == '.':
+ continue
+ dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld))
+ if dirseg not in result:
+ result.append(dirseg)
+ return result
+
+ def write_benchmark_file(self, datafile):
+ self.write_test_serialisation(self.build.get_benchmarks(), datafile)
+
+ def write_test_file(self, datafile):
+ self.write_test_serialisation(self.build.get_tests(), datafile)
+
+ def write_test_serialisation(self, tests, datafile):
+ arr = []
+ for t in tests:
+ exe = t.get_exe()
+ if isinstance(exe, dependencies.ExternalProgram):
+ fname = exe.fullpath
+ else:
+ fname = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))]
+ is_cross = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler()
+ if is_cross:
+ exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None)
+ else:
+ exe_wrapper = None
+ if mesonlib.is_windows():
+ extra_paths = self.determine_windows_extra_paths(exe)
+ else:
+ extra_paths = []
+ cmd_args = []
+ for a in t.cmd_args:
+ if isinstance(a, mesonlib.File):
+ a = os.path.join(self.environment.get_build_dir(), a.rel_to_builddir(self.build_to_src))
+ cmd_args.append(a)
+ ts = TestSerialisation(t.get_name(), t.suite, fname, is_cross, exe_wrapper,
+ t.is_parallel, cmd_args, t.env, t.should_fail, t.valgrind_args,
+ t.timeout, t.workdir, extra_paths)
+ arr.append(ts)
+ pickle.dump(arr, datafile)
+
+
+ def generate_depmf_install(self, d):
+ if self.build.dep_manifest_name is None:
+ return
+ ifilename = os.path.join(self.environment.get_build_dir(), 'depmf.json')
+ ofilename = os.path.join(self.environment.get_prefix(), self.build.dep_manifest_name)
+ mfobj = {'type': 'dependency manifest',
+ 'version': '1.0'}
+ mfobj['projects'] = self.build.dep_manifest
+ open(ifilename, 'w').write(json.dumps(mfobj))
+ d.data.append([ifilename, ofilename])
+
+ def get_regen_filelist(self):
+ '''List of all files whose alteration means that the build
+ definition needs to be regenerated.'''
+ deps = [os.path.join(self.build_to_src, df) \
+ for df in self.interpreter.get_build_def_files()]
+ if self.environment.is_cross_build():
+ deps.append(os.path.join(self.build_to_src,
+ self.environment.coredata.cross_file))
+ deps.append('meson-private/coredata.dat')
+ if os.path.exists(os.path.join(self.environment.get_source_dir(), 'meson_options.txt')):
+ deps.append(os.path.join(self.build_to_src, 'meson_options.txt'))
+ for sp in self.build.subprojects.keys():
+ fname = os.path.join(self.environment.get_source_dir(), sp, 'meson_options.txt')
+ if os.path.isfile(fname):
+ deps.append(os.path.join(self.build_to_src, sp, 'meson_options.txt'))
+ return deps
+
+ def exe_object_to_cmd_array(self, exe):
+ if self.environment.is_cross_build() and \
+ isinstance(exe, build.BuildTarget) and exe.is_cross:
+ if 'exe_wrapper' not in self.environment.cross_info:
+ s = 'Can not use target %s as a generator because it is cross-built\n'
+ s += 'and no exe wrapper is defined. You might want to set it to native instead.'
+ s = s % exe.name
+ raise MesonException(s)
+ if isinstance(exe, build.BuildTarget):
+ exe_arr = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe))]
+ else:
+ exe_arr = exe.get_command()
+ return exe_arr
+
+ def eval_custom_target_command(self, target, absolute_paths=False):
+ ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output]
+ srcs = []
+ outdir = self.get_target_dir(target)
+ # Many external programs fail on empty arguments.
+ if outdir == '':
+ outdir = '.'
+ if absolute_paths:
+ outdir = os.path.join(self.environment.get_build_dir(), outdir)
+ for i in target.sources:
+ if isinstance(i, str):
+ fname = os.path.join(self.build_to_src, target.subdir, i)
+ else:
+ fname = i.rel_to_builddir(self.build_to_src)
+ if absolute_paths:
+ fname = os.path.join(self.environment.get_build_dir(), fname)
+ srcs.append(fname)
+ cmd = []
+ for i in target.command:
+ if isinstance(i, build.Executable):
+ cmd += self.exe_object_to_cmd_array(i)
+ continue
+ if isinstance(i, build.CustomTarget):
+ # GIR scanner will attempt to execute this binary but
+ # it assumes that it is in path, so always give it a full path.
+ tmp = i.get_filename()[0]
+ i = os.path.join(self.get_target_dir(i), tmp)
+ for (j, src) in enumerate(srcs):
+ i = i.replace('@INPUT%d@' % j, src)
+ for (j, res) in enumerate(ofilenames):
+ i = i.replace('@OUTPUT%d@' % j, res)
+ if i == '@INPUT@':
+ cmd += srcs
+ elif i == '@OUTPUT@':
+ cmd += ofilenames
+ else:
+ if '@OUTDIR@' in i:
+ i = i.replace('@OUTDIR@', outdir)
+ elif '@PRIVATE_OUTDIR_' in i:
+ match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i)
+ source = match.group(0)
+ if match.group(1) is None and not absolute_paths:
+ lead_dir = ''
+ else:
+ lead_dir = self.environment.get_build_dir()
+ target_id = match.group(2)
+ i = i.replace(source,
+ os.path.join(lead_dir,
+ outdir))
+ cmd.append(i)
+ cmd = [i.replace('\\', '/') for i in cmd]
+ return (srcs, ofilenames, cmd)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
new file mode 100644
index 0000000..c0ba895
--- /dev/null
+++ b/mesonbuild/build.py
@@ -0,0 +1,969 @@
+# Copyright 2012-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.
+
+from . import coredata
+from . import environment
+from . import dependencies
+from . import mlog
+import copy, os
+from .mesonlib import File, flatten
+
+known_basic_kwargs = {'install' : True,
+ 'c_pch' : True,
+ 'cpp_pch' : True,
+ 'c_args' : True,
+ 'cpp_args' : True,
+ 'cs_args' : True,
+ 'vala_args' : True,
+ 'link_args' : True,
+ 'link_depends': True,
+ 'link_with' : True,
+ 'include_directories': True,
+ 'dependencies' : True,
+ 'install_dir' : True,
+ 'main_class' : True,
+ 'gui_app' : True,
+ 'extra_files' : True,
+ 'install_rpath' : True,
+ 'resources' : True,
+ 'sources' : True,
+ 'objects' : True,
+ 'native' : True,
+ }
+
+known_shlib_kwargs = known_basic_kwargs.copy()
+known_shlib_kwargs.update({'version' : True,
+ 'soversion' : True})
+
+backslash_explanation = \
+'''Compiler arguments have a backslash "\\" character. This is unfortunately not
+permitted. The reason for this is that backslash is a shell quoting character
+that behaves differently across different systems. Because of this is it not
+possible to make it work reliably across all the platforms Meson needs to
+support.
+
+There are several different ways of working around this issue. Most of the time
+you are using this to provide a -D define to your compiler. Try instead to
+create a config.h file and put all of your definitions in it using
+configure_file().
+
+Another approach is to move the backslashes into the source and have the other
+bits in the def. So you would have an arg -DPLAIN_TEXT="foo" and then in your
+C sources something like this:
+
+const char *fulltext = "\\\\" PLAIN_TEXT;
+
+We are fully aware that these are not really usable or pleasant ways to do
+this but it's the best we can do given the way shell quoting works.
+'''
+
+class InvalidArguments(coredata.MesonException):
+ pass
+
+class Build:
+ """A class that holds the status of one build including
+ all dependencies and so on.
+ """
+
+ def __init__(self, environment):
+ self.project_name = 'name of master project'
+ self.project_version = None
+ self.environment = environment
+ self.projects = {}
+ self.targets = {}
+ self.compilers = []
+ self.cross_compilers = []
+ self.global_args = {}
+ self.tests = []
+ self.benchmarks = []
+ self.headers = []
+ self.man = []
+ self.data = []
+ self.static_linker = None
+ self.static_cross_linker = None
+ self.pot = []
+ self.subprojects = {}
+ self.install_scripts = []
+ self.install_dirs = []
+ self.dep_manifest_name = None
+ self.dep_manifest = {}
+
+ def has_language(self, language):
+ for i in self.compilers:
+ if i.get_language() == language:
+ return True
+ return False
+
+ def add_compiler(self, compiler):
+ if self.static_linker is None and compiler.needs_static_linker():
+ self.static_linker = self.environment.detect_static_linker(compiler)
+ if self.has_language(compiler.get_language()):
+ return
+ self.compilers.append(compiler)
+
+ def add_cross_compiler(self, compiler):
+ if len(self.cross_compilers) == 0:
+ self.static_cross_linker = self.environment.detect_static_linker(compiler)
+ for i in self.cross_compilers:
+ if i.get_language() == compiler.get_language():
+ return
+ self.cross_compilers.append(compiler)
+
+ def get_project(self):
+ return self.projects['']
+
+ def get_targets(self):
+ return self.targets
+
+ def get_tests(self):
+ return self.tests
+
+ def get_benchmarks(self):
+ return self.benchmarks
+
+ def get_headers(self):
+ return self.headers
+
+ def get_man(self):
+ return self.man
+
+ def get_data(self):
+ return self.data
+
+ def get_install_subdirs(self):
+ return self.install_dirs
+
+ def get_global_args(self, compiler):
+ return self.global_args.get(compiler.get_language(), [])
+
+class IncludeDirs():
+ def __init__(self, curdir, dirs, is_system, extra_build_dirs=None):
+ self.curdir = curdir
+ self.incdirs = dirs
+ self.is_system = is_system
+ # Interpreter has validated that all given directories
+ # actually exist.
+ if extra_build_dirs is None:
+ self.extra_build_dirs = []
+ else:
+ self.extra_build_dirs = extra_build_dirs
+
+ def get_curdir(self):
+ return self.curdir
+
+ def get_incdirs(self):
+ return self.incdirs
+
+ def get_extra_build_dirs(self):
+ return self.extra_build_dirs
+
+class ExtractedObjects():
+ def __init__(self, target, srclist):
+ self.target = target
+ self.srclist = srclist
+
+class BuildTarget():
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ self.name = name
+ self.subdir = subdir
+ self.subproject = subproject # Can not be calculated from subdir as subproject dirname can be changed per project.
+ self.is_cross = is_cross
+ self.sources = []
+ self.objects = []
+ self.external_deps = []
+ self.include_dirs = []
+ self.link_targets = []
+ self.link_depends = []
+ self.filename = 'no_name'
+ self.need_install = False
+ self.pch = {}
+ self.extra_args = {}
+ self.generated = []
+ self.extra_files = []
+ self.process_sourcelist(sources)
+ self.process_objectlist(objects)
+ self.process_kwargs(kwargs, environment)
+ self.check_unknown_kwargs(kwargs)
+ if len(self.sources) == 0 and \
+ len(self.generated) == 0 and \
+ len(self.objects) == 0:
+ raise InvalidArguments('Build target %s has no sources.' % name)
+ self.validate_sources()
+
+ def get_id(self):
+ # This ID must also be a valid file name on all OSs.
+ # It should also avoid shell metacharacters for obvious
+ # reasons.
+ base = self.name + self.type_suffix()
+ if self.subproject == '':
+ return base
+ return self.subproject + '@@' + base
+
+ def check_unknown_kwargs(self, kwargs):
+ # Override this method in derived classes that have more
+ # keywords.
+ self.check_unknown_kwargs_int(kwargs, known_basic_kwargs)
+
+ def check_unknown_kwargs_int(self, kwargs, known_kwargs):
+ unknowns = []
+ for k in kwargs:
+ if not k in known_kwargs:
+ unknowns.append(k)
+ if len(unknowns) > 0:
+ mlog.log(mlog.bold('Warning:'), 'Unknown keyword argument(s) in target %s: %s.' %
+ (self.name, ', '.join(unknowns)))
+
+ def process_objectlist(self, objects):
+ assert(isinstance(objects, list))
+ for s in objects:
+ if hasattr(s, 'held_object'):
+ s = s.held_object
+ if isinstance(s, str):
+ self.objects.append(s)
+ elif isinstance(s, ExtractedObjects):
+ self.objects.append(s)
+ else:
+ raise InvalidArguments('Bad object in target %s.' % self.name)
+
+ def process_sourcelist(self, sources):
+ if not isinstance(sources, list):
+ sources = [sources]
+ added_sources = {} # If the same source is defined multiple times, use it only once.
+ for s in sources:
+ # Holder unpacking. Ugly.
+ if hasattr(s, 'held_object'):
+ s = s.held_object
+ if isinstance(s, File):
+ if not s in added_sources:
+ self.sources.append(s)
+ added_sources[s] = True
+ elif isinstance(s, GeneratedList) or isinstance(s, CustomTarget):
+ self.generated.append(s)
+ else:
+ raise InvalidArguments('Bad source in target %s.' % self.name)
+
+ def validate_sources(self):
+ if len(self.sources) > 0:
+ firstname = self.sources[0]
+ if isinstance(firstname, File):
+ firstname = firstname.fname
+ first = os.path.split(firstname)[1]
+ (base, suffix) = os.path.splitext(first)
+ if suffix == '.rs':
+ if self.name != base:
+ raise InvalidArguments('In Rust targets, the first source file must be named projectname.rs.')
+
+ def get_original_kwargs(self):
+ return self.kwargs
+
+ def unpack_holder(self, d):
+ if not isinstance(d, list):
+ d = [d]
+ newd = []
+ for i in d:
+ if hasattr(i, 'held_object'):
+ newd.append(i.held_object)
+ else:
+ newd.append(i)
+ return newd
+
+ def copy_kwargs(self, kwargs):
+ self.kwargs = copy.copy(kwargs)
+ # This sucks quite badly. Arguments
+ # are holders but they can't be pickled
+ # so unpack those known.
+ if 'dependencies' in self.kwargs:
+ self.kwargs['dependencies'] = self.unpack_holder(self.kwargs['dependencies'])
+ if 'link_with' in self.kwargs:
+ self.kwargs['link_with'] = self.unpack_holder(self.kwargs['link_with'])
+
+ def extract_objects(self, srcargs):
+ obj_src = []
+ for srclist in srcargs:
+ if not isinstance(srclist, list):
+ srclist = [srclist]
+ for src in srclist:
+ if not isinstance(src, str):
+ raise coredata.MesonException('Extraction arguments must be strings.')
+ src = File(False, self.subdir, src)
+ if src not in self.sources:
+ raise coredata.MesonException('Tried to extract unknown source %s.' % src)
+ obj_src.append(src)
+ return ExtractedObjects(self, obj_src)
+
+ def extract_all_objects(self):
+ return ExtractedObjects(self, self.sources)
+
+ def get_all_link_deps(self):
+ return self.get_transitive_link_deps()
+
+ def get_transitive_link_deps(self):
+ result = []
+ for i in self.link_targets:
+ result += i.get_all_link_deps()
+ return result
+
+ def get_custom_install_dir(self):
+ return self.custom_install_dir
+
+ def process_kwargs(self, kwargs, environment):
+ self.copy_kwargs(kwargs)
+ kwargs.get('modules', [])
+ self.need_install = kwargs.get('install', self.need_install)
+ llist = kwargs.get('link_with', [])
+ if not isinstance(llist, list):
+ llist = [llist]
+ for linktarget in llist:
+ # Sorry for this hack. Keyword targets are kept in holders
+ # in kwargs. Unpack here without looking at the exact type.
+ if hasattr(linktarget, "held_object"):
+ linktarget = linktarget.held_object
+ self.link(linktarget)
+ c_pchlist = kwargs.get('c_pch', [])
+ if not isinstance(c_pchlist, list):
+ c_pchlist = [c_pchlist]
+ self.add_pch('c', c_pchlist)
+ cpp_pchlist = kwargs.get('cpp_pch', [])
+ if not isinstance(cpp_pchlist, list):
+ cpp_pchlist = [cpp_pchlist]
+ self.add_pch('cpp', cpp_pchlist)
+ clist = kwargs.get('c_args', [])
+ if not isinstance(clist, list):
+ clist = [clist]
+ self.add_compiler_args('c', clist)
+ cpplist = kwargs.get('cpp_args', [])
+ if not isinstance(cpplist, list):
+ cpplist = [cpplist]
+ self.add_compiler_args('cpp', cpplist)
+ cslist = kwargs.get('cs_args', [])
+ if not isinstance(cslist, list):
+ cslist = [cslist]
+ self.add_compiler_args('cs', cslist)
+ valalist = kwargs.get('vala_args', [])
+ if not isinstance(valalist, list):
+ valalist = [valalist]
+ self.add_compiler_args('vala', valalist)
+ self.link_args = kwargs.get('link_args', [])
+ if not isinstance(self.link_args, list):
+ self.link_args = [self.link_args]
+ for i in self.link_args:
+ if not isinstance(i, str):
+ raise InvalidArguments('Link_args arguments must be strings.')
+ self.link_depends = kwargs.get('link_depends', [])
+ if not isinstance(self.link_depends, list):
+ self.link_depends = [self.link_depends]
+ for i in self.link_depends:
+ if not isinstance(i, str):
+ raise InvalidArguments('Link_depends arguments must be strings.')
+ inclist = kwargs.get('include_directories', [])
+ if not isinstance(inclist, list):
+ inclist = [inclist]
+ self.add_include_dirs(inclist)
+ deplist = kwargs.get('dependencies', [])
+ if not isinstance(deplist, list):
+ deplist = [deplist]
+ self.add_external_deps(deplist)
+ self.custom_install_dir = kwargs.get('install_dir', None)
+ if self.custom_install_dir is not None:
+ if not isinstance(self.custom_install_dir, str):
+ raise InvalidArguments('Custom_install_dir must be a string')
+ main_class = kwargs.get('main_class', '')
+ if not isinstance(main_class, str):
+ raise InvalidArguments('Main class must be a string')
+ self.main_class = main_class
+ if isinstance(self, Executable):
+ self.gui_app = kwargs.get('gui_app', False)
+ if not isinstance(self.gui_app, bool):
+ raise InvalidArguments('Argument gui_app must be boolean.')
+ elif 'gui_app' in kwargs:
+ raise InvalidArguments('Argument gui_app can only be used on executables.')
+ extra_files = kwargs.get('extra_files', [])
+ if isinstance(extra_files, str):
+ extra_files = [extra_files]
+ for i in extra_files:
+ if not isinstance(i, str):
+ raise InvalidArguments('Arguments to extra_files must be strings.')
+ trial = os.path.join(environment.get_source_dir(), self.subdir, i)
+ if not(os.path.isfile(trial)):
+ raise InvalidArguments('Tried to add non-existing extra file %s.' % i)
+ self.extra_files = extra_files
+ self.install_rpath = kwargs.get('install_rpath', '')
+ if not isinstance(self.install_rpath, str):
+ raise InvalidArguments('Install_rpath is not a string.')
+ resources = kwargs.get('resources', [])
+ if not isinstance(resources, list):
+ resources = [resources]
+ for r in resources:
+ if not isinstance(r, str):
+ raise InvalidArguments('Resource argument is not a string.')
+ trial = os.path.join(environment.get_source_dir(), self.subdir, r)
+ if not os.path.isfile(trial):
+ raise InvalidArguments('Tried to add non-existing resource %s.' % r)
+ self.resources = resources
+
+ def get_subdir(self):
+ return self.subdir
+
+ def get_filename(self):
+ return self.filename
+
+ def get_extra_args(self, language):
+ return self.extra_args.get(language, [])
+
+ def get_dependencies(self):
+ transitive_deps = []
+ for t in self.link_targets:
+ transitive_deps.append(t)
+ if isinstance(t, StaticLibrary):
+ transitive_deps += t.get_dependencies()
+ return transitive_deps
+
+ def get_basename(self):
+ return self.name
+
+ def get_source_subdir(self):
+ return self.subdir
+
+ def get_sources(self):
+ return self.sources
+
+ def get_objects(self):
+ return self.objects
+
+ def get_generated_sources(self):
+ return self.generated
+
+ def should_install(self):
+ return self.need_install
+
+ def has_pch(self):
+ return len(self.pch) > 0
+
+ def get_pch(self, language):
+ try:
+ return self.pch[language]
+ except KeyError:
+ return[]
+
+ def get_include_dirs(self):
+ return self.include_dirs
+
+ def add_external_deps(self, deps):
+ if not isinstance(deps, list):
+ deps = [deps]
+ for dep in deps:
+ if hasattr(dep, 'held_object'):
+ dep = dep.held_object
+ if isinstance(dep, dependencies.InternalDependency):
+ self.process_sourcelist(dep.sources)
+ self.add_include_dirs(dep.include_directories)
+ for l in dep.libraries:
+ self.link(l)
+ self.add_external_deps(dep.ext_deps)
+ elif isinstance(dep, dependencies.Dependency):
+ self.external_deps.append(dep)
+ self.process_sourcelist(dep.get_sources())
+ else:
+ raise InvalidArguments('Argument is not an external dependency')
+
+ def get_external_deps(self):
+ return self.external_deps
+
+ def link(self, target):
+ if not isinstance(target, list):
+ target = [target]
+ for t in target:
+ if hasattr(t, 'held_object'):
+ t = t.held_object
+ if not isinstance(t, StaticLibrary) and \
+ not isinstance(t, SharedLibrary):
+ raise InvalidArguments('Link target is not library.')
+ if self.is_cross != t.is_cross:
+ raise InvalidArguments('Tried to mix cross built and native libraries in target %s.' % self.name)
+ self.link_targets.append(t)
+
+ def set_generated(self, genlist):
+ for g in genlist:
+ if not(isinstance(g, GeneratedList)):
+ raise InvalidArguments('Generated source argument is not the output of a generator.')
+ self.generated.append(g)
+
+ def add_pch(self, language, pchlist):
+ if len(pchlist) == 0:
+ return
+ elif len(pchlist) == 1:
+ if not environment.is_header(pchlist[0]):
+ raise InvalidArguments('Pch argument %s is not a header.' % pchlist[0])
+ elif len(pchlist) == 2:
+ if environment.is_header(pchlist[0]):
+ if not environment.is_source(pchlist[1]):
+ raise InvalidArguments('PCH definition must contain one header and at most one source.')
+ elif environment.is_source(pchlist[0]):
+ if not environment.is_header(pchlist[1]):
+ raise InvalidArguments('PCH definition must contain one header and at most one source.')
+ pchlist = [pchlist[1], pchlist[0]]
+ else:
+ raise InvalidArguments('PCH argument %s is of unknown type.' % pchlist[0])
+ elif len(pchlist) > 2:
+ raise InvalidArguments('PCH definition may have a maximum of 2 files.')
+ self.pch[language] = pchlist
+
+ def add_include_dirs(self, args):
+ ids = []
+ for a in args:
+ # FIXME same hack, forcibly unpack from holder.
+ if hasattr(a, 'held_object'):
+ a = a.held_object
+ if not isinstance(a, IncludeDirs):
+ raise InvalidArguments('Include directory to be added is not an include directory object.')
+ ids.append(a)
+ self.include_dirs += ids
+
+ def add_compiler_args(self, language, args):
+ args = flatten(args)
+ for a in args:
+ if not isinstance(a, (str, File)):
+ raise InvalidArguments('A non-string passed to compiler args.')
+ if isinstance(a, str) and '\\' in a:
+ raise InvalidArguments(backslash_explanation)
+ if language in self.extra_args:
+ self.extra_args[language] += args
+ else:
+ self.extra_args[language] = args
+
+ def get_aliaslist(self):
+ return []
+
+
+class Generator():
+ def __init__(self, args, kwargs):
+ if len(args) != 1:
+ raise InvalidArguments('Generator requires one and only one positional argument')
+
+ exe = args[0]
+ if hasattr(exe, 'held_object'):
+ exe = exe.held_object
+ if not isinstance(exe, Executable) and not isinstance(exe, dependencies.ExternalProgram):
+ raise InvalidArguments('First generator argument must be an executable.')
+ self.exe = exe
+ self.process_kwargs(kwargs)
+
+ def get_exe(self):
+ return self.exe
+
+ def process_kwargs(self, kwargs):
+ if 'arguments' not in kwargs:
+ raise InvalidArguments('Generator must have "arguments" keyword argument.')
+ args = kwargs['arguments']
+ if isinstance(args, str):
+ args = [args]
+ if not isinstance(args, list):
+ raise InvalidArguments('"Arguments" keyword argument must be a string or a list of strings.')
+ for a in args:
+ if not isinstance(a, str):
+ raise InvalidArguments('A non-string object in "arguments" keyword argument.')
+ self.arglist = args
+
+ if 'output' not in kwargs:
+ raise InvalidArguments('Generator must have "output" keyword argument.')
+ outputs = kwargs['output']
+ if not isinstance(outputs, list):
+ outputs = [outputs]
+ for rule in outputs:
+ if not isinstance(rule, str):
+ raise InvalidArguments('"output" may only contain strings.')
+ if not '@BASENAME@' in rule and not '@PLAINNAME@' in rule:
+ raise InvalidArguments('Every element of "output" must contain @BASENAME@ or @PLAINNAME@.')
+ if '/' in rule or '\\' in rule:
+ raise InvalidArguments('"outputs" must not contain a directory separator.')
+ if len(outputs) > 1:
+ for o in outputs:
+ if '@OUTPUT@' in o:
+ raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.')
+ self.outputs = outputs
+
+ def get_base_outnames(self, inname):
+ plainname = os.path.split(inname)[1]
+ basename = plainname.split('.')[0]
+ return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs]
+
+ def get_arglist(self):
+ return self.arglist
+
+class GeneratedList():
+ def __init__(self, generator, extra_args=[]):
+ if hasattr(generator, 'held_object'):
+ generator = generator.held_object
+ self.generator = generator
+ self.infilelist = []
+ self.outfilelist = []
+ self.outmap = {}
+ self.extra_depends = []
+ self.extra_args = extra_args
+
+ def add_file(self, newfile):
+ self.infilelist.append(newfile)
+ outfiles = self.generator.get_base_outnames(newfile)
+ self.outfilelist += outfiles
+ self.outmap[newfile] = outfiles
+
+ def get_infilelist(self):
+ return self.infilelist
+
+ def get_outfilelist(self):
+ return self.outfilelist
+
+ def get_outputs_for(self, filename):
+ return self.outmap[filename]
+
+ def get_generator(self):
+ return self.generator
+
+ def get_extra_args(self):
+ return self.extra_args
+
+class Executable(BuildTarget):
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
+ self.prefix = ''
+ self.suffix = environment.get_exe_suffix()
+ suffix = environment.get_exe_suffix()
+ if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
+ suffix = 'exe'
+ if suffix != '':
+ self.filename = self.name + '.' + suffix
+ else:
+ self.filename = self.name
+
+ def type_suffix(self):
+ return "@exe"
+
+class StaticLibrary(BuildTarget):
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
+ if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
+ raise InvalidArguments('Static libraries not supported for C#.')
+ self.prefix = environment.get_static_lib_prefix()
+ self.suffix = environment.get_static_lib_suffix()
+ if len(self.sources) > 0 and self.sources[0].endswith('.rs'):
+ self.suffix = 'rlib'
+ self.filename = self.prefix + self.name + '.' + self.suffix
+
+ def get_import_filename(self):
+ return self.filename
+
+ def get_osx_filename(self):
+ return self.get_filename()
+
+ def type_suffix(self):
+ return "@sta"
+
+class SharedLibrary(BuildTarget):
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ self.version = None
+ self.soversion = None
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs);
+ if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
+ self.suffix = 'dll'
+ self.prefix = 'lib'
+ else:
+ self.prefix = environment.get_shared_lib_prefix()
+ self.suffix = environment.get_shared_lib_suffix()
+ if len(self.sources) > 0 and self.sources[0].endswith('.rs'):
+ self.suffix = 'rlib'
+ self.importsuffix = environment.get_import_lib_suffix()
+ self.filename = self.prefix + self.name + '.' + self.suffix
+
+ def process_kwargs(self, kwargs, environment):
+ super().process_kwargs(kwargs, environment)
+ if 'version' in kwargs:
+ self.set_version(kwargs['version'])
+ if 'soversion' in kwargs:
+ self.set_soversion(kwargs['soversion'])
+
+ def check_unknown_kwargs(self, kwargs):
+ self.check_unknown_kwargs_int(kwargs, known_shlib_kwargs)
+
+ def get_shbase(self):
+ return self.prefix + self.name + '.' + self.suffix
+
+ def get_import_filename(self):
+ return self.prefix + self.name + '.' + self.importsuffix
+
+ def get_all_link_deps(self):
+ return [self] + self.get_transitive_link_deps()
+
+ def get_filename(self):
+ '''Works on all platforms except OSX, which does its own thing.'''
+ fname = self.get_shbase()
+ if self.version is None:
+ return fname
+ else:
+ return fname + '.' + self.version
+
+ def get_osx_filename(self):
+ if self.version is None:
+ return self.get_shbase()
+ return self.prefix + self.name + '.' + self.version + '.' + self.suffix
+
+ def set_version(self, version):
+ if not isinstance(version, str):
+ raise InvalidArguments('Shared library version is not a string.')
+ self.version = version
+
+ def set_soversion(self, version):
+ if isinstance(version, int):
+ version = str(version)
+ if not isinstance(version, str):
+ raise InvalidArguments('Shared library soversion is not a string or integer.')
+ self.soversion = version
+
+ def get_aliaslist(self):
+ aliases = []
+ if self.soversion is not None:
+ aliases.append(self.get_shbase() + '.' + self.soversion)
+ if self.version is not None:
+ aliases.append(self.get_shbase())
+ return aliases
+
+ def type_suffix(self):
+ return "@sha"
+
+class CustomTarget:
+ known_kwargs = {'input' : True,
+ 'output' : True,
+ 'command' : True,
+ 'install' : True,
+ 'install_dir' : True,
+ 'build_always' : True,
+ 'depends' : True,
+ 'depend_files' : True,
+ }
+
+ def __init__(self, name, subdir, kwargs):
+ self.name = name
+ self.subdir = subdir
+ self.dependencies = []
+ self.extra_depends = []
+ self.depend_files = [] # Files that this target depends on but are not on the command line.
+ self.process_kwargs(kwargs)
+ self.extra_files = []
+ self.install_rpath = ''
+ unknowns = []
+ for k in kwargs:
+ if k not in CustomTarget.known_kwargs:
+ unknowns.append(k)
+ if len(unknowns) > 0:
+ mlog.log(mlog.bold('Warning:'), 'Unknown keyword arguments in target %s: %s' %
+ (self.name, ', '.join(unknowns)))
+
+ def get_id(self):
+ return self.name + self.type_suffix()
+
+ def process_kwargs(self, kwargs):
+ self.sources = kwargs.get('input', [])
+ if not isinstance(self.sources, list):
+ self.sources = [self.sources]
+ if 'output' not in kwargs:
+ raise InvalidArguments('Missing keyword argument "output".')
+ self.output = kwargs['output']
+ if not isinstance(self.output, list):
+ self.output = [self.output]
+ for i in self.output:
+ if not(isinstance(i, str)):
+ raise InvalidArguments('Output argument not a string.')
+ if '/' in i:
+ raise InvalidArguments('Output must not contain a path segment.')
+ if 'command' not in kwargs:
+ raise InvalidArguments('Missing keyword argument "command".')
+ cmd = kwargs['command']
+ if not(isinstance(cmd, list)):
+ cmd = [cmd]
+ final_cmd = []
+ for i, c in enumerate(cmd):
+ if hasattr(c, 'held_object'):
+ c = c.held_object
+ if isinstance(c, str):
+ final_cmd.append(c)
+ elif isinstance(c, dependencies.ExternalProgram):
+ final_cmd += c.get_command()
+ elif isinstance(c, BuildTarget) or isinstance(c, CustomTarget):
+ self.dependencies.append(c)
+ final_cmd.append(c)
+ elif isinstance(c, list):
+ # Hackety hack, only supports one level of flattening. Should really
+ # work to arbtrary depth.
+ for s in c:
+ if not isinstance(s, str):
+ raise InvalidArguments('Array as argument %d contains a non-string.' % i)
+ final_cmd.append(s)
+ else:
+ raise InvalidArguments('Argument %s in "command" is invalid.' % i)
+ self.command = final_cmd
+ if 'install' in kwargs:
+ self.install = kwargs['install']
+ if not isinstance(self.install, bool):
+ raise InvalidArguments('"install" must be boolean.')
+ if 'install_dir' not in kwargs:
+ raise InvalidArguments('"install_dir" not specified.')
+ self.install_dir = kwargs['install_dir']
+ if not(isinstance(self.install_dir, str)):
+ raise InvalidArguments('"install_dir" must be a string.')
+ else:
+ self.install = False
+ self.build_always = kwargs.get('build_always', False)
+ if not isinstance(self.build_always, bool):
+ raise InvalidArguments('Argument build_always must be a boolean.')
+ extra_deps = kwargs.get('depends', [])
+ if not isinstance(extra_deps, list):
+ extra_deps = [extra_deps]
+ for ed in extra_deps:
+ while hasattr(ed, 'held_object'):
+ ed = ed.held_object
+ if not isinstance(ed, CustomTarget) and not isinstance(ed, BuildTarget):
+ raise InvalidArguments('Can only depend on toplevel targets.')
+ self.extra_depends.append(ed)
+ depend_files = kwargs.get('depend_files', [])
+ if not isinstance(depend_files, list):
+ depend_files = [depend_files]
+ for i in depend_files:
+ if isinstance(i, (File, str)):
+ self.depend_files.append(i)
+ else:
+ mlog.debug(i)
+ raise InvalidArguments('Unknown type in depend_files.')
+
+ def get_basename(self):
+ return self.name
+
+ def get_dependencies(self):
+ return self.dependencies
+
+ def should_install(self):
+ return self.install
+
+ def get_custom_install_dir(self):
+ return self.install_dir
+
+ def get_subdir(self):
+ return self.subdir
+
+ def get_filename(self):
+ return self.output
+
+ def get_aliaslist(self):
+ return []
+
+ def get_sources(self):
+ return self.sources
+
+ def get_generated_sources(self):
+ return []
+
+ def type_suffix(self):
+ return "@cus"
+
+class RunTarget:
+ def __init__(self, name, command, args, subdir):
+ self.name = name
+ self.command = command
+ self.args = args
+ self.subdir = subdir
+
+ def get_id(self):
+ return self.name + self.type_suffix()
+
+ def get_basename(self):
+ return self.name
+
+ def get_dependencies(self):
+ return []
+
+ def get_generated_sources(self):
+ return []
+
+ def get_sources(self):
+ return []
+
+ def get_subdir(self):
+ return self.subdir
+
+ def should_install(self):
+ return False
+
+ def get_filename(self):
+ return self.name
+
+ def type_suffix(self):
+ return "@run"
+
+class Jar(BuildTarget):
+ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs);
+ for s in self.sources:
+ if not s.endswith('.java'):
+ raise InvalidArguments('Jar source %s is not a java file.' % s)
+ self.filename = self.name + '.jar'
+ incdirs = kwargs.get('include_directories', [])
+
+ def get_main_class(self):
+ return self.main_class
+
+ def type_suffix(self):
+ return "@jar"
+
+class ConfigureFile():
+
+ def __init__(self, subdir, sourcename, targetname, configuration_data):
+ self.subdir = subdir
+ self.sourcename = sourcename
+ self.targetname = targetname
+ self.configuration_data = configuration_data
+
+ def get_configuration_data(self):
+ return self.configuration_data
+
+ def get_subdir(self):
+ return self.subdir
+
+ def get_source_name(self):
+ return self.sourcename
+
+ def get_target_name(self):
+ return self.targetname
+
+class ConfigurationData():
+ def __init__(self):
+ super().__init__()
+ self.values = {}
+
+ def get(self, name):
+ return self.values[name]
+
+ def keys(self):
+ return self.values.keys()
+
+# A bit poorly named, but this represents plain data files to copy
+# during install.
+class Data():
+ def __init__(self, in_sourcetree, source_subdir, sources, install_dir):
+ self.in_sourcetree = in_sourcetree
+ self.source_subdir = source_subdir
+ self.sources = sources
+ self.install_dir = install_dir
+
+class InstallScript:
+ def __init__(self, cmd_arr):
+ assert(isinstance(cmd_arr, list))
+ self.cmd_arr = cmd_arr
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
new file mode 100644
index 0000000..ec0181e
--- /dev/null
+++ b/mesonbuild/compilers.py
@@ -0,0 +1,1837 @@
+# Copyright 2012-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 subprocess, os.path
+import tempfile
+from .import mesonlib
+from . import mlog
+from .coredata import MesonException
+from . import coredata
+
+"""This file contains the data files of all compilers Meson knows
+about. To support a new compiler, add its information below.
+Also add corresponding autodetection code in environment.py."""
+
+header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H', 'moc', 'vapi']
+cpp_suffixes = ['cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx', 'c++']
+c_suffixes = ['c']
+clike_suffixes = c_suffixes + cpp_suffixes
+obj_suffixes = ['o', 'obj', 'res']
+lib_suffixes = ['a', 'lib', 'dll', 'dylib', 'so']
+
+def is_header(fname):
+ if hasattr(fname, 'fname'):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in header_suffixes
+
+def is_source(fname):
+ if hasattr(fname, 'fname'):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in clike_suffixes
+
+def is_object(fname):
+ if hasattr(fname, 'fname'):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in obj_suffixes
+
+def is_library(fname):
+ if hasattr(fname, 'fname'):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in lib_suffixes
+
+gnulike_buildtype_args = {'plain' : [],
+ 'debug' : ['-g'],
+ 'debugoptimized' : ['-O2', '-g'],
+ 'release' : ['-O3']}
+
+msvc_buildtype_args = {'plain' : [],
+ 'debug' : ["/MDd", "/ZI", "/Ob0", "/Od", "/RTC1"],
+ 'debugoptimized' : ["/MD", "/Zi", "/O2", "/Ob1", "/D"],
+ 'release' : ["/MD", "/O2", "/Ob2"]}
+
+gnulike_buildtype_linker_args = {}
+
+if mesonlib.is_osx():
+ gnulike_buildtype_linker_args.update({'plain' : [],
+ 'debug' : [],
+ 'debugoptimized' : [],
+ 'release' : [],
+ })
+else:
+ gnulike_buildtype_linker_args.update({'plain' : [],
+ 'debug' : [],
+ 'debugoptimized' : [],
+ 'release' : ['-Wl,-O1'],
+ })
+
+msvc_buildtype_linker_args = {'plain' : [],
+ 'debug' : [],
+ 'debugoptimized' : [],
+ 'release' : []}
+
+java_buildtype_args = {'plain' : [],
+ 'debug' : ['-g'],
+ 'debugoptimized' : ['-g'],
+ 'release' : []}
+
+rust_buildtype_args = {'plain' : [],
+ 'debug' : ['-g'],
+ 'debugoptimized' : ['-g', '--opt-level', '2'],
+ 'release' : ['--opt-level', '3']}
+
+mono_buildtype_args = {'plain' : [],
+ 'debug' : ['-debug'],
+ 'debugoptimized': ['-debug', '-optimize+'],
+ 'release' : ['-optimize+']}
+
+swift_buildtype_args = {'plain' : [],
+ 'debug' : ['-g'],
+ 'debugoptimized': ['-g', '-O'],
+ 'release' : ['-O']}
+
+gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32',
+ '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32']
+
+msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib',
+ 'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib',
+ 'uuid.lib', 'comdlg32.lib', 'advapi32.lib']
+
+def build_unix_rpath_args(build_dir, rpath_paths, install_rpath):
+ if len(rpath_paths) == 0 and len(install_rpath) == 0:
+ return []
+ paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths])
+ if len(paths) < len(install_rpath):
+ padding = 'X'*(len(install_rpath) - len(paths))
+ if len(paths) == 0:
+ paths = padding
+ else:
+ paths = paths + ':' + padding
+ return ['-Wl,-rpath,' + paths]
+
+class EnvironmentException(MesonException):
+ def __init(self, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+
+class CrossNoRunException(MesonException):
+ def __init(self, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+
+class RunResult():
+ def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'):
+ self.compiled = compiled
+ self.returncode = returncode
+ self.stdout = stdout
+ self.stderr = stderr
+
+class Compiler():
+ def __init__(self, exelist, version):
+ if type(exelist) == type(''):
+ self.exelist = [exelist]
+ elif type(exelist) == type([]):
+ self.exelist = exelist
+ else:
+ raise TypeError('Unknown argument to Compiler')
+ self.version = version
+
+ def get_always_args(self):
+ return []
+
+ def get_linker_always_args(self):
+ return []
+
+ def get_options(self):
+ return {} # build afresh every time
+
+ def get_option_compile_args(self, options):
+ return []
+
+ def get_option_link_args(self, options):
+ return []
+
+ def has_header(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support header checks.' % self.language)
+
+ def compiles(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support compile checks.' % self.language)
+
+ def links(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support link checks.' % self.language)
+
+ def run(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support run checks.' % self.language)
+
+ def sizeof(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support sizeof checks.' % self.language)
+
+ def alignment(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support alignment checks.' % self.language)
+
+ def has_function(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support function checks.' % self.language)
+
+ def unixtype_flags_to_native(self, args):
+ return args
+
+class CCompiler(Compiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version)
+ self.language = 'c'
+ self.default_suffix = 'c'
+ self.id = 'unknown'
+ self.is_cross = is_cross
+ if isinstance(exe_wrapper, str):
+ self.exe_wrapper = [exe_wrapper]
+ else:
+ self.exe_wrapper = exe_wrapper
+
+ def needs_static_linker(self):
+ return True # When compiling static libraries, so yes.
+
+ def get_always_args(self):
+ return []
+
+ def get_warn_args(self, level):
+ return self.warn_args[level]
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return []
+
+ def split_shlib_to_parts(self, fname):
+ return (None, fname)
+
+ # The default behaviour is this, override in
+ # OSX and MSVC.
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return build_unix_rpath_args(build_dir, rpath_paths, install_rpath)
+
+ def get_id(self):
+ return self.id
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return ['-MMD', '-MQ', outtarget, '-MF', outfile]
+
+ def depfile_for_object(self, objfile):
+ return objfile + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self):
+ return 'd'
+
+ def get_language(self):
+ return self.language
+
+ def get_default_suffix(self):
+ return self.default_suffix
+
+ def get_exelist(self):
+ return self.exelist[:]
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_output_args(self, target):
+ return ['-o', target]
+
+ def get_linker_output_args(self, outputname):
+ return ['-o', outputname]
+
+ def get_coverage_args(self):
+ return ['--coverage']
+
+ def get_coverage_link_args(self):
+ return ['-lgcov']
+
+ def get_werror_args(self):
+ return ['-Werror']
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_include_args(self, path, is_system):
+ if path == '':
+ path = '.'
+ if is_system:
+ return ['-isystem', path]
+ return ['-I' + path]
+
+ def get_std_shared_lib_link_args(self):
+ return ['-shared']
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix == 'c' or suffix == 'h':
+ return True
+ return False
+
+ def get_pic_args(self):
+ return ['-fPIC']
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_pch_use_args(self, pch_dir, header):
+ return ['-include', os.path.split(header)[-1]]
+
+ def get_pch_name(self, header_name):
+ return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix()
+
+ def sanity_check(self, work_dir):
+ mlog.debug('Sanity testing C compiler:', ' '.join(self.exelist))
+ mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
+
+ source_name = os.path.join(work_dir, 'sanitycheckc.c')
+ if self.is_cross:
+ binname = 'sanitycheckc_cross'
+ else:
+ binname = 'sanitycheckc'
+ binary_name = os.path.join(work_dir, binname)
+ ofile = open(source_name, 'w')
+ ofile.write('int main(int argc, char **argv) { int class=0; return class; }\n')
+ ofile.close()
+ if self.is_cross and self.exe_wrapper is None:
+ # Linking cross built apps is painful. You can't really
+ # tell if you should use -nostdlib or not and for example
+ # on OSX the compiler binary is the same but you need
+ # a ton of compiler flags to differentiate between
+ # arm and x86_64. So just compile.
+ extra_flags = ['-c']
+ else:
+ extra_flags = []
+ cmdlist = self.exelist + extra_flags + [source_name, '-o', binary_name]
+ pc = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdo, stde) = pc.communicate()
+ stdo = stdo.decode()
+ stde = stde.decode()
+ mlog.debug('Sanity check compiler command line:', ' '.join(cmdlist))
+ mlog.debug('Sanity check compile stdout:')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check compile stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper + [binary_name]
+ else:
+ cmdlist = [binary_name]
+ mlog.debug('Running test binary command: ' + ' '.join(cmdlist))
+ pe = subprocess.Popen(cmdlist)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by C compiler %s are not runnable.' % self.name_string())
+
+ def has_header(self, hname, extra_args=[]):
+ templ = '''#include<%s>
+int someSymbolHereJustForFun;
+'''
+ return self.compiles(templ % hname, extra_args)
+
+ def compile(self, code, srcname, extra_args=[]):
+ commands = self.get_exelist()
+ commands.append(srcname)
+ commands += extra_args
+ mlog.debug('Running compile:')
+ mlog.debug('Command line: ', ' '.join(commands))
+ mlog.debug('Code:\n', code)
+ p = subprocess.Popen(commands, cwd=os.path.split(srcname)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stde, stdo) = p.communicate()
+ stde = stde.decode()
+ stdo = stdo.decode()
+ mlog.debug('Compiler stdout:\n', stdo)
+ mlog.debug('Compiler stderr:\n', stde)
+ os.remove(srcname)
+ return p
+
+ def compiles(self, code, extra_args = []):
+ suflen = len(self.default_suffix)
+ (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
+ os.close(fd)
+ ofile = open(srcname, 'w')
+ ofile.write(code)
+ ofile.close()
+ extra_args = extra_args + self.get_compile_only_args()
+ p = self.compile(code, srcname, extra_args)
+ try:
+ trial = srcname[:-suflen] + 'o'
+ os.remove(trial)
+ except FileNotFoundError:
+ pass
+ try:
+ os.remove(srcname[:-suflen] + 'obj')
+ except FileNotFoundError:
+ pass
+ return p.returncode == 0
+
+ def links(self, code, extra_args = []):
+ suflen = len(self.default_suffix)
+ (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
+ os.close(fd)
+ (fd, dstname) = tempfile.mkstemp()
+ os.close(fd)
+ ofile = open(srcname, 'w')
+ ofile.write(code)
+ ofile.close()
+ extra_args = extra_args + self.get_output_args(dstname)
+ p = self.compile(code, srcname, extra_args)
+ try:
+ os.remove(dstname)
+ except FileNotFoundError:
+ pass
+ return p.returncode == 0
+
+ def run(self, code, extra_args=[]):
+ mlog.debug('Running code:\n\n', code)
+ if self.is_cross and self.exe_wrapper is None:
+ raise CrossNoRunException('Can not run test applications in this cross environment.')
+ (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
+ os.close(fd)
+ ofile = open(srcname, 'w')
+ ofile.write(code)
+ ofile.close()
+ exename = srcname + '.exe' # Is guaranteed to be executable on every platform.
+ commands = self.get_exelist()
+ commands += extra_args
+ commands.append(srcname)
+ commands += self.get_output_args(exename)
+ p = subprocess.Popen(commands, cwd=os.path.split(srcname)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdo, stde) = p.communicate()
+ stde = stde.decode()
+ stdo = stdo.decode()
+ mlog.debug('Compiler stdout:\n', stdo)
+ mlog.debug('Compiler stderr:\n', stde)
+ os.remove(srcname)
+ if p.returncode != 0:
+ return RunResult(False)
+ if self.is_cross:
+ cmdlist = self.exe_wrapper + [exename]
+ else:
+ cmdlist = exename
+ try:
+ pe = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except Exception as e:
+ mlog.debug('Could not run: %s (error: %s)\n' % (cmdlist, e))
+ return RunResult(False)
+
+ (so, se) = pe.communicate()
+ so = so.decode()
+ se = se.decode()
+ mlog.debug('Program stdout:\n', so)
+ mlog.debug('Program stderr:\n', se)
+ os.remove(exename)
+ return RunResult(True, pe.returncode, so, se)
+
+ def cross_sizeof(self, element, prefix, env, extra_args=[]):
+ templ = '''%s
+int temparray[%d-sizeof(%s)];
+'''
+ try:
+ extra_args += env.cross_info.config['properties'][self.language + '_args']
+ except KeyError:
+ pass
+ for i in range(1, 1024):
+ code = templ % (prefix, i, element)
+ if self.compiles(code, extra_args):
+ return i
+ raise EnvironmentException('Cross checking sizeof overflowed.')
+
+ def sizeof(self, element, prefix, env, extra_args=[]):
+ if self.is_cross:
+ return self.cross_sizeof(element, prefix, env, extra_args)
+ templ = '''#include<stdio.h>
+%s
+
+int main(int argc, char **argv) {
+ printf("%%ld\\n", (long)(sizeof(%s)));
+ return 0;
+};
+'''
+ res = self.run(templ % (prefix, element), extra_args)
+ if not res.compiled:
+ raise EnvironmentException('Could not compile sizeof test.')
+ if res.returncode != 0:
+ raise EnvironmentException('Could not run sizeof test binary.')
+ return int(res.stdout)
+
+ def cross_alignment(self, typename, env, extra_args=[]):
+ templ = '''#include<stddef.h>
+struct tmp {
+ char c;
+ %s target;
+};
+
+int testarray[%d-offsetof(struct tmp, target)];
+'''
+ try:
+ extra_args += env.cross_info.config['properties'][self.language + '_args']
+ except KeyError:
+ pass
+ for i in range(1, 1024):
+ code = templ % (typename, i)
+ if self.compiles(code, extra_args):
+ return i
+ raise EnvironmentException('Cross checking offsetof overflowed.')
+
+ def alignment(self, typename, env, extra_args=[]):
+ if self.is_cross:
+ return self.cross_alignment(typename, env, extra_args)
+ templ = '''#include<stdio.h>
+#include<stddef.h>
+
+struct tmp {
+ char c;
+ %s target;
+};
+
+int main(int argc, char **argv) {
+ printf("%%d", (int)offsetof(struct tmp, target));
+ return 0;
+}
+'''
+ res = self.run(templ % typename, extra_args)
+ if not res.compiled:
+ raise EnvironmentException('Could not compile alignment test.')
+ if res.returncode != 0:
+ raise EnvironmentException('Could not run alignment test binary.')
+ align = int(res.stdout)
+ if align == 0:
+ raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
+ return align
+
+ def has_function(self, funcname, prefix, env, extra_args=[]):
+ # This fails (returns true) if funcname is a ptr or a variable.
+ # The correct check is a lot more difficult.
+ # Fix this to do that eventually.
+ templ = '''%s
+int main(int argc, char **argv) {
+ void *ptr = (void*)(%s);
+ return 0;
+};
+'''
+ varname = 'has function ' + funcname
+ varname = varname.replace(' ', '_')
+ if self.is_cross:
+ val = env.cross_info.config['properties'].get(varname, None)
+ if val is not None:
+ if isinstance(val, bool):
+ return val
+ raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
+ return self.compiles(templ % (prefix, funcname), extra_args)
+
+ def has_member(self, typename, membername, prefix, extra_args=[]):
+ templ = '''%s
+void bar() {
+ %s foo;
+ foo.%s;
+};
+'''
+ return self.compiles(templ % (prefix, typename, membername), extra_args)
+
+ def has_type(self, typename, prefix, extra_args):
+ templ = '''%s
+void bar() {
+ sizeof(%s);
+};
+'''
+ return self.compiles(templ % (prefix, typename), extra_args)
+
+ def thread_flags(self):
+ return ['-pthread']
+
+ def thread_link_flags(self):
+ return ['-pthread']
+
+class CPPCompiler(CCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap):
+ CCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.language = 'cpp'
+ self.default_suffix = 'cpp'
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix in cpp_suffixes:
+ return True
+ return False
+
+ def sanity_check(self, work_dir):
+ source_name = os.path.join(work_dir, 'sanitycheckcpp.cc')
+ binary_name = os.path.join(work_dir, 'sanitycheckcpp')
+ ofile = open(source_name, 'w')
+ ofile.write('class breakCCompiler;int main(int argc, char **argv) { return 0; }\n')
+ ofile.close()
+ if self.is_cross and self.exe_wrapper is None:
+ # Skipping link because of the same reason as for C.
+ # The comment in CCompiler explains why this is done.
+ extra_flags = ['-c']
+ else:
+ extra_flags = []
+ cmdlist = self.exelist + extra_flags + [source_name, '-o', binary_name]
+ pc = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdo, stde) = pc.communicate()
+ stdo = stdo.decode()
+ stde = stde.decode()
+ mlog.debug('Sanity check compiler command line:', ' '.join(cmdlist))
+ mlog.debug('Sanity check compile stdout:')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check compile stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper + [binary_name]
+ else:
+ cmdlist = [binary_name]
+ pe = subprocess.Popen(cmdlist)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by C++ compiler %s are not runnable.' % self.name_string())
+
+class ObjCCompiler(CCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap):
+ CCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.language = 'objc'
+ self.default_suffix = 'm'
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix == 'm' or suffix == 'h':
+ return True
+ return False
+
+ def sanity_check(self, work_dir):
+ source_name = os.path.join(work_dir, 'sanitycheckobjc.m')
+ binary_name = os.path.join(work_dir, 'sanitycheckobjc')
+ ofile = open(source_name, 'w')
+ ofile.write('#import<stdio.h>\nint main(int argc, char **argv) { return 0; }\n')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [source_name, '-o', binary_name])
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('ObjC compiler %s can not compile programs.' % self.name_string())
+ pe = subprocess.Popen(binary_name)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by ObjC compiler %s are not runnable.' % self.name_string())
+
+class ObjCPPCompiler(CPPCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap):
+ CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.language = 'objcpp'
+ self.default_suffix = 'mm'
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix == 'mm' or suffix == 'h':
+ return True
+ return False
+
+ def sanity_check(self, work_dir):
+ source_name = os.path.join(work_dir, 'sanitycheckobjcpp.mm')
+ binary_name = os.path.join(work_dir, 'sanitycheckobjcpp')
+ ofile = open(source_name, 'w')
+ ofile.write('#import<stdio.h>\nclass MyClass;int main(int argc, char **argv) { return 0; }\n')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [source_name, '-o', binary_name])
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('ObjC++ compiler %s can not compile programs.' % self.name_string())
+ pe = subprocess.Popen(binary_name)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by ObjC++ compiler %s are not runnable.' % self.name_string())
+
+class MonoCompiler(Compiler):
+ def __init__(self, exelist, version):
+ super().__init__(exelist, version)
+ self.language = 'cs'
+ self.default_suffix = 'cs'
+ self.id = 'mono'
+ self.monorunner = 'mono'
+
+ def get_output_args(self, fname):
+ return ['-out:' + fname]
+
+ def get_link_args(self, fname):
+ return ['-r:' + fname]
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return []
+
+ def get_werror_args(self):
+ return ['-warnaserror']
+
+ def split_shlib_to_parts(self, fname):
+ return (None, fname)
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return []
+
+ def get_id(self):
+ return self.id
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return []
+
+ def get_language(self):
+ return self.language
+
+ def get_default_suffix(self):
+ return self.default_suffix
+
+ def get_exelist(self):
+ return self.exelist[:]
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def get_compile_only_args(self):
+ return []
+
+ def get_linker_output_args(self, outputname):
+ return []
+
+ def get_coverage_args(self):
+ return []
+
+ def get_coverage_link_args(self):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_include_args(self, path):
+ return []
+
+ def get_std_shared_lib_link_args(self):
+ return []
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix == 'cs':
+ return True
+ return False
+
+ def get_pic_args(self):
+ return []
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_pch_use_args(self, pch_dir, header):
+ return []
+
+ def get_pch_name(self, header_name):
+ return ''
+
+ def sanity_check(self, work_dir):
+ src = 'sanity.cs'
+ obj = 'sanity.exe'
+ source_name = os.path.join(work_dir, src)
+ ofile = open(source_name, 'w')
+ ofile.write('''public class Sanity {
+ static public void Main () {
+ }
+}
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string())
+ cmdlist = [self.monorunner, obj]
+ pe = subprocess.Popen(cmdlist, cwd=work_dir)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string())
+
+ def needs_static_linker(self):
+ return False
+
+ def get_buildtype_args(self, buildtype):
+ return mono_buildtype_args[buildtype]
+
+class JavaCompiler(Compiler):
+ def __init__(self, exelist, version):
+ super().__init__(exelist, version)
+ self.language = 'java'
+ self.default_suffix = 'java'
+ self.id = 'unknown'
+ self.javarunner = 'java'
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return []
+
+ def get_werror_args(self):
+ return ['-Werror']
+
+ def split_shlib_to_parts(self, fname):
+ return (None, fname)
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return []
+
+ def get_id(self):
+ return self.id
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return []
+
+ def get_language(self):
+ return self.language
+
+ def get_default_suffix(self):
+ return self.default_suffix
+
+ def get_exelist(self):
+ return self.exelist[:]
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def get_compile_only_args(self):
+ return []
+
+ def get_output_args(self, subdir):
+ if subdir == '':
+ subdir = './'
+ return ['-d', subdir, '-s', subdir]
+
+ def get_linker_output_args(self, outputname):
+ return []
+
+ def get_coverage_args(self):
+ return []
+
+ def get_coverage_link_args(self):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def get_include_args(self, path):
+ return []
+
+ def get_std_shared_lib_link_args(self):
+ return []
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix == 'java':
+ return True
+ return False
+
+ def get_pic_args(self):
+ return []
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_pch_use_args(self, pch_dir, header):
+ return []
+
+ def get_pch_name(self, header_name):
+ return ''
+
+ def get_buildtype_args(self, buildtype):
+ return java_buildtype_args[buildtype]
+
+ def sanity_check(self, work_dir):
+ src = 'SanityCheck.java'
+ obj = 'SanityCheck'
+ source_name = os.path.join(work_dir, src)
+ ofile = open(source_name, 'w')
+ ofile.write('''class SanityCheck {
+ public static void main(String[] args) {
+ int i;
+ }
+}
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Java compiler %s can not compile programs.' % self.name_string())
+ cmdlist = [self.javarunner, obj]
+ pe = subprocess.Popen(cmdlist, cwd=work_dir)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Java compiler %s are not runnable.' % self.name_string())
+
+ def needs_static_linker(self):
+ return False
+
+class ValaCompiler(Compiler):
+ def __init__(self, exelist, version):
+ super().__init__(exelist, version)
+ self.version = version
+ self.id = 'unknown'
+ self.language = 'vala'
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def needs_static_linker(self):
+ return False # Because compiles into C.
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_werror_args(self):
+ return ['--fatal-warnings']
+
+ def get_language(self):
+ return self.language
+
+ def sanity_check(self, work_dir):
+ src = 'valatest.vala'
+ source_name = os.path.join(work_dir, src)
+ ofile = open(source_name, 'w')
+ ofile.write('''class SanityCheck : Object {
+}
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + ['-C', '-c', src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Vala compiler %s can not compile programs.' % self.name_string())
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ return suffix in ('vala', 'vapi')
+
+class RustCompiler(Compiler):
+ def __init__(self, exelist, version):
+ super().__init__(exelist, version)
+ self.id = 'unknown'
+ self.language = 'rust'
+
+ def needs_static_linker(self):
+ return False
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_id(self):
+ return self.id
+
+ def get_language(self):
+ return self.language
+
+ def sanity_check(self, work_dir):
+ source_name = os.path.join(work_dir, 'sanity.rs')
+ output_name = os.path.join(work_dir, 'rusttest')
+ ofile = open(source_name, 'w')
+ ofile.write('''fn main() {
+}
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string())
+ if subprocess.call(output_name) != 0:
+ raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string())
+
+ def can_compile(self, fname):
+ return fname.endswith('.rs')
+
+ def get_dependency_gen_args(self, outfile):
+ return ['--dep-info', outfile]
+
+ def get_buildtype_args(self, buildtype):
+ return rust_buildtype_args[buildtype]
+
+class SwiftCompiler(Compiler):
+ def __init__(self, exelist, version):
+ super().__init__(exelist, version)
+ self.version = version
+ self.id = 'llvm'
+ self.language = 'swift'
+ self.is_cross = False
+
+ def get_id(self):
+ return self.id
+
+ def get_linker_exelist(self):
+ return self.exelist
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def needs_static_linker(self):
+ return True
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_werror_args(self):
+ return ['--fatal-warnings']
+
+ def get_language(self):
+ return self.language
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return ['-emit-dependencies']
+
+ def depfile_for_object(self, objfile):
+ return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self):
+ return 'd'
+
+ def get_output_args(self, target):
+ return ['-o', target]
+
+ def get_linker_output_args(self, target):
+ return ['-o', target]
+
+ def get_header_import_args(self, headername):
+ return ['-import-objc-header', headername]
+
+ def get_warn_args(self, level):
+ return []
+
+ def get_buildtype_args(self, buildtype):
+ return swift_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_std_exe_link_args(self):
+ return ['-emit-executable']
+
+ def get_module_args(self, modname):
+ return ['-module-name', modname]
+
+ def get_mod_gen_args(self):
+ return ['-emit-module']
+
+ def build_rpath_args(self, *args):
+ return [] # FIXME
+
+ def get_include_args(self, dirname):
+ return ['-I' + dirname]
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def sanity_check(self, work_dir):
+ src = 'swifttest.swift'
+ source_name = os.path.join(work_dir, src)
+ output_name = os.path.join(work_dir, 'swifttest')
+ ofile = open(source_name, 'w')
+ ofile.write('''1 + 2
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + ['-emit-executable', '-o', output_name, src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Swift compiler %s can not compile programs.' % self.name_string())
+ if subprocess.call(output_name) != 0:
+ raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string())
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ return suffix in ('swift')
+
+class VisualStudioCCompiler(CCompiler):
+ std_warn_args = ['/W3']
+ std_opt_args= ['/O2']
+ vs2010_always_args = ['/nologo', '/showIncludes']
+ vs2013_always_args = ['/nologo', '/showIncludes', '/FS']
+
+ def __init__(self, exelist, version, is_cross, exe_wrap):
+ CCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.id = 'msvc'
+ if int(version.split('.')[0]) > 17:
+ self.always_args = VisualStudioCCompiler.vs2013_always_args
+ else:
+ self.always_args = VisualStudioCCompiler.vs2010_always_args
+ self.warn_args = {'1': ['/W2'],
+ '2': ['/W3'],
+ '3': ['/w4']}
+
+ def get_always_args(self):
+ return self.always_args
+
+ def get_buildtype_args(self, buildtype):
+ return msvc_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return msvc_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'pch'
+
+ def get_pch_name(self, header):
+ chopped = os.path.split(header)[-1].split('.')[:-1]
+ chopped.append(self.get_pch_suffix())
+ pchname = '.'.join(chopped)
+ return pchname
+
+ def get_pch_use_args(self, pch_dir, header):
+ base = os.path.split(header)[-1]
+ pchname = self.get_pch_name(header)
+ return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)]
+
+ def get_compile_only_args(self):
+ return ['/c']
+
+ def get_output_args(self, target):
+ if target.endswith('.exe'):
+ return ['/Fe' + target]
+ return ['/Fo' + target]
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return []
+
+ def get_linker_exelist(self):
+ return ['link'] # FIXME, should have same path as compiler.
+
+ def get_linker_always_args(self):
+ return ['/nologo']
+
+ def get_linker_output_args(self, outputname):
+ return ['/OUT:' + outputname]
+
+ def get_pic_args(self):
+ return ['/LD']
+
+ def get_std_shared_lib_link_args(self):
+ return ['/DLL']
+
+ def gen_pch_args(self, header, source, pchname):
+ objname = os.path.splitext(pchname)[0] + '.obj'
+ return (objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname ])
+
+ def sanity_check(self, work_dir):
+ source_name = 'sanitycheckc.c'
+ binary_name = 'sanitycheckc'
+ ofile = open(os.path.join(work_dir, source_name), 'w')
+ ofile.write('int main(int argc, char **argv) { return 0; }\n')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [source_name, '/Fe' + binary_name],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
+ pe = subprocess.Popen(os.path.join(work_dir, binary_name))
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by C++ compiler %s are not runnable.' % self.name_string())
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return []
+
+ # FIXME, no idea what these should be.
+ def thread_flags(self):
+ return []
+
+ def thread_link_flags(self):
+ return []
+
+ def get_options(self):
+ return {'c_winlibs' : coredata.UserStringArrayOption('c_winlibs',
+ 'Windows libs to link against.',
+ msvc_winlibs)
+ }
+
+ def get_option_link_args(self, options):
+ return options['c_winlibs'].value
+
+ def unixtype_flags_to_native(self, args):
+ result = []
+ for i in args:
+ if i.startswith('-L'):
+ i = '/LIBPATH:' + i[2:]
+ result.append(i)
+ return result
+
+ def get_include_args(self, path, is_system):
+ if path == '':
+ path = '.'
+ # msvc does not have a concept of system header dirs.
+ return ['-I' + path]
+
+class VisualStudioCPPCompiler(VisualStudioCCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrap):
+ VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.language = 'cpp'
+ self.default_suffix = 'cpp'
+
+ def can_compile(self, filename):
+ suffix = filename.split('.')[-1]
+ if suffix in cpp_suffixes:
+ return True
+ return False
+
+ def sanity_check(self, work_dir):
+ source_name = 'sanitycheckcpp.cpp'
+ binary_name = 'sanitycheckcpp'
+ ofile = open(os.path.join(work_dir, source_name), 'w')
+ ofile.write('class BreakPlainC;int main(int argc, char **argv) { return 0; }\n')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [source_name, '/Fe' + binary_name],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
+ pe = subprocess.Popen(os.path.join(work_dir, binary_name))
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by C++ compiler %s are not runnable.' % self.name_string())
+
+ def get_options(self):
+ return {'cpp_eh' : coredata.UserComboOption('cpp_eh',
+ 'C++ exception handling type.',
+ ['none', 'a', 's', 'sc'],
+ 'sc'),
+ 'cpp_winlibs' : coredata.UserStringArrayOption('cpp_winlibs',
+ 'Windows libs to link against.',
+ msvc_winlibs)
+ }
+
+ def get_option_compile_args(self, options):
+ args = []
+ std = options['cpp_eh']
+ if std.value != 'none':
+ args.append('/EH' + std.value)
+ return args
+
+ def get_option_link_args(self, options):
+ return options['cpp_winlibs'].value
+
+GCC_STANDARD = 0
+GCC_OSX = 1
+GCC_MINGW = 2
+
+def get_gcc_soname_args(gcc_type, shlib_name, path, soversion):
+ if soversion is None:
+ sostr = ''
+ else:
+ sostr = '.' + soversion
+ if gcc_type == GCC_STANDARD or gcc_type == GCC_MINGW:
+ # Might not be correct for mingw but seems to work.
+ return ['-Wl,-soname,lib%s.so%s' % (shlib_name, sostr)]
+ elif gcc_type == GCC_OSX:
+ return ['-install_name', os.path.join(path, 'lib' + shlib_name + '.dylib')]
+ else:
+ raise RuntimeError('Not implemented yet.')
+
+
+class GnuCCompiler(CCompiler):
+ def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None):
+ CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ self.id = 'gcc'
+ self.gcc_type = gcc_type
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch'],
+ '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']}
+
+ def get_pic_args(self):
+ if self.gcc_type == GCC_MINGW:
+ return [] # On Window gcc defaults to fpic being always on.
+ return ['-fPIC']
+
+ def get_always_args(self):
+ return ['-pipe']
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'gch'
+
+ def split_shlib_to_parts(self, fname):
+ return (os.path.split(fname)[0], fname)
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
+
+ def can_compile(self, filename):
+ return super().can_compile(filename) or filename.split('.')[-1].lower() == 's' # Gcc can do asm, too.
+
+ def get_options(self):
+ opts = {'c_std' : coredata.UserComboOption('c_std', 'C language standard to use',
+ ['none', 'c89', 'c99', 'c11', 'gnu89', 'gnu99', 'gnu11'],
+ 'none')}
+ if self.gcc_type == GCC_MINGW:
+ opts.update({
+ 'c_winlibs': coredata.UserStringArrayOption('c_winlibs', 'Standard Win libraries to link against',
+ gnu_winlibs),
+ })
+ return opts
+
+ def get_option_compile_args(self, options):
+ args = []
+ std = options['c_std']
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options):
+ if self.gcc_type == GCC_MINGW:
+ return options['c_winlibs'].value
+ return []
+
+class GnuObjCCompiler(ObjCCompiler):
+ std_opt_args = ['-O2']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ self.id = 'gcc'
+ # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug
+ # if this breaks your use case.
+ self.gcc_type = GCC_STANDARD
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch'],
+ '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch']}
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'gch'
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
+
+class GnuObjCPPCompiler(ObjCPPCompiler):
+ std_opt_args = ['-O2']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ self.id = 'gcc'
+ # Not really correct, but GNU objc is only used on non-OSX non-win. File a bug
+ # if this breaks your use case.
+ self.gcc_type = GCC_STANDARD
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '3' : ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']}
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'gch'
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
+
+class ClangObjCCompiler(GnuObjCCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper)
+ self.id = 'clang'
+
+class ClangObjCPPCompiler(GnuObjCPPCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper)
+ self.id = 'clang'
+
+class ClangCCompiler(CCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ self.id = 'clang'
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch'],
+ '3' : ['-Weverything']}
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'pch'
+
+ def can_compile(self, filename):
+ return super().can_compile(filename) or filename.split('.')[-1].lower() == 's' # Clang can do asm, too.
+
+ def get_pch_use_args(self, pch_dir, header):
+ # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136
+ # This flag is internal to Clang (or at least not documented on the man page)
+ # so it might change semantics at any time.
+ return ['-include-pch', os.path.join (pch_dir, self.get_pch_name (header))]
+
+ def get_options(self):
+ return {'c_std' : coredata.UserComboOption('c_std', 'C language standard to use',
+ ['none', 'c89', 'c99', 'c11'],
+ 'none')}
+
+ def get_option_compile_args(self, options):
+ args = []
+ std = options['c_std']
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options):
+ return []
+
+class GnuCPPCompiler(CPPCompiler):
+ # may need to separate the latter to extra_debug_args or something
+ std_debug_args = ['-g']
+
+ def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap):
+ CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
+ self.id = 'gcc'
+ self.gcc_type = gcc_type
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '3': ['-Wall', '-Wpedantic', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor']}
+
+ def get_always_args(self):
+ return ['-pipe']
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'gch'
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
+
+ def get_options(self):
+ opts = {'cpp_std' : coredata.UserComboOption('cpp_std', 'C++ language standard to use',
+ ['none', 'c++03', 'c++11', 'c++14'],
+ 'none')}
+ if self.gcc_type == GCC_MINGW:
+ opts.update({
+ 'cpp_winlibs': coredata.UserStringArrayOption('c_winlibs', 'Standard Win libraries to link against',
+ gnu_winlibs),
+ })
+ return opts
+
+ def get_option_compile_args(self, options):
+ args = []
+ std = options['cpp_std']
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options):
+ if self.gcc_type == GCC_MINGW:
+ return options['cpp_winlibs'].value
+ return []
+
+class ClangCPPCompiler(CPPCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
+ self.id = 'clang'
+ self.warn_args = {'1': ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '2': ['-Wall', '-Wextra', '-Winvalid-pch', '-Wnon-virtual-dtor'],
+ '3': ['-Weverything']}
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def get_pch_suffix(self):
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir, header):
+ # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136
+ # This flag is internal to Clang (or at least not documented on the man page)
+ # so it might change semantics at any time.
+ return ['-include-pch', os.path.join (pch_dir, self.get_pch_name (header))]
+
+ def get_options(self):
+ return {'cpp_std' : coredata.UserComboOption('cpp_std', 'C++ language standard to use',
+ ['none', 'c++03', 'c++11', 'c++14'],
+ 'none')}
+
+ def get_option_compile_args(self, options):
+ args = []
+ std = options['cpp_std']
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options):
+ return []
+
+class FortranCompiler(Compiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version)
+ self.is_cross = is_cross
+ self.exe_wrapper = exe_wrapper
+ self.language = 'fortran'
+ # Not really correct but I don't have Fortran compilers to test with. Sorry.
+ self.gcc_type = GCC_STANDARD
+ self.id = "IMPLEMENTATION CLASSES MUST SET THIS"
+
+ def get_id(self):
+ return self.id
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_language(self):
+ return self.language
+
+ def get_pic_args(self):
+ if self.gcc_type == GCC_MINGW:
+ return [] # On Windows gcc defaults to fpic being always on.
+ return ['-fPIC']
+
+ def get_std_shared_lib_link_args(self):
+ return ['-shared']
+
+ def needs_static_linker(self):
+ return True
+
+ def sanity_check(self, work_dir):
+ source_name = os.path.join(work_dir, 'sanitycheckf.f90')
+ binary_name = os.path.join(work_dir, 'sanitycheckf')
+ ofile = open(source_name, 'w')
+ ofile.write('''program prog
+ print *, "Fortran compilation is working."
+end program prog
+''')
+ ofile.close()
+ pc = subprocess.Popen(self.exelist + [source_name, '-o', binary_name])
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper + [binary_name]
+ else:
+ cmdlist = [binary_name]
+ pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string())
+
+ def get_std_warn_args(self, level):
+ return FortranCompiler.std_warn_args
+
+ def get_buildtype_args(self, buildtype):
+ return gnulike_buildtype_args[buildtype]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return gnulike_buildtype_linker_args[buildtype]
+
+ def split_shlib_to_parts(self, fname):
+ return (os.path.split(fname)[0], fname)
+
+ def get_soname_args(self, shlib_name, path, soversion):
+ return get_gcc_soname_args(self.gcc_type, shlib_name, path, soversion)
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ # Disabled until this is fixed:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62162
+ #return ['-cpp', '-MMD', '-MQ', outtarget]
+ return []
+
+ def get_output_args(self, target):
+ return ['-o', target]
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def get_linker_output_args(self, outputname):
+ return ['-o', outputname]
+
+ def can_compile(self, src):
+ if hasattr(src, 'fname'):
+ src = src.fname
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f95' or suffix == '.f90':
+ return True
+ return False
+
+ def get_include_args(self, path, is_system):
+ return ['-I' + path]
+
+ def get_module_outdir_args(self, path):
+ return ['-J' + path]
+
+ def depfile_for_object(self, objfile):
+ return objfile + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self):
+ return 'd'
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return build_unix_rpath_args(build_dir, rpath_paths, install_rpath)
+
+ def module_name_to_filename(self, module_name):
+ return module_name.lower() + '.mod'
+
+ def get_warn_args(self, level):
+ return ['-Wall']
+
+
+class GnuFortranCompiler(FortranCompiler):
+ def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.gcc_type = gcc_type
+ self.id = 'gcc'
+
+ def get_always_args(self):
+ return ['-pipe']
+
+class G95FortranCompiler(FortranCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'g95'
+
+ def get_module_outdir_args(self, path):
+ return ['-fmod='+path]
+
+ def get_always_args(self):
+ return ['-pipe']
+
+class SunFortranCompiler(FortranCompiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'sun'
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return ['-fpp']
+
+ def get_always_args(self):
+ return []
+
+ def get_warn_args(self):
+ return []
+
+ def get_module_outdir_args(self, path):
+ return ['-moddir='+path]
+
+class IntelFortranCompiler(FortranCompiler):
+ std_warn_args = ['-warn', 'all']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'intel'
+
+ def get_module_outdir_args(self, path):
+ return ['-module', path]
+
+ def can_compile(self, src):
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f90':
+ return True
+ return False
+
+ def get_warn_args(self, level):
+ return IntelFortranCompiler.std_warn_args
+
+class PathScaleFortranCompiler(FortranCompiler):
+ std_warn_args = ['-fullwarn']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'pathscale'
+
+ def get_module_outdir_args(self, path):
+ return ['-module', path]
+
+ def can_compile(self, src):
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f90' or suffix == '.f95':
+ return True
+ return False
+
+ def get_std_warn_args(self, level):
+ return PathScaleFortranCompiler.std_warn_args
+
+class PGIFortranCompiler(FortranCompiler):
+ std_warn_args = ['-Minform=inform']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'pgi'
+
+ def get_module_outdir_args(self, path):
+ return ['-module', path]
+
+ def can_compile(self, src):
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f90' or suffix == '.f95':
+ return True
+ return False
+
+ def get_warn_args(self, level):
+ return PGIFortranCompiler.std_warn_args
+
+
+class Open64FortranCompiler(FortranCompiler):
+ std_warn_args = ['-fullwarn']
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'open64'
+
+ def get_module_outdir_args(self, path):
+ return ['-module', path]
+
+ def can_compile(self, src):
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f90' or suffix == '.f95':
+ return True
+ return False
+
+ def get_warn_args(self, level):
+ return Open64FortranCompiler.std_warn_args
+
+class NAGFortranCompiler(FortranCompiler):
+ std_warn_args = []
+
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ super().__init__(exelist, version, is_cross, exe_wrapper=None)
+ self.id = 'nagfor'
+
+ def get_module_outdir_args(self, path):
+ return ['-mdir', path]
+
+ def get_always_args(self):
+ return []
+
+ def can_compile(self, src):
+ suffix = os.path.splitext(src)[1].lower()
+ if suffix == '.f' or suffix == '.f90' or suffix == '.f95':
+ return True
+ return False
+
+ def get_warn_args(self, level):
+ return NAGFortranCompiler.std_warn_args
+
+
+class VisualStudioLinker():
+ always_args = ['/NOLOGO']
+ def __init__(self, exelist):
+ self.exelist = exelist
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_std_link_args(self):
+ return []
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_output_args(self, target):
+ return ['/OUT:' + target]
+
+ def get_coverage_link_args(self):
+ return []
+
+ def get_always_args(self):
+ return VisualStudioLinker.always_args
+
+ def get_linker_always_args(self):
+ return VisualStudioLinker.always_args
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return []
+
+ def thread_link_flags(self):
+ return []
+
+ def get_option_link_args(self, options):
+ return []
+
+ def unixtype_flags_to_native(self, args):
+ return args
+
+class ArLinker():
+ std_args = ['csr']
+
+ def __init__(self, exelist):
+ self.exelist = exelist
+ self.id = 'ar'
+
+ def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
+ return []
+
+ def get_exelist(self):
+ return self.exelist
+
+ def get_std_link_args(self):
+ return self.std_args
+
+ def get_output_args(self, target):
+ return [target]
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_linker_always_args(self):
+ return []
+
+ def get_coverage_link_args(self):
+ return []
+
+ def get_always_args(self):
+ return []
+
+ def thread_link_flags(self):
+ return []
+
+ def get_option_link_args(self, options):
+ return []
+
+ def unixtype_flags_to_native(self, args):
+ return args
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
new file mode 100644
index 0000000..5b1102c
--- /dev/null
+++ b/mesonbuild/coredata.py
@@ -0,0 +1,222 @@
+# Copyright 2012-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 pickle, os, uuid
+
+version = '0.29.0-research'
+
+build_types = ['plain', 'debug', 'debugoptimized', 'release']
+layouts = ['mirror', 'flat']
+warning_levels = ['1', '2', '3']
+libtypelist = ['shared', 'static']
+
+builtin_options = {'buildtype': True,
+ 'strip': True,
+ 'coverage': True,
+ 'pch': True,
+ 'unity': True,
+ 'prefix': True,
+ 'libdir' : True,
+ 'bindir' : True,
+ 'includedir' : True,
+ 'datadir' : True,
+ 'mandir' : True,
+ 'localedir' : True,
+ 'werror' : True,
+ 'warning_level': True,
+ 'layout' : True,
+ 'default_library': True,
+ }
+
+class MesonException(Exception):
+ def __init__(self, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+
+class UserOption:
+ def __init__(self, name, description, choices):
+ super().__init__()
+ self.name = name
+ self.choices = choices
+ self.description = description
+
+ def parse_string(self, valuestring):
+ return valuestring
+
+class UserStringOption(UserOption):
+ def __init__(self, name, description, value, choices=None):
+ super().__init__(name, description, choices)
+ self.set_value(value)
+
+ def validate(self, value):
+ if not isinstance(value, str):
+ raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(newvalue), self.name))
+ if self.name == 'prefix' and not os.path.isabs(value):
+ raise MesonException('Prefix option must be an absolute path.')
+ if self.name in ('libdir', 'bindir', 'includedir', 'datadir', 'mandir', 'localedir') \
+ and os.path.isabs(value):
+ raise MesonException('Option %s must not be an absolute path.' % self.name)
+
+ def set_value(self, newvalue):
+ self.validate(newvalue)
+ self.value = newvalue
+
+class UserBooleanOption(UserOption):
+ def __init__(self, name, description, value):
+ super().__init__(name, description, '[true, false]')
+ self.set_value(value)
+
+ def tobool(self, thing):
+ if isinstance(thing, bool):
+ return thing
+ if thing.lower() == 'true':
+ return True
+ if thing.lower() == 'false':
+ return False
+ raise MesonException('Value %s is not boolean (true or false).' % thing)
+
+ def set_value(self, newvalue):
+ self.value = self.tobool(newvalue)
+
+ def parse_string(self, valuestring):
+ if valuestring == 'false':
+ return False
+ if valuestring == 'true':
+ return True
+ raise MesonException('Value "%s" for boolean option "%s" is not a boolean.' % (valuestring, self.name))
+
+class UserComboOption(UserOption):
+ def __init__(self, name, description, choices, value):
+ super().__init__(name, description, choices)
+ if not isinstance(self.choices, list):
+ raise MesonException('Combo choices must be an array.')
+ for i in self.choices:
+ if not isinstance(i, str):
+ raise MesonException('Combo choice elements must be strings.')
+ self.set_value(value)
+
+ def set_value(self, newvalue):
+ if newvalue not in self.choices:
+ optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices])
+ raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (newvalue, self.name, optionsstring))
+ self.value = newvalue
+
+class UserStringArrayOption(UserOption):
+ def __init__(self, name, description, value, **kwargs):
+ super().__init__(name, description, kwargs.get('choices', []))
+ self.set_value(value)
+
+ def set_value(self, newvalue):
+ if isinstance(newvalue, str):
+ if not newvalue.startswith('['):
+ raise MesonException('Valuestring does not define an array: ' + newvalue)
+ newvalue = eval(newvalue, {}, {}) # Yes, it is unsafe.
+ if not isinstance(newvalue, list):
+ raise MesonException('String array value is not an array.')
+ for i in newvalue:
+ if not isinstance(i, str):
+ raise MesonException('String array element not a string.')
+ self.value = newvalue
+
+# This class contains all data that must persist over multiple
+# invocations of Meson. It is roughly the same thing as
+# cmakecache.
+
+class CoreData():
+
+ def __init__(self, options):
+ self.guid = str(uuid.uuid4()).upper()
+ self.test_guid = str(uuid.uuid4()).upper()
+ self.regen_guid = str(uuid.uuid4()).upper()
+ self.target_guids = {}
+ self.version = version
+ self.builtin_options = {}
+ self.init_builtins(options)
+ self.user_options = {}
+ self.compiler_options = {}
+ self.external_args = {} # These are set from "the outside" with e.g. mesonconf
+ self.external_link_args = {}
+ if options.cross_file is not None:
+ self.cross_file = os.path.join(os.getcwd(), options.cross_file)
+ else:
+ self.cross_file = None
+
+ self.compilers = {}
+ self.cross_compilers = {}
+ self.deps = {}
+ self.ext_progs = {}
+ self.modules = {}
+
+ def init_builtins(self, options):
+ self.builtin_options['prefix'] = UserStringOption('prefix', 'Installation prefix', options.prefix)
+ self.builtin_options['libdir'] = UserStringOption('libdir', 'Library dir', options.libdir)
+ self.builtin_options['bindir'] = UserStringOption('bindir', 'Executable dir', options.bindir)
+ self.builtin_options['includedir'] = UserStringOption('includedir', 'Include dir', options.includedir)
+ self.builtin_options['datadir'] = UserStringOption('datadir', 'Data directory', options.datadir)
+ self.builtin_options['mandir'] = UserStringOption('mandir', 'Man page dir', options.mandir)
+ self.builtin_options['localedir'] = UserStringOption('localedir', 'Locale dir', options.localedir)
+ self.builtin_options['backend'] = UserStringOption('backend', 'Backend to use', options.backend)
+ self.builtin_options['buildtype'] = UserComboOption('buildtype', 'Build type', build_types, options.buildtype)
+ self.builtin_options['strip'] = UserBooleanOption('strip', 'Strip on install', options.strip)
+ self.builtin_options['use_pch'] = UserBooleanOption('use_pch', 'Use precompiled headers', options.use_pch)
+ self.builtin_options['unity'] = UserBooleanOption('unity', 'Unity build', options.unity)
+ self.builtin_options['coverage'] = UserBooleanOption('coverage', 'Enable coverage', options.coverage)
+ self.builtin_options['warning_level'] = UserComboOption('warning_level', 'Warning level', warning_levels, options.warning_level)
+ self.builtin_options['werror'] = UserBooleanOption('werror', 'Warnings are errors', options.werror)
+ self.builtin_options['layout'] = UserComboOption('layout', 'Build dir layout', layouts, options.layout)
+ self.builtin_options['default_library'] = UserComboOption('default_library', 'Default_library type', libtypelist, options.default_library)
+
+ def get_builtin_option(self, optname):
+ if optname in self.builtin_options:
+ return self.builtin_options[optname].value
+ raise RuntimeError('Tried to get unknown builtin option %s' % optname)
+
+ def set_builtin_option(self, optname, value):
+ if optname in self.builtin_options:
+ self.builtin_options[optname].set_value(value)
+ else:
+ raise RuntimeError('Tried to set unknown builtin option %s' % optname)
+
+ def is_builtin_option(self, optname):
+ return optname in self.builtin_options
+
+def load(filename):
+ obj = pickle.load(open(filename, 'rb'))
+ if not isinstance(obj, CoreData):
+ raise RuntimeError('Core data file is corrupted.')
+ if obj.version != version:
+ raise RuntimeError('Build tree has been generated with Meson version %s, which is incompatible with current version %s.'%
+ (obj.version, version))
+ return obj
+
+def save(obj, filename):
+ if obj.version != version:
+ raise RuntimeError('Fatal version mismatch corruption.')
+ pickle.dump(obj, open(filename, 'wb'))
+
+forbidden_target_names = {'clean': None,
+ 'clean-gcno': None,
+ 'clean-gcda': None,
+ 'coverage-text': None,
+ 'coverage-xml': None,
+ 'coverage-html': None,
+ 'phony': None,
+ 'PHONY': None,
+ 'all': None,
+ 'test': None,
+ 'test-valgrind': None,
+ 'test-': None,
+ 'benchmark': None,
+ 'install': None,
+ 'build.ninja': None,
+ }
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
new file mode 100644
index 0000000..974559f
--- /dev/null
+++ b/mesonbuild/dependencies.py
@@ -0,0 +1,1120 @@
+# Copyright 2013-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.
+
+# This file contains the detection logic for external
+# dependencies. Mostly just uses pkg-config but also contains
+# custom logic for packages that don't provide them.
+
+# Currently one file, should probably be split into a
+# package before this gets too big.
+
+import re
+import os, stat, glob, subprocess, shutil
+from . coredata import MesonException
+from . import mlog
+from . import mesonlib
+
+class DependencyException(MesonException):
+ def __init__(self, *args, **kwargs):
+ MesonException.__init__(self, *args, **kwargs)
+
+class Dependency():
+ def __init__(self):
+ self.name = "null"
+ self.is_found = False
+
+ def get_compile_args(self):
+ return []
+
+ def get_link_args(self):
+ return []
+
+ def found(self):
+ return self.is_found
+
+ def get_sources(self):
+ """Source files that need to be added to the target.
+ As an example, gtest-all.cc when using GTest."""
+ return []
+
+ def get_name(self):
+ return self.name
+
+ def get_exe_args(self):
+ return []
+
+ def need_threads(self):
+ return False
+
+class InternalDependency():
+ def __init__(self, incdirs, libraries, sources, ext_deps):
+ super().__init__()
+ self.include_directories = incdirs
+ self.libraries = libraries
+ self.sources = sources
+ self.ext_deps = ext_deps
+
+class PkgConfigDependency(Dependency):
+ pkgconfig_found = None
+
+ def __init__(self, name, environment, kwargs):
+ Dependency.__init__(self)
+ self.is_libtool = False
+ self.required = kwargs.get('required', True)
+ if 'native' in kwargs and environment.is_cross_build():
+ want_cross = not kwargs['native']
+ else:
+ want_cross = environment.is_cross_build()
+ self.name = name
+ if PkgConfigDependency.pkgconfig_found is None:
+ self.check_pkgconfig()
+
+ self.is_found = False
+ if not PkgConfigDependency.pkgconfig_found:
+ if self.required:
+ raise DependencyException('Pkg-config not found.')
+ self.cargs = []
+ self.libs = []
+ return
+ if environment.is_cross_build() and want_cross:
+ if "pkgconfig" not in environment.cross_info.config["binaries"]:
+ raise DependencyException('Pkg-config binary missing from cross file.')
+ pkgbin = environment.cross_info.config["binaries"]['pkgconfig']
+ self.type_string = 'Cross'
+ else:
+ pkgbin = 'pkg-config'
+ self.type_string = 'Native'
+
+ mlog.debug('Determining dependency %s with pkg-config executable %s.' % (name, pkgbin))
+ self.pkgbin = pkgbin
+ p = subprocess.Popen([pkgbin, '--modversion', name],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ if self.required:
+ raise DependencyException('%s dependency %s not found.' % (self.type_string, name))
+ self.modversion = 'none'
+ self.cargs = []
+ self.libs = []
+ else:
+ self.modversion = out.decode().strip()
+ mlog.log('%s dependency' % self.type_string, mlog.bold(name), 'found:',
+ mlog.green('YES'), self.modversion)
+ self.version_requirement = kwargs.get('version', None)
+ if self.version_requirement is None:
+ self.is_found = True
+ else:
+ if not isinstance(self.version_requirement, str):
+ raise DependencyException('Version argument must be string.')
+ self.is_found = mesonlib.version_compare(self.modversion, self.version_requirement)
+ if not self.is_found and self.required:
+ raise DependencyException(
+ 'Invalid version of a dependency, needed %s %s found %s.' %
+ (name, self.version_requirement, self.modversion))
+ if not self.is_found:
+ return
+ p = subprocess.Popen([pkgbin, '--cflags', name], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate cargs for %s:\n\n%s' % \
+ (name, out.decode(errors='ignore')))
+ self.cargs = out.decode().split()
+
+ p = subprocess.Popen([pkgbin, '--libs', name], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate libs for %s:\n\n%s' % \
+ (name, out.decode(errors='ignore')))
+ self.libs = []
+ for lib in out.decode().split():
+ if lib.endswith(".la"):
+ shared_libname = self.extract_libtool_shlib(lib)
+ shared_lib = os.path.join(os.path.dirname(lib), shared_libname)
+ if not os.path.exists(shared_lib):
+ shared_lib = os.path.join(os.path.dirname(lib), ".libs", shared_libname)
+
+ if not os.path.exists(shared_lib):
+ raise DependencyException('Got a libtools specific "%s" dependencies'
+ 'but we could not compute the actual shared'
+ 'library path' % lib)
+ lib = shared_lib
+ self.is_libtool = True
+
+ self.libs.append(lib)
+
+ def get_variable(self, variable_name):
+ p = subprocess.Popen([self.pkgbin, '--variable=%s' % variable_name, self.name],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ if self.required:
+ raise DependencyException('%s dependency %s not found.' %
+ (self.type_string, self.name))
+ else:
+ variable = out.decode().strip()
+ mlog.debug('return of subprocess : %s' % variable)
+
+ return variable
+
+ def get_modversion(self):
+ return self.modversion
+
+ def get_compile_args(self):
+ return self.cargs
+
+ def get_link_args(self):
+ return self.libs
+
+ def check_pkgconfig(self):
+ try:
+ p = subprocess.Popen(['pkg-config', '--version'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode == 0:
+ mlog.log('Found pkg-config:', mlog.bold(shutil.which('pkg-config')),
+ '(%s)' % out.decode().strip())
+ PkgConfigDependency.pkgconfig_found = True
+ return
+ except Exception:
+ pass
+ PkgConfigDependency.pkgconfig_found = False
+ mlog.log('Found Pkg-config:', mlog.red('NO'))
+
+ def found(self):
+ return self.is_found
+
+ def extract_field(self, la_file, fieldname):
+ for line in open(la_file):
+ arr = line.strip().split('=')
+ if arr[0] == fieldname:
+ return arr[1][1:-1]
+ return None
+
+ def extract_dlname_field(self, la_file):
+ return self.extract_field(la_file, 'dlname')
+
+ def extract_libdir_field(self, la_file):
+ return self.extract_field(la_file, 'libdir')
+
+ def extract_libtool_shlib(self, la_file):
+ '''
+ Returns the path to the shared library
+ corresponding to this .la file
+ '''
+ dlname = self.extract_dlname_field(la_file)
+ if dlname is None:
+ return None
+
+ # Darwin uses absolute paths where possible; since the libtool files never
+ # contain absolute paths, use the libdir field
+ if mesonlib.is_osx():
+ dlbasename = os.path.basename(dlname)
+ libdir = self.extract_libdir_field(la_file)
+ if libdir is None:
+ return dlbasename
+ return os.path.join(libdir, dlbasename)
+ # From the comments in extract_libtool(), older libtools had
+ # a path rather than the raw dlname
+ return os.path.basename(dlname)
+
+class WxDependency(Dependency):
+ wx_found = None
+
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ if WxDependency.wx_found is None:
+ self.check_wxconfig()
+
+ if not WxDependency.wx_found:
+ raise DependencyException('Wx-config not found.')
+ self.is_found = False
+ p = subprocess.Popen([self.wxc, '--version'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ mlog.log('Dependency wxwidgets found:', mlog.red('NO'))
+ self.cargs = []
+ self.libs = []
+ else:
+ self.modversion = out.decode().strip()
+ version_req = kwargs.get('version', None)
+ if version_req is not None:
+ if not mesonlib.version_compare(self.modversion, version_req):
+ mlog.log('Wxwidgets version %s does not fullfill requirement %s' %\
+ (self.modversion, version_req))
+ return
+ mlog.log('Dependency wxwidgets found:', mlog.green('YES'))
+ self.is_found = True
+ self.requested_modules = self.get_requested(kwargs)
+ # wx-config seems to have a cflags as well but since it requires C++,
+ # this should be good, at least for now.
+ p = subprocess.Popen([self.wxc, '--cxxflags'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate cargs for wxwidgets.')
+ self.cargs = out.decode().split()
+
+ p = subprocess.Popen([self.wxc, '--libs'] + self.requested_modules,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode != 0:
+ raise DependencyException('Could not generate libs for wxwidgets.')
+ self.libs = out.decode().split()
+
+ def get_requested(self, kwargs):
+ modules = 'modules'
+ if not modules in kwargs:
+ return []
+ candidates = kwargs[modules]
+ if isinstance(candidates, str):
+ return [candidates]
+ for c in candidates:
+ if not isinstance(c, str):
+ raise DependencyException('wxwidgets module argument is not a string.')
+ return candidates
+
+ def get_modversion(self):
+ return self.modversion
+
+ def get_compile_args(self):
+ return self.cargs
+
+ def get_link_args(self):
+ return self.libs
+
+ def check_wxconfig(self):
+ for wxc in ['wx-config-3.0', 'wx-config']:
+ try:
+ p = subprocess.Popen([wxc, '--version'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = p.communicate()[0]
+ if p.returncode == 0:
+ mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)),
+ '(%s)' % out.decode().strip())
+ self.wxc = wxc
+ WxDependency.wx_found = True
+ return
+ except Exception:
+ pass
+ WxDependency.wxconfig_found = False
+ mlog.log('Found wx-config:', mlog.red('NO'))
+
+ def found(self):
+ return self.is_found
+
+class ExternalProgram():
+ def __init__(self, name, fullpath=None, silent=False, search_dir=None):
+ self.name = name
+ self.fullpath = None
+ if fullpath is not None:
+ if not isinstance(fullpath, list):
+ self.fullpath = [fullpath]
+ else:
+ self.fullpath = fullpath
+ else:
+ self.fullpath = [shutil.which(name)]
+ if self.fullpath[0] is None and search_dir is not None:
+ trial = os.path.join(search_dir, name)
+ suffix = os.path.splitext(trial)[-1].lower()[1:]
+ if mesonlib.is_windows() and (suffix == 'exe' or suffix == 'com'\
+ or suffix == 'bat'):
+ self.fullpath = [trial]
+ elif not mesonlib.is_windows() and os.access(trial, os.X_OK):
+ self.fullpath = [trial]
+ else:
+ # Now getting desperate. Maybe it is a script file that is a) not chmodded
+ # executable or b) we are on windows so they can't be directly executed.
+ try:
+ first_line = open(trial).readline().strip()
+ if first_line.startswith('#!'):
+ commands = first_line[2:].split('#')[0].strip().split()
+ if mesonlib.is_windows():
+ # Windows does not have /usr/bin.
+ commands[0] = commands[0].split('/')[-1]
+ if commands[0] == 'env':
+ commands = commands[1:]
+ self.fullpath = commands + [trial]
+ except Exception:
+ pass
+ if not silent:
+ if self.found():
+ mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
+ '(%s)' % ' '.join(self.fullpath))
+ else:
+ mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO'))
+
+ def found(self):
+ return self.fullpath[0] is not None
+
+ def get_command(self):
+ return self.fullpath
+
+ def get_name(self):
+ return self.name
+
+class ExternalLibrary(Dependency):
+ def __init__(self, name, fullpath=None, silent=False):
+ super().__init__()
+ self.name = name
+ self.fullpath = fullpath
+ if not silent:
+ if self.found():
+ mlog.log('Library', mlog.bold(name), 'found:', mlog.green('YES'),
+ '(%s)' % self.fullpath)
+ else:
+ mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO'))
+
+ def found(self):
+ return self.fullpath is not None
+
+ def get_link_args(self):
+ if self.found():
+ return [self.fullpath]
+ return []
+
+class BoostDependency(Dependency):
+ # Some boost libraries have different names for
+ # their sources and libraries. This dict maps
+ # between the two.
+ name2lib = {'test' : 'unit_test_framework'}
+
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.name = 'boost'
+ self.libdir = ''
+ try:
+ self.boost_root = os.environ['BOOST_ROOT']
+ if not os.path.isabs(self.boost_root):
+ raise DependencyException('BOOST_ROOT must be an absolute path.')
+ except KeyError:
+ self.boost_root = None
+ if self.boost_root is None:
+ if mesonlib.is_windows():
+ self.boost_root = self.detect_win_root()
+ self.incdir = self.boost_root
+ else:
+ self.incdir = '/usr/include'
+ else:
+ self.incdir = os.path.join(self.boost_root, 'include')
+ self.boost_inc_subdir = os.path.join(self.incdir, 'boost')
+ mlog.debug('Boost library root dir is', self.boost_root)
+ self.src_modules = {}
+ self.lib_modules = {}
+ self.lib_modules_mt = {}
+ self.detect_version()
+ self.requested_modules = self.get_requested(kwargs)
+ module_str = ', '.join(self.requested_modules)
+ if self.version is not None:
+ self.detect_src_modules()
+ self.detect_lib_modules()
+ self.validate_requested()
+ if self.boost_root is not None:
+ info = self.version + ', ' + self.boost_root
+ else:
+ info = self.version
+ mlog.log('Dependency Boost (%s) found:' % module_str, mlog.green('YES'),
+ '(' + info + ')')
+ else:
+ mlog.log("Dependency Boost (%s) found:" % module_str, mlog.red('NO'))
+
+ def detect_win_root(self):
+ globtext = 'c:\\local\\boost_*'
+ files = glob.glob(globtext)
+ if len(files) > 0:
+ return files[0]
+ return 'C:\\'
+
+ def get_compile_args(self):
+ args = []
+ if self.boost_root is not None:
+ if mesonlib.is_windows():
+ args.append('-I' + self.boost_root)
+ else:
+ args.append('-I' + os.path.join(self.boost_root, 'include'))
+ else:
+ args.append('-I' + self.incdir)
+ return args
+
+ def get_requested(self, kwargs):
+ candidates = kwargs.get('modules', [])
+ if isinstance(candidates, str):
+ return [candidates]
+ for c in candidates:
+ if not isinstance(c, str):
+ raise DependencyException('Boost module argument is not a string.')
+ return candidates
+
+ def validate_requested(self):
+ for m in self.requested_modules:
+ if m not in self.src_modules:
+ raise DependencyException('Requested Boost module "%s" not found.' % m)
+
+ def found(self):
+ return self.version is not None
+
+ def get_version(self):
+ return self.version
+
+ def detect_version(self):
+ try:
+ ifile = open(os.path.join(self.boost_inc_subdir, 'version.hpp'))
+ except FileNotFoundError:
+ self.version = None
+ return
+ for line in ifile:
+ if line.startswith("#define") and 'BOOST_LIB_VERSION' in line:
+ ver = line.split()[-1]
+ ver = ver[1:-1]
+ self.version = ver.replace('_', '.')
+ return
+ self.version = None
+
+ def detect_src_modules(self):
+ for entry in os.listdir(self.boost_inc_subdir):
+ entry = os.path.join(self.boost_inc_subdir, entry)
+ if stat.S_ISDIR(os.stat(entry).st_mode):
+ self.src_modules[os.path.split(entry)[-1]] = True
+
+ def detect_lib_modules(self):
+ if mesonlib.is_windows():
+ return self.detect_lib_modules_win()
+ return self.detect_lib_modules_nix()
+
+ def detect_lib_modules_win(self):
+ if mesonlib.is_32bit():
+ gl = 'lib32*'
+ else:
+ gl = 'lib64*'
+ libdir = glob.glob(os.path.join(self.boost_root, gl))
+ if len(libdir) == 0:
+ return
+ libdir = libdir[0]
+ self.libdir = libdir
+ globber = 'boost_*-gd-*.lib' # FIXME
+ for entry in glob.glob(os.path.join(libdir, globber)):
+ (_, fname) = os.path.split(entry)
+ base = fname.split('_', 1)[1]
+ modname = base.split('-', 1)[0]
+ self.lib_modules_mt[modname] = fname
+
+ def detect_lib_modules_nix(self):
+ libsuffix = None
+ if mesonlib.is_osx():
+ libsuffix = 'dylib'
+ else:
+ libsuffix = 'so'
+
+ globber = 'libboost_*.{}'.format(libsuffix)
+ if self.boost_root is None:
+ libdirs = mesonlib.get_library_dirs()
+ else:
+ libdirs = [os.path.join(self.boost_root, 'lib')]
+ for libdir in libdirs:
+ for entry in glob.glob(os.path.join(libdir, globber)):
+ lib = os.path.basename(entry)
+ name = lib.split('.')[0].split('_', 1)[-1]
+ # I'm not 100% sure what to do here. Some distros
+ # have modules such as thread only as -mt versions.
+ if entry.endswith('-mt.so'):
+ self.lib_modules_mt[name] = True
+ else:
+ self.lib_modules[name] = True
+
+ def get_win_link_args(self):
+ args = []
+ if self.boost_root:
+ args.append('-L' + self.libdir)
+ for module in self.requested_modules:
+ module = BoostDependency.name2lib.get(module, module)
+ if module in self.lib_modules_mt:
+ args.append(self.lib_modules_mt[module])
+ return args
+
+ def get_link_args(self):
+ if mesonlib.is_windows():
+ return self.get_win_link_args()
+ args = []
+ if self.boost_root:
+ args.append('-L' + os.path.join(self.boost_root, 'lib'))
+ for module in self.requested_modules:
+ module = BoostDependency.name2lib.get(module, module)
+ if module in self.lib_modules or module in self.lib_modules_mt:
+ linkcmd = '-lboost_' + module
+ args.append(linkcmd)
+ # FIXME a hack, but Boost's testing framework has a lot of
+ # different options and it's hard to determine what to do
+ # without feedback from actual users. Update this
+ # as we get more bug reports.
+ if module == 'unit_testing_framework':
+ args.append('-lboost_test_exec_monitor')
+ elif module + '-mt' in self.lib_modules_mt:
+ linkcmd = '-lboost_' + module + '-mt'
+ args.append(linkcmd)
+ if module == 'unit_testing_framework':
+ args.append('-lboost_test_exec_monitor-mt')
+ return args
+
+ def get_sources(self):
+ return []
+
+ def need_threads(self):
+ return 'thread' in self.requested_modules
+
+class GTestDependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.main = kwargs.get('main', False)
+ self.name = 'gtest'
+ self.libname = 'libgtest.so'
+ self.libmain_name = 'libgtest_main.so'
+ self.include_dir = '/usr/include'
+ self.src_include_dir = '/usr/src/gtest'
+ self.src_dir = '/usr/src/gtest/src'
+ self.all_src = mesonlib.File.from_absolute_file(
+ os.path.join(self.src_dir, 'gtest-all.cc'))
+ self.main_src = mesonlib.File.from_absolute_file(
+ os.path.join(self.src_dir, 'gtest_main.cc'))
+ self.detect()
+
+ def found(self):
+ return self.is_found
+
+ def detect(self):
+ trial_dirs = mesonlib.get_library_dirs()
+ glib_found = False
+ gmain_found = False
+ for d in trial_dirs:
+ if os.path.isfile(os.path.join(d, self.libname)):
+ glib_found = True
+ if os.path.isfile(os.path.join(d, self.libmain_name)):
+ gmain_found = True
+ if glib_found and gmain_found:
+ self.is_found = True
+ self.compile_args = []
+ self.link_args = ['-lgtest']
+ if self.main:
+ self.link_args.append('-lgtest_main')
+ self.sources = []
+ mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
+ elif os.path.exists(self.src_dir):
+ self.is_found = True
+ self.compile_args = ['-I' + self.src_include_dir]
+ self.link_args = []
+ if self.main:
+ self.sources = [self.all_src, self.main_src]
+ else:
+ self.sources = [self.all_src]
+ mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
+ else:
+ mlog.log('Dependency GTest found:', mlog.red('NO'))
+ self.is_found = False
+ return self.is_found
+
+ def get_compile_args(self):
+ arr = []
+ if self.include_dir != '/usr/include':
+ arr.append('-I' + self.include_dir)
+ arr.append('-I' + self.src_include_dir)
+ return arr
+
+ def get_link_args(self):
+ return self.link_args
+ def get_version(self):
+ return '1.something_maybe'
+ def get_sources(self):
+ return self.sources
+
+ def need_threads(self):
+ return True
+
+class GMockDependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ # GMock may be a library or just source.
+ # Work with both.
+ self.name = 'gmock'
+ self.libname = 'libgmock.so'
+ trial_dirs = mesonlib.get_library_dirs()
+ gmock_found = False
+ for d in trial_dirs:
+ if os.path.isfile(os.path.join(d, self.libname)):
+ gmock_found = True
+ if gmock_found:
+ self.is_found = True
+ self.compile_args = []
+ self.link_args = ['-lgmock']
+ self.sources = []
+ mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
+ return
+
+ for d in ['/usr/src/gmock/src', '/usr/src/gmock']:
+ if os.path.exists(d):
+ self.is_found = True
+ # Yes, we need both because there are multiple
+ # versions of gmock that do different things.
+ self.compile_args = ['-I/usr/src/gmock', '-I/usr/src/gmock/src']
+ self.link_args = []
+ all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
+ main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
+ if kwargs.get('main', False):
+ self.sources = [all_src, main_src]
+ else:
+ self.sources = [all_src]
+ mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
+ return
+
+ mlog.log('Dependency GMock found:', mlog.red('NO'))
+ self.is_found = False
+
+ def get_version(self):
+ return '1.something_maybe'
+
+ def get_compile_args(self):
+ return self.compile_args
+
+ def get_sources(self):
+ return self.sources
+
+ def get_link_args(self):
+ return self.link_args
+
+ def found(self):
+ return self.is_found
+
+class Qt5Dependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.name = 'qt5'
+ self.root = '/usr'
+ mods = kwargs.get('modules', [])
+ self.cargs = []
+ self.largs = []
+ self.is_found = False
+ if isinstance(mods, str):
+ mods = [mods]
+ if len(mods) == 0:
+ raise DependencyException('No Qt5 modules specified.')
+ type_text = 'native'
+ if environment.is_cross_build() and kwargs.get('native', False):
+ type_text = 'cross'
+ self.pkgconfig_detect(mods, environment, kwargs)
+ elif not environment.is_cross_build() and shutil.which('pkg-config') is not None:
+ self.pkgconfig_detect(mods, environment, kwargs)
+ elif shutil.which('qmake') is not None:
+ self.qmake_detect(mods, kwargs)
+ else:
+ self.version = 'none'
+ if not self.is_found:
+ mlog.log('Qt5 %s dependency found: ' % type_text, mlog.red('NO'))
+ else:
+ mlog.log('Qt5 %s dependency found: ' % type_text, mlog.green('YES'))
+
+ def pkgconfig_detect(self, mods, environment, kwargs):
+ modules = []
+ for module in mods:
+ modules.append(PkgConfigDependency('Qt5' + module, environment, kwargs))
+ for m in modules:
+ self.cargs += m.get_compile_args()
+ self.largs += m.get_link_args()
+ self.is_found = True
+ self.version = modules[0].modversion
+
+ def qmake_detect(self, mods, kwargs):
+ pc = subprocess.Popen(['qmake', '-v'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (stdo, _) = pc.communicate()
+ if pc.returncode != 0:
+ return
+ stdo = stdo.decode()
+ if not 'version 5' in stdo:
+ mlog.log('QMake is not for Qt5.')
+ return
+ self.version = re.search('5(\.\d+)+', stdo).group(0)
+ (stdo, _) = subprocess.Popen(['qmake', '-query'], stdout=subprocess.PIPE).communicate()
+ qvars = {}
+ for line in stdo.decode().split('\n'):
+ line = line.strip()
+ if line == '':
+ continue
+ (k, v) = tuple(line.split(':', 1))
+ qvars[k] = v
+ if mesonlib.is_osx():
+ return self.framework_detect(qvars, mods, kwargs)
+ incdir = qvars['QT_INSTALL_HEADERS']
+ self.cargs.append('-I' + incdir)
+ libdir = qvars['QT_INSTALL_LIBS']
+ bindir = qvars['QT_INSTALL_BINS']
+ #self.largs.append('-L' + libdir)
+ for module in mods:
+ mincdir = os.path.join(incdir, 'Qt' + module)
+ self.cargs.append('-I' + mincdir)
+ libfile = os.path.join(libdir, 'Qt5' + module + '.lib')
+ if not os.path.isfile(libfile):
+ # MinGW links directly to .dll, not to .lib.
+ libfile = os.path.join(bindir, 'Qt5' + module + '.dll')
+ self.largs.append(libfile)
+ self.is_found = True
+
+ def framework_detect(self, qvars, modules, kwargs):
+ libdir = qvars['QT_INSTALL_LIBS']
+ for m in modules:
+ fname = 'Qt' + m
+ fwdep = ExtraFrameworkDependency(fname, kwargs.get('required', True), libdir)
+ self.cargs.append('-F' + libdir)
+ if fwdep.found():
+ self.is_found = True
+ self.cargs += fwdep.get_compile_args()
+ self.largs += fwdep.get_link_args()
+
+
+ def get_version(self):
+ return self.version
+
+ def get_compile_args(self):
+ return self.cargs
+
+ def get_sources(self):
+ return []
+
+ def get_link_args(self):
+ return self.largs
+
+ def found(self):
+ return self.is_found
+
+ def get_exe_args(self):
+ # Originally this was -fPIE but nowadays the default
+ # for upstream and distros seems to be -reduce-relocations
+ # which requires -fPIC. This may cause a performance
+ # penalty when using self-built Qt or on platforms
+ # where -fPIC is not required. If this is an issue
+ # for you, patches are welcome.
+ # Fix this to be more portable, especially to MSVC.
+ return ['-fPIC']
+
+class Qt4Dependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.name = 'qt4'
+ self.root = '/usr'
+ self.modules = []
+ mods = kwargs.get('modules', [])
+ if isinstance(mods, str):
+ mods = [mods]
+ for module in mods:
+ self.modules.append(PkgConfigDependency('Qt' + module, environment, kwargs))
+ if len(self.modules) == 0:
+ raise DependencyException('No Qt4 modules specified.')
+
+ def get_version(self):
+ return self.modules[0].get_version()
+
+ def get_compile_args(self):
+ args = []
+ for m in self.modules:
+ args += m.get_compile_args()
+ return args
+
+ def get_sources(self):
+ return []
+
+ def get_link_args(self):
+ args = []
+ for module in self.modules:
+ args += module.get_link_args()
+ return args
+
+ def found(self):
+ for i in self.modules:
+ if not i.found():
+ return False
+ return True
+
+class GnuStepDependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.modules = kwargs.get('modules', [])
+ self.detect()
+
+ def detect(self):
+ confprog = 'gnustep-config'
+ try:
+ gp = subprocess.Popen([confprog, '--help'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ gp.communicate()
+ except FileNotFoundError:
+ self.args = None
+ mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)')
+ return
+ if gp.returncode != 0:
+ self.args = None
+ mlog.log('Dependency GnuStep found:', mlog.red('NO'))
+ return
+ if 'gui' in self.modules:
+ arg = '--gui-libs'
+ else:
+ arg = '--base-libs'
+ fp = subprocess.Popen([confprog, '--objc-flags'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (flagtxt, flagerr) = fp.communicate()
+ flagtxt = flagtxt.decode()
+ flagerr = flagerr.decode()
+ if fp.returncode != 0:
+ raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr))
+ args = flagtxt.split()
+ self.args = self.filter_arsg(args)
+ fp = subprocess.Popen([confprog, arg],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (libtxt, liberr) = fp.communicate()
+ libtxt = libtxt.decode()
+ liberr = liberr.decode()
+ if fp.returncode != 0:
+ raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr))
+ self.libs = self.weird_filter(libtxt.split())
+ mlog.log('Dependency GnuStep found:', mlog.green('YES'))
+
+ def weird_filter(self, elems):
+ """When building packages, the output of the enclosing Make
+is sometimes mixed among the subprocess output. I have no idea
+why. As a hack filter out everything that is not a flag."""
+ return [e for e in elems if e.startswith('-')]
+
+
+ def filter_arsg(self, args):
+ """gnustep-config returns a bunch of garbage args such
+ as -O2 and so on. Drop everything that is not needed."""
+ result = []
+ for f in args:
+ if f.startswith('-D') or f.startswith('-f') or \
+ f.startswith('-I') or f == '-pthread' or\
+ (f.startswith('-W') and not f == '-Wall'):
+ result.append(f)
+ return result
+
+ def found(self):
+ return self.args is not None
+
+ def get_compile_args(self):
+ if self.args is None:
+ return []
+ return self.args
+
+ def get_link_args(self):
+ return self.libs
+
+class AppleFrameworks(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ modules = kwargs.get('modules', [])
+ if isinstance(modules, str):
+ modules = [modules]
+ if len(modules) == 0:
+ raise DependencyException("AppleFrameworks dependency requires at least one module.")
+ self.frameworks = modules
+
+ def get_link_args(self):
+ args = []
+ for f in self.frameworks:
+ args.append('-framework')
+ args.append(f)
+ return args
+
+ def found(self):
+ return mesonlib.is_osx()
+
+class GLDependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.is_found = False
+ self.cargs = []
+ self.linkargs = []
+ try:
+ pcdep = PkgConfigDependency('gl', environment, kwargs)
+ if pcdep.found():
+ self.is_found = True
+ self.cargs = pcdep.get_compile_args()
+ self.linkargs = pcdep.get_link_args()
+ return
+ except Exception:
+ pass
+ if mesonlib.is_osx():
+ self.is_found = True
+ self.linkargs = ['-framework', 'OpenGL']
+ return
+ if mesonlib.is_windows():
+ self.is_found = True
+ self.linkargs = ['-lopengl32']
+ return
+
+ def get_link_args(self):
+ return self.linkargs
+
+# There are three different ways of depending on SDL2:
+# sdl2-config, pkg-config and OSX framework
+class SDL2Dependency(Dependency):
+ def __init__(self, environment, kwargs):
+ Dependency.__init__(self)
+ self.is_found = False
+ self.cargs = []
+ self.linkargs = []
+ sdlconf = shutil.which('sdl2-config')
+ if sdlconf:
+ pc = subprocess.Popen(['sdl2-config', '--cflags'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL)
+ (stdo, _) = pc.communicate()
+ self.cargs = stdo.decode().strip().split()
+ pc = subprocess.Popen(['sdl2-config', '--libs'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL)
+ (stdo, _) = pc.communicate()
+ self.linkargs = stdo.decode().strip().split()
+ self.is_found = True
+ mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), '(%s)' % sdlconf)
+ return
+ try:
+ pcdep = PkgConfigDependency('sdl2', kwargs)
+ if pcdep.found():
+ self.is_found = True
+ self.cargs = pcdep.get_compile_args()
+ self.linkargs = pcdep.get_link_args()
+ return
+ except Exception:
+ pass
+ if mesonlib.is_osx():
+ fwdep = ExtraFrameworkDependency('sdl2', kwargs.get('required', True))
+ if fwdep.found():
+ self.is_found = True
+ self.cargs = fwdep.get_compile_args()
+ self.linkargs = fwdep.get_link_args()
+ return
+ mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.red('NO'))
+
+ def get_compile_args(self):
+ return self.cargs
+
+ def get_link_args(self):
+ return self.linkargs
+
+ def found(self):
+ return self.is_found
+
+class ExtraFrameworkDependency(Dependency):
+ def __init__(self, name, required, path=None):
+ Dependency.__init__(self)
+ self.name = None
+ self.detect(name, path)
+ if self.found():
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.green('YES'),
+ os.path.join(self.path, self.name))
+ else:
+ mlog.log('Dependency', name, 'found:', mlog.red('NO'))
+
+ def detect(self, name, path):
+ lname = name.lower()
+ if path is None:
+ paths = ['/Library/Frameworks']
+ else:
+ paths = [path]
+ for p in paths:
+ for d in os.listdir(p):
+ fullpath = os.path.join(p, d)
+ if lname != d.split('.')[0].lower():
+ continue
+ if not stat.S_ISDIR(os.stat(fullpath).st_mode):
+ continue
+ self.path = p
+ self.name = d
+ return
+
+ def get_compile_args(self):
+ if self.found():
+ return ['-I' + os.path.join(self.path, self.name, 'Headers')]
+ return []
+
+ def get_link_args(self):
+ if self.found():
+ return ['-F' + self.path, '-framework', self.name.split('.')[0]]
+ return []
+
+ def found(self):
+ return self.name is not None
+
+class ThreadDependency(Dependency):
+ def __init__(self, environment, kwargs):
+ super().__init__()
+ self.name = 'threads'
+ self.is_found = True
+ mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
+
+ def need_threads(self):
+ return True
+
+def get_dep_identifier(name, kwargs):
+ elements = [name]
+ modlist = kwargs.get('modules', [])
+ if isinstance(modlist, str):
+ modlist = [modlist]
+ for module in modlist:
+ elements.append(module)
+ return '/'.join(elements) + '/main' + str(kwargs.get('main', False))
+
+def find_external_dependency(name, environment, kwargs):
+ required = kwargs.get('required', True)
+ if not isinstance(required, bool):
+ raise DependencyException('Keyword "required" must be a boolean.')
+ lname = name.lower()
+ if lname in packages:
+ dep = packages[lname](environment, kwargs)
+ if required and not dep.found():
+ raise DependencyException('Dependency "%s" not found' % name)
+ return dep
+ pkg_exc = None
+ pkgdep = None
+ try:
+ pkgdep = PkgConfigDependency(name, environment, kwargs)
+ if pkgdep.found():
+ return pkgdep
+ except Exception as e:
+ pkg_exc = e
+ if mesonlib.is_osx():
+ fwdep = ExtraFrameworkDependency(name, required)
+ if required and not fwdep.found():
+ raise DependencyException('Dependency "%s" not found' % name)
+ return fwdep
+ if pkg_exc is not None:
+ raise pkg_exc
+ mlog.log('Dependency', mlog.bold(name), 'found:', mlog.red('NO'))
+ return pkgdep
+
+# This has to be at the end so the classes it references
+# are defined.
+packages = {'boost': BoostDependency,
+ 'gtest': GTestDependency,
+ 'gmock': GMockDependency,
+ 'qt5': Qt5Dependency,
+ 'qt4': Qt4Dependency,
+ 'gnustep': GnuStepDependency,
+ 'appleframeworks': AppleFrameworks,
+ 'wxwidgets' : WxDependency,
+ 'sdl2' : SDL2Dependency,
+ 'gl' : GLDependency,
+ 'threads' : ThreadDependency,
+ }
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
new file mode 100644
index 0000000..8df856c
--- /dev/null
+++ b/mesonbuild/environment.py
@@ -0,0 +1,673 @@
+# Copyright 2012-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 os, re, subprocess
+from . import coredata, mesonlib
+from .compilers import *
+import configparser
+
+build_filename = 'meson.build'
+
+class EnvironmentException(coredata.MesonException):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+def find_coverage_tools():
+ gcovr_exe = 'gcovr'
+ lcov_exe = 'lcov'
+ genhtml_exe = 'genhtml'
+
+ if not mesonlib.exe_exists([gcovr_exe, '--version']):
+ gcovr_exe = None
+ if not mesonlib.exe_exists([lcov_exe, '--version']):
+ lcov_exe = None
+ if not mesonlib.exe_exists([genhtml_exe, '--version']):
+ genhtml_exe = None
+ return (gcovr_exe, lcov_exe, genhtml_exe)
+
+def find_valgrind():
+ valgrind_exe = 'valgrind'
+ if not mesonlib.exe_exists([valgrind_exe, '--version']):
+ valgrind_exe = None
+ return valgrind_exe
+
+def detect_ninja():
+ for n in ['ninja', 'ninja-build']:
+ try:
+ p = subprocess.Popen([n, '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except FileNotFoundError:
+ continue
+ p.communicate()
+ if p.returncode == 0:
+ return n
+
+
+class Environment():
+ private_dir = 'meson-private'
+ log_dir = 'meson-logs'
+ coredata_file = os.path.join(private_dir, 'coredata.dat')
+ version_regex = '\d+(\.\d+)+(-[a-zA-Z0-9]+)?'
+ def __init__(self, source_dir, build_dir, main_script_file, options):
+ assert(os.path.isabs(main_script_file))
+ assert(not os.path.islink(main_script_file))
+ self.source_dir = source_dir
+ self.build_dir = build_dir
+ self.meson_script_file = main_script_file
+ self.scratch_dir = os.path.join(build_dir, Environment.private_dir)
+ self.log_dir = os.path.join(build_dir, Environment.log_dir)
+ os.makedirs(self.scratch_dir, exist_ok=True)
+ os.makedirs(self.log_dir, exist_ok=True)
+ try:
+ cdf = os.path.join(self.get_build_dir(), Environment.coredata_file)
+ self.coredata = coredata.load(cdf)
+ self.first_invocation = False
+ except FileNotFoundError:
+ self.coredata = coredata.CoreData(options)
+ self.first_invocation = True
+ if self.coredata.cross_file:
+ self.cross_info = CrossBuildInfo(self.coredata.cross_file)
+ else:
+ self.cross_info = None
+ self.cmd_line_options = options
+
+ # List of potential compilers.
+ if mesonlib.is_windows():
+ self.default_c = ['cl', 'cc', 'gcc', 'clang']
+ self.default_cpp = ['cl', 'c++', 'g++', 'clang++']
+ else:
+ self.default_c = ['cc']
+ self.default_cpp = ['c++']
+ self.default_objc = ['cc']
+ self.default_objcpp = ['c++']
+ self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77']
+ self.default_static_linker = 'ar'
+ self.vs_static_linker = 'lib'
+
+ cross = self.is_cross_build()
+ if (not cross and mesonlib.is_windows()) \
+ or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'windows'):
+ self.exe_suffix = 'exe'
+ self.import_lib_suffix = 'lib'
+ self.shared_lib_suffix = 'dll'
+ self.shared_lib_prefix = ''
+ self.static_lib_suffix = 'lib'
+ self.static_lib_prefix = ''
+ self.object_suffix = 'obj'
+ else:
+ self.exe_suffix = ''
+ if (not cross and mesonlib.is_osx()) or \
+ (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'darwin'):
+ self.shared_lib_suffix = 'dylib'
+ else:
+ self.shared_lib_suffix = 'so'
+ self.shared_lib_prefix = 'lib'
+ self.static_lib_suffix = 'a'
+ self.static_lib_prefix = 'lib'
+ self.object_suffix = 'o'
+ self.import_lib_suffix = self.shared_lib_suffix
+
+ def is_cross_build(self):
+ return self.cross_info is not None
+
+ def generating_finished(self):
+ cdf = os.path.join(self.get_build_dir(), Environment.coredata_file)
+ coredata.save(self.coredata, cdf)
+
+ def get_script_dir(self):
+ return os.path.join(os.path.dirname(self.meson_script_file), '../scripts')
+
+ def get_log_dir(self):
+ return self.log_dir
+
+ def get_coredata(self):
+ return self.coredata
+
+ def get_build_command(self):
+ return self.meson_script_file
+
+ def is_header(self, fname):
+ return is_header(fname)
+
+ def is_source(self, fname):
+ return is_source(fname)
+
+ def is_object(self, fname):
+ return is_object(fname)
+
+ def is_library(self, fname):
+ return is_library(fname)
+
+ def merge_options(self, options):
+ for (name, value) in options.items():
+ if name not in self.coredata.user_options:
+ self.coredata.user_options[name] = value
+ else:
+ oldval = self.coredata.user_options[name]
+ if type(oldval) != type(value):
+ self.coredata.user_options[name] = value
+
+ def detect_c_compiler(self, want_cross):
+ evar = 'CC'
+ if self.is_cross_build() and want_cross:
+ compilers = [self.cross_info.config['binaries']['c']]
+ ccache = []
+ is_cross = True
+ exe_wrap = self.cross_info.config['binaries'].get('exe_wrapper', None)
+ elif evar in os.environ:
+ compilers = os.environ[evar].split()
+ ccache = []
+ is_cross = False
+ exe_wrap = None
+ else:
+ compilers = self.default_c
+ ccache = self.detect_ccache()
+ is_cross = False
+ exe_wrap = None
+ for compiler in compilers:
+ try:
+ basename = os.path.basename(compiler).lower()
+ if basename == 'cl' or basename == 'cl.exe':
+ arg = '/?'
+ else:
+ arg = '--version'
+ p = subprocess.Popen([compiler] + [arg], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except OSError:
+ continue
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'apple' in out and 'Free Software Foundation' in out:
+ return GnuCCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap)
+ if (out.startswith('cc') or 'gcc' in out) and \
+ 'Free Software Foundation' in out:
+ lowerout = out.lower()
+ if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower():
+ gtype = GCC_MINGW
+ else:
+ gtype = GCC_STANDARD
+ return GnuCCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
+ if 'clang' in out:
+ return ClangCCompiler(ccache + [compiler], version, is_cross, exe_wrap)
+ if 'Microsoft' in out or 'Microsoft' in err:
+ # Visual Studio prints version number to stderr but
+ # everything else to stdout. Why? Lord only knows.
+ version = re.search(Environment.version_regex, err).group()
+ return VisualStudioCCompiler([compiler], version, is_cross, exe_wrap)
+ raise EnvironmentException('Unknown compiler(s): "' + ', '.join(compilers) + '"')
+
+ def detect_fortran_compiler(self, want_cross):
+ evar = 'FC'
+ if self.is_cross_build() and want_cross:
+ compilers = [self.cross_info['fortran']]
+ is_cross = True
+ exe_wrap = self.cross_info.get('exe_wrapper', None)
+ elif evar in os.environ:
+ compilers = os.environ[evar].split()
+ is_cross = False
+ exe_wrap = None
+ else:
+ compilers = self.default_fortran
+ is_cross = False
+ exe_wrap = None
+ for compiler in compilers:
+ for arg in ['--version', '-V']:
+ try:
+ p = subprocess.Popen([compiler] + [arg],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except OSError:
+ continue
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+
+ version = 'unknown version'
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+
+ if 'GNU Fortran' in out:
+ return GnuFortranCompiler([compiler], version, GCC_STANDARD, is_cross, exe_wrap)
+
+ if 'G95' in out:
+ return G95FortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'Sun Fortran' in err:
+ version = 'unknown version'
+ vmatch = re.search(Environment.version_regex, err)
+ if vmatch:
+ version = vmatch.group(0)
+ return SunFortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'ifort (IFORT)' in out:
+ return IntelFortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'PathScale EKOPath(tm)' in err:
+ return PathScaleFortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'pgf90' in out:
+ return PGIFortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'Open64 Compiler Suite' in err:
+ return Open64FortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ if 'NAG Fortran' in err:
+ return NAGFortranCompiler([compiler], version, is_cross, exe_wrap)
+
+ raise EnvironmentException('Unknown compiler(s): "' + ', '.join(compilers) + '"')
+
+ def get_scratch_dir(self):
+ return self.scratch_dir
+
+ def get_depfixer(self):
+ path = os.path.split(__file__)[0]
+ return os.path.join(path, 'depfixer.py')
+
+ def detect_cpp_compiler(self, want_cross):
+ evar = 'CXX'
+ if self.is_cross_build() and want_cross:
+ compilers = [self.cross_info.config['binaries']['cpp']]
+ ccache = []
+ is_cross = True
+ exe_wrap = self.cross_info.config['binaries'].get('exe_wrapper', None)
+ elif evar in os.environ:
+ compilers = os.environ[evar].split()
+ ccache = []
+ is_cross = False
+ exe_wrap = None
+ else:
+ compilers = self.default_cpp
+ ccache = self.detect_ccache()
+ is_cross = False
+ exe_wrap = None
+ for compiler in compilers:
+ basename = os.path.basename(compiler).lower()
+ if basename == 'cl' or basename == 'cl.exe':
+ arg = '/?'
+ else:
+ arg = '--version'
+ try:
+ p = subprocess.Popen([compiler, arg],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except OSError:
+ continue
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'apple' in out and 'Free Software Foundation' in out:
+ return GnuCPPCompiler(ccache + [compiler], version, GCC_OSX, is_cross, exe_wrap)
+ if (out.startswith('c++ ') or 'g++' in out or 'GCC' in out) and \
+ 'Free Software Foundation' in out:
+ lowerout = out.lower()
+ if 'mingw' in lowerout or 'msys' in lowerout or 'mingw' in compiler.lower():
+ gtype = GCC_MINGW
+ else:
+ gtype = GCC_STANDARD
+ return GnuCPPCompiler(ccache + [compiler], version, gtype, is_cross, exe_wrap)
+ if 'clang' in out:
+ return ClangCPPCompiler(ccache + [compiler], version, is_cross, exe_wrap)
+ if 'Microsoft' in out or 'Microsoft' in err:
+ version = re.search(Environment.version_regex, err).group()
+ return VisualStudioCPPCompiler([compiler], version, is_cross, exe_wrap)
+ raise EnvironmentException('Unknown compiler(s) "' + ', '.join(compilers) + '"')
+
+ def detect_objc_compiler(self, want_cross):
+ if self.is_cross_build() and want_cross:
+ exelist = [self.cross_info['objc']]
+ is_cross = True
+ exe_wrap = self.cross_info.get('exe_wrapper', None)
+ else:
+ exelist = self.get_objc_compiler_exelist()
+ is_cross = False
+ exe_wrap = None
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute ObjC compiler "%s"' % ' '.join(exelist))
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if (out.startswith('cc ') or 'gcc' in out) and \
+ 'Free Software Foundation' in out:
+ return GnuObjCCompiler(exelist, version, is_cross, exe_wrap)
+ if out.startswith('Apple LLVM'):
+ return ClangObjCCompiler(exelist, version, is_cross, exe_wrap)
+ if 'apple' in out and 'Free Software Foundation' in out:
+ return GnuObjCCompiler(exelist, version, is_cross, exe_wrap)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_objcpp_compiler(self, want_cross):
+ if self.is_cross_build() and want_cross:
+ exelist = [self.cross_info['objcpp']]
+ is_cross = True
+ exe_wrap = self.cross_info.get('exe_wrapper', None)
+ else:
+ exelist = self.get_objcpp_compiler_exelist()
+ is_cross = False
+ exe_wrap = None
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute ObjC++ compiler "%s"' % ' '.join(exelist))
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if (out.startswith('c++ ') or out.startswith('g++')) and \
+ 'Free Software Foundation' in out:
+ return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap)
+ if out.startswith('Apple LLVM'):
+ return ClangObjCPPCompiler(exelist, version, is_cross, exe_wrap)
+ if 'apple' in out and 'Free Software Foundation' in out:
+ return GnuObjCPPCompiler(exelist, version, is_cross, exe_wrap)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_java_compiler(self):
+ exelist = ['javac']
+ try:
+ p = subprocess.Popen(exelist + ['-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute Java compiler "%s"' % ' '.join(exelist))
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, err)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'javac' in err:
+ return JavaCompiler(exelist, version)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_cs_compiler(self):
+ exelist = ['mcs']
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute C# compiler "%s"' % ' '.join(exelist))
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'Mono' in out:
+ return MonoCompiler(exelist, version)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_vala_compiler(self):
+ exelist = ['valac']
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute Vala compiler "%s"' % ' '.join(exelist))
+ (out, _) = p.communicate()
+ out = out.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'Vala' in out:
+ return ValaCompiler(exelist, version)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_rust_compiler(self):
+ exelist = ['rustc']
+ try:
+ p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute Rust compiler "%s"' % ' '.join(exelist))
+ (out, _) = p.communicate()
+ out = out.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, out)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'rustc' in out:
+ return RustCompiler(exelist, version)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_swift_compiler(self):
+ exelist = ['swiftc']
+ try:
+ p = subprocess.Popen(exelist + ['-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist))
+ (_, err) = p.communicate()
+ err = err.decode(errors='ignore')
+ vmatch = re.search(Environment.version_regex, err)
+ if vmatch:
+ version = vmatch.group(0)
+ else:
+ version = 'unknown version'
+ if 'Swift' in err:
+ return SwiftCompiler(exelist, version)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ def detect_static_linker(self, compiler):
+ if compiler.is_cross:
+ linker = self.cross_info.config['binaries']['ar']
+ else:
+ evar = 'AR'
+ if evar in os.environ:
+ linker = os.environ[evar].strip()
+ if isinstance(compiler, VisualStudioCCompiler):
+ linker= self.vs_static_linker
+ else:
+ linker = self.default_static_linker
+ basename = os.path.basename(linker).lower()
+ if basename == 'lib' or basename == 'lib.exe':
+ arg = '/?'
+ else:
+ arg = '--version'
+ try:
+ p = subprocess.Popen([linker, arg], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ raise EnvironmentException('Could not execute static linker "%s".' % linker)
+ (out, err) = p.communicate()
+ out = out.decode(errors='ignore')
+ err = err.decode(errors='ignore')
+ if '/OUT:' in out or '/OUT:' in err:
+ return VisualStudioLinker([linker])
+ if p.returncode == 0:
+ return ArLinker([linker])
+ if p.returncode == 1 and err.startswith('usage'): # OSX
+ return ArLinker([linker])
+ raise EnvironmentException('Unknown static linker "%s"' % linker)
+
+ def detect_ccache(self):
+ try:
+ has_ccache = subprocess.call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ has_ccache = 1
+ if has_ccache == 0:
+ cmdlist = ['ccache']
+ else:
+ cmdlist = []
+ return cmdlist
+
+ def get_objc_compiler_exelist(self):
+ ccachelist = self.detect_ccache()
+ evar = 'OBJCC'
+ if evar in os.environ:
+ return os.environ[evar].split()
+ return ccachelist + self.default_objc
+
+ def get_objcpp_compiler_exelist(self):
+ ccachelist = self.detect_ccache()
+ evar = 'OBJCXX'
+ if evar in os.environ:
+ return os.environ[evar].split()
+ return ccachelist + self.default_objcpp
+
+ def get_source_dir(self):
+ return self.source_dir
+
+ def get_build_dir(self):
+ return self.build_dir
+
+ def get_exe_suffix(self):
+ return self.exe_suffix
+
+ # On Windows the library has suffix dll
+ # but you link against a file that has suffix lib.
+ def get_import_lib_suffix(self):
+ return self.import_lib_suffix
+
+ def get_shared_lib_prefix(self):
+ return self.shared_lib_prefix
+
+ def get_shared_lib_suffix(self):
+ return self.shared_lib_suffix
+
+ def get_static_lib_prefix(self):
+ return self.static_lib_prefix
+
+ def get_static_lib_suffix(self):
+ return self.static_lib_suffix
+
+ def get_object_suffix(self):
+ return self.object_suffix
+
+ def get_prefix(self):
+ return self.coredata.get_builtin_option('prefix')
+
+ def get_libdir(self):
+ return self.coredata.get_builtin_option('libdir')
+
+ def get_bindir(self):
+ return self.coredata.get_builtin_option('bindir')
+
+ def get_includedir(self):
+ return self.coredata.get_builtin_option('includedir')
+
+ def get_mandir(self):
+ return self.coredata.get_builtin_option('mandir')
+
+ def get_datadir(self):
+ return self.coredata.get_builtin_option('datadir')
+
+ def find_library(self, libname, dirs):
+ if dirs is None:
+ dirs = mesonlib.get_library_dirs()
+ suffixes = [self.get_shared_lib_suffix(), self.get_static_lib_suffix()]
+ prefix = self.get_shared_lib_prefix()
+ for d in dirs:
+ for suffix in suffixes:
+ trial = os.path.join(d, prefix + libname + '.' + suffix)
+ if os.path.isfile(trial):
+ return trial
+
+
+def get_args_from_envvars(lang):
+ if lang == 'c':
+ compile_args = os.environ.get('CFLAGS', '').split()
+ link_args = compile_args + os.environ.get('LDFLAGS', '').split()
+ compile_args += os.environ.get('CPPFLAGS', '').split()
+ elif lang == 'cpp':
+ compile_args = os.environ.get('CXXFLAGS', '').split()
+ link_args = compile_args + os.environ.get('LDFLAGS', '').split()
+ compile_args += os.environ.get('CPPFLAGS', '').split()
+ elif lang == 'objc':
+ compile_args = os.environ.get('OBJCFLAGS', '').split()
+ link_args = compile_args + os.environ.get('LDFLAGS', '').split()
+ compile_args += os.environ.get('CPPFLAGS', '').split()
+ elif lang == 'objcpp':
+ compile_args = os.environ.get('OBJCXXFLAGS', '').split()
+ link_args = compile_args + os.environ.get('LDFLAGS', '').split()
+ compile_args += os.environ.get('CPPFLAGS', '').split()
+ elif lang == 'fortran':
+ compile_args = os.environ.get('FFLAGS', '').split()
+ link_args = compile_args + os.environ.get('LDFLAGS', '').split()
+ else:
+ compile_args = []
+ link_args = []
+ return (compile_args, link_args)
+
+class CrossBuildInfo():
+ def __init__(self, filename):
+ self.config = {}
+ self.parse_datafile(filename)
+ if 'target_machine' in self.config:
+ return
+ if not 'host_machine' in self.config:
+ raise coredata.MesonException('Cross info file must have either host or a target machine.')
+ if not 'properties' in self.config:
+ raise coredata.MesonException('Cross file is missing "properties".')
+ if not 'binaries' in self.config:
+ raise coredata.MesonException('Cross file is missing "binaries".')
+
+ def ok_type(self, i):
+ return isinstance(i, str) or isinstance(i, int) or isinstance(i, bool)
+
+ def parse_datafile(self, filename):
+ config = configparser.ConfigParser()
+ config.read(filename)
+ # This is a bit hackish at the moment.
+ for s in config.sections():
+ self.config[s] = {}
+ for entry in config[s]:
+ value = config[s][entry]
+ if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
+ raise EnvironmentException('Malformed variable name %s in cross file..' % varname)
+ try:
+ res = eval(value, {'true' : True, 'false' : False})
+ except Exception:
+ raise EnvironmentException('Malformed value in cross file variable %s.' % varname)
+ if self.ok_type(res):
+ self.config[s][entry] = res
+ elif isinstance(res, list):
+ for i in res:
+ if not self.ok_type(i):
+ raise EnvironmentException('Malformed value in cross file variable %s.' % varname)
+ self.config[s][entry] = res
+ else:
+ raise EnvironmentException('Malformed value in cross file variable %s.' % varname)
+
+ def has_host(self):
+ return 'host_machine' in self.config
+
+ def has_target(self):
+ return 'target_machine' in self.config
+
+ # Wehn compiling a cross compiler we use the native compiler for everything.
+ # But not when cross compiling a cross compiler.
+ def need_cross_compiler(self):
+ return 'host_machine' in self.config
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
new file mode 100644
index 0000000..4894ac7
--- /dev/null
+++ b/mesonbuild/interpreter.py
@@ -0,0 +1,2259 @@
+# Copyright 2012-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.
+
+from . import mparser
+from . import environment
+from . import coredata
+from . import dependencies
+from . import mlog
+from . import build
+from . import optinterpreter
+from .wrap import wrap
+from . import mesonlib
+
+import os, sys, platform, subprocess, shutil, uuid, re
+from functools import wraps
+
+import importlib
+
+class InterpreterException(coredata.MesonException):
+ pass
+
+class InvalidCode(InterpreterException):
+ pass
+
+class InvalidArguments(InterpreterException):
+ pass
+
+# Decorators for method calls.
+
+def check_stringlist(a, msg='Arguments must be strings.'):
+ if not isinstance(a, list):
+ mlog.debug('Not a list:', str(a))
+ raise InvalidArguments('Argument not a list.')
+ if not all(isinstance(s, str) for s in a):
+ mlog.debug('Element not a string:', str(a))
+ raise InvalidArguments(msg)
+
+def noPosargs(f):
+ @wraps(f)
+ def wrapped(self, node, args, kwargs):
+ if len(args) != 0:
+ raise InvalidArguments('Function does not take positional arguments.')
+ return f(self, node, args, kwargs)
+ return wrapped
+
+def noKwargs(f):
+ @wraps(f)
+ def wrapped(self, node, args, kwargs):
+ if len(kwargs) != 0:
+ raise InvalidArguments('Function does not take keyword arguments.')
+ return f(self, node, args, kwargs)
+ return wrapped
+
+def stringArgs(f):
+ @wraps(f)
+ def wrapped(self, node, args, kwargs):
+ assert(isinstance(args, list))
+ check_stringlist(args)
+ return f(self, node, args, kwargs)
+ return wrapped
+
+def stringifyUserArguments(args):
+ if isinstance(args, list):
+ return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args])
+ elif isinstance(args, int):
+ return str(args)
+ elif isinstance(args, str):
+ return "'%s'" % args
+ raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
+
+class InterpreterObject():
+ def __init__(self):
+ self.methods = {}
+
+ def method_call(self, method_name, args, kwargs):
+ if method_name in self.methods:
+ return self.methods[method_name](args, kwargs)
+ raise InvalidCode('Unknown method "%s" in object.' % method_name)
+
+class TryRunResultHolder(InterpreterObject):
+ def __init__(self, res):
+ super().__init__()
+ self.res = res
+ self.methods.update({'returncode' : self.returncode_method,
+ 'compiled' : self.compiled_method,
+ 'stdout' : self.stdout_method,
+ 'stderr' : self.stderr_method,
+ })
+
+ def returncode_method(self, args, kwargs):
+ return self.res.returncode
+
+ def compiled_method(self, args, kwargs):
+ return self.res.compiled
+
+ def stdout_method(self, args, kwargs):
+ return self.res.stdout
+
+ def stderr_method(self, args, kwargs):
+ return self.res.stderr
+
+class RunProcess(InterpreterObject):
+
+ def __init__(self, command_array, source_dir, build_dir, subdir, in_builddir=False):
+ super().__init__()
+ pc = self.run_command(command_array, source_dir, build_dir, subdir, in_builddir)
+ (stdout, stderr) = pc.communicate()
+ self.returncode = pc.returncode
+ self.stdout = stdout.decode().replace('\r\n', '\n')
+ self.stderr = stderr.decode().replace('\r\n', '\n')
+ self.methods.update({'returncode' : self.returncode_method,
+ 'stdout' : self.stdout_method,
+ 'stderr' : self.stderr_method,
+ })
+
+ def run_command(self, command_array, source_dir, build_dir, subdir, in_builddir):
+ cmd_name = command_array[0]
+ env = {'MESON_SOURCE_ROOT' : source_dir,
+ 'MESON_BUILD_ROOT' : build_dir,
+ 'MESON_SUBDIR' : subdir}
+ if in_builddir:
+ cwd = os.path.join(build_dir, subdir)
+ else:
+ cwd = os.path.join(source_dir, subdir)
+ child_env = os.environ.copy()
+ child_env.update(env)
+ try:
+ return subprocess.Popen(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env=child_env, cwd=cwd)
+ except FileNotFoundError:
+ pass
+ # Was not a command, is a program in path?
+ exe = shutil.which(cmd_name)
+ if exe is not None:
+ command_array = [exe] + command_array[1:]
+ return subprocess.Popen(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env=child_env, cwd=cwd)
+ # No? Maybe it is a script in the source tree.
+ fullpath = os.path.join(source_dir, subdir, cmd_name)
+ command_array = [fullpath] + command_array[1:]
+ try:
+ return subprocess.Popen(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env=child_env, cwd=cwd)
+ except FileNotFoundError:
+ raise InterpreterException('Could not execute command "%s".' % cmd_name)
+
+ def returncode_method(self, args, kwargs):
+ return self.returncode
+
+ def stdout_method(self, args, kwargs):
+ return self.stdout
+
+ def stderr_method(self, args, kwargs):
+ return self.stderr
+
+class ConfigureFileHolder(InterpreterObject):
+
+ def __init__(self, subdir, sourcename, targetname, configuration_data):
+ InterpreterObject.__init__(self)
+ self.held_object = build.ConfigureFile(subdir, sourcename, targetname, configuration_data)
+
+class ConfigurationDataHolder(InterpreterObject):
+ def __init__(self):
+ super().__init__()
+ self.used = False # These objects become immutable after use in configure_file.
+ self.held_object = build.ConfigurationData()
+ self.methods.update({'set': self.set_method,
+ 'set10': self.set10_method,
+ 'has' : self.has_method,
+ })
+
+ def is_used(self):
+ return self.used
+
+ def mark_used(self):
+ self.used = True
+
+ def validate_args(self, args):
+ if len(args) != 2:
+ raise InterpreterException("Configuration set requires 2 arguments.")
+ if self.used:
+ raise InterpreterException("Can not set values on configuration object that has been used.")
+ name = args[0]
+ val = args[1]
+ if not isinstance(name, str):
+ raise InterpreterException("First argument to set must be a string.")
+ return (name, val)
+
+ def set_method(self, args, kwargs):
+ (name, val) = self.validate_args(args)
+ self.held_object.values[name] = val
+
+ def set10_method(self, args, kwargs):
+ (name, val) = self.validate_args(args)
+ if val:
+ self.held_object.values[name] = 1
+ else:
+ self.held_object.values[name] = 0
+
+ def has_method(self, args, kwargs):
+ return args[0] in self.held_object.values
+
+ def get(self, name):
+ return self.held_object.values[name]
+
+ def keys(self):
+ return self.held_object.values.keys()
+
+# Interpreter objects can not be pickled so we must have
+# these wrappers.
+
+class DependencyHolder(InterpreterObject):
+ def __init__(self, dep):
+ InterpreterObject.__init__(self)
+ self.held_object = dep
+ self.methods.update({'found' : self.found_method})
+
+ def found_method(self, args, kwargs):
+ return self.held_object.found()
+
+class InternalDependencyHolder(InterpreterObject):
+ def __init__(self, dep):
+ InterpreterObject.__init__(self)
+ self.held_object = dep
+ self.methods.update({'found' : self.found_method})
+
+ def found_method(self, args, kwargs):
+ return True
+
+class ExternalProgramHolder(InterpreterObject):
+ def __init__(self, ep):
+ InterpreterObject.__init__(self)
+ self.held_object = ep
+ self.methods.update({'found': self.found_method})
+
+ def found_method(self, args, kwargs):
+ return self.found()
+
+ def found(self):
+ return self.held_object.found()
+
+ def get_command(self):
+ return self.held_object.fullpath
+
+ def get_name(self):
+ return self.held_object.name
+
+class ExternalLibraryHolder(InterpreterObject):
+ def __init__(self, el):
+ InterpreterObject.__init__(self)
+ self.held_object = el
+ self.methods.update({'found': self.found_method})
+
+ def found(self):
+ return self.held_object.found()
+
+ def found_method(self, args, kwargs):
+ return self.found()
+
+ def get_filename(self):
+ return self.held_object.fullpath
+
+ def get_name(self):
+ return self.held_object.name
+
+ def get_compile_args(self):
+ return self.held_object.get_compile_args()
+
+ def get_link_args(self):
+ return self.held_object.get_link_args()
+
+ def get_exe_args(self):
+ return self.held_object.get_exe_args()
+
+class GeneratorHolder(InterpreterObject):
+ def __init__(self, interpreter, args, kwargs):
+ super().__init__()
+ self.interpreter = interpreter
+ self.held_object = build.Generator(args, kwargs)
+ self.methods.update({'process' : self.process_method})
+
+ def process_method(self, args, kwargs):
+ check_stringlist(args)
+ extras = mesonlib.stringlistify(kwargs.get('extra_args', []))
+ gl = GeneratedListHolder(self, extras)
+ [gl.add_file(os.path.join(self.interpreter.subdir, a)) for a in args]
+ return gl
+
+class GeneratedListHolder(InterpreterObject):
+ def __init__(self, arg1, extra_args=[]):
+ super().__init__()
+ if isinstance(arg1, GeneratorHolder):
+ self.held_object = build.GeneratedList(arg1.held_object, extra_args)
+ else:
+ self.held_object = arg1
+
+ def add_file(self, a):
+ self.held_object.add_file(a)
+
+class BuildMachine(InterpreterObject):
+ def __init__(self):
+ InterpreterObject.__init__(self)
+ self.methods.update({'system' : self.system_method,
+ 'cpu_family' : self.cpu_family_method,
+ 'cpu' : self.cpu_method,
+ 'endian' : self.endian_method,
+ })
+
+ # Python is inconsistent in its platform module.
+ # It returns different values for the same cpu.
+ # For x86 it might return 'x86', 'i686' or somesuch.
+ # Do some canonicalization.
+ def cpu_family_method(self, args, kwargs):
+ trial = platform.machine().lower()
+ if trial.startswith('i') and trial.endswith('86'):
+ return 'x86'
+ if trial.startswith('arm'):
+ return 'arm'
+ # Add fixes here as bugs are reported.
+ return trial
+
+ def cpu_method(self, args, kwargs):
+ return platform.machine().lower()
+
+ def system_method(self, args, kwargs):
+ return platform.system().lower()
+
+ def endian_method(self, args, kwargs):
+ return sys.byteorder
+
+# This class will provide both host_machine and
+# target_machine
+class CrossMachineInfo(InterpreterObject):
+ def __init__(self, cross_info):
+ InterpreterObject.__init__(self)
+ minimum_cross_info = {'cpu', 'cpu_family', 'endian', 'system'}
+ if set(cross_info) < minimum_cross_info:
+ raise InterpreterException(
+ 'Machine info is currently {}\n'.format(cross_info) +
+ 'but is missing {}.'.format(minimum_cross_info - set(cross_info)))
+ self.info = cross_info
+ self.methods.update({'system' : self.system_method,
+ 'cpu' : self.cpu_method,
+ 'cpu_family' : self.cpu_family_method,
+ 'endian' : self.endian_method,
+ })
+
+ def system_method(self, args, kwargs):
+ return self.info['system']
+
+ def cpu_method(self, args, kwargs):
+ return self.info['cpu']
+
+ def cpu_family_method(self, args, kwargs):
+ return self.info['cpu_family']
+
+ def endian_method(self, args, kwargs):
+ return self.info['endian']
+
+class IncludeDirsHolder(InterpreterObject):
+ def __init__(self, idobj):
+ super().__init__()
+ self.held_object = idobj
+
+class Headers(InterpreterObject):
+
+ def __init__(self, src_subdir, sources, kwargs):
+ InterpreterObject.__init__(self)
+ self.sources = sources
+ self.source_subdir = src_subdir
+ self.install_subdir = kwargs.get('subdir', '')
+ self.custom_install_dir = kwargs.get('install_dir', None)
+ if self.custom_install_dir is not None:
+ if not isinstance(self.custom_install_dir, str):
+ raise InterpreterException('Custom_install_dir must be a string.')
+
+ def set_install_subdir(self, subdir):
+ self.install_subdir = subdir
+
+ def get_install_subdir(self):
+ return self.install_subdir
+
+ def get_source_subdir(self):
+ return self.source_subdir
+
+ def get_sources(self):
+ return self.sources
+
+ def get_custom_install_dir(self):
+ return self.custom_install_dir
+
+class DataHolder(InterpreterObject):
+ def __init__(self, in_sourcetree, source_subdir, sources, kwargs):
+ super().__init__()
+ kwsource = mesonlib.stringlistify(kwargs.get('sources', []))
+ sources += kwsource
+ check_stringlist(sources)
+ install_dir = kwargs.get('install_dir', None)
+ if not isinstance(install_dir, str):
+ raise InterpreterException('Custom_install_dir must be a string.')
+ self.held_object = build.Data(in_sourcetree, source_subdir, sources, install_dir)
+
+ def get_source_subdir(self):
+ return self.held_object.source_subdir
+
+ def get_sources(self):
+ return self.held_object.sources
+
+ def get_install_dir(self):
+ return self.held_object.install_dir
+
+class InstallDir(InterpreterObject):
+ def __init__(self, source_subdir, installable_subdir, install_dir):
+ InterpreterObject.__init__(self)
+ self.source_subdir = source_subdir
+ self.installable_subdir = installable_subdir
+ self.install_dir = install_dir
+
+class Man(InterpreterObject):
+
+ def __init__(self, source_subdir, sources, kwargs):
+ InterpreterObject.__init__(self)
+ self.source_subdir = source_subdir
+ self.sources = sources
+ self.validate_sources()
+ if len(kwargs) > 1:
+ raise InvalidArguments('Man function takes at most one keyword arguments.')
+ self.custom_install_dir = kwargs.get('install_dir', None)
+ if self.custom_install_dir is not None and not isinstance(self.custom_install_dir, str):
+ raise InterpreterException('Custom_install_dir must be a string.')
+
+ def validate_sources(self):
+ for s in self.sources:
+ num = int(s.split('.')[-1])
+ if num < 1 or num > 8:
+ raise InvalidArguments('Man file must have a file extension of a number between 1 and 8')
+
+ def get_custom_install_dir(self):
+ return self.custom_install_dir
+
+ def get_sources(self):
+ return self.sources
+
+ def get_source_subdir(self):
+ return self.source_subdir
+
+class GeneratedObjectsHolder(InterpreterObject):
+ def __init__(self, held_object):
+ super().__init__()
+ self.held_object = held_object
+
+class BuildTargetHolder(InterpreterObject):
+ def __init__(self, target, interp):
+ super().__init__()
+ self.held_object = target
+ self.interpreter = interp
+ self.methods.update({'extract_objects' : self.extract_objects_method,
+ 'extract_all_objects' : self.extract_all_objects_method,
+ 'get_id': self.get_id_method,
+ 'outdir' : self.outdir_method,
+ 'private_dir_include' : self.private_dir_include_method,
+ })
+
+ def is_cross(self):
+ return self.held_object.is_cross()
+
+ def private_dir_include_method(self, args, kwargs):
+ return IncludeDirsHolder(build.IncludeDirs('', [], False,
+ [self.interpreter.backend.get_target_private_dir(self.held_object)]))
+
+ def outdir_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_dir(self.held_object)
+
+ def extract_objects_method(self, args, kwargs):
+ gobjs = self.held_object.extract_objects(args)
+ return GeneratedObjectsHolder(gobjs)
+
+ def extract_all_objects_method(self, args, kwargs):
+ gobjs = self.held_object.extract_all_objects()
+ return GeneratedObjectsHolder(gobjs)
+
+ def get_id_method(self, args, kwargs):
+ return self.held_object.get_id()
+
+class ExecutableHolder(BuildTargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
+class StaticLibraryHolder(BuildTargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
+class SharedLibraryHolder(BuildTargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
+class JarHolder(BuildTargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
+class CustomTargetHolder(InterpreterObject):
+ def __init__(self, object_to_hold):
+ self.held_object = object_to_hold
+
+ def is_cross(self):
+ return self.held_object.is_cross()
+
+ def extract_objects_method(self, args, kwargs):
+ gobjs = self.held_object.extract_objects(args)
+ return GeneratedObjectsHolder(gobjs)
+
+class RunTargetHolder(InterpreterObject):
+ def __init__(self, name, command, args, subdir):
+ self.held_object = build.RunTarget(name, command, args, subdir)
+
+class Test(InterpreterObject):
+ def __init__(self, name, suite, exe, is_parallel, cmd_args, env, should_fail, valgrind_args, timeout, workdir):
+ InterpreterObject.__init__(self)
+ self.name = name
+ self.suite = suite
+ self.exe = exe
+ self.is_parallel = is_parallel
+ self.cmd_args = cmd_args
+ self.env = env
+ self.should_fail = should_fail
+ self.valgrind_args = valgrind_args
+ self.timeout = timeout
+ self.workdir = workdir
+
+ def get_exe(self):
+ return self.exe
+
+ def get_name(self):
+ return self.name
+
+class SubprojectHolder(InterpreterObject):
+
+ def __init__(self, subinterpreter):
+ super().__init__()
+ self.subinterpreter = subinterpreter
+ self.methods.update({'get_variable' : self.get_variable_method,
+ })
+
+ def get_variable_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Get_variable takes one argument.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Get_variable takes a string argument.')
+ return self.subinterpreter.variables[varname]
+
+class CompilerHolder(InterpreterObject):
+ def __init__(self, compiler, env):
+ InterpreterObject.__init__(self)
+ self.compiler = compiler
+ self.environment = env
+ self.methods.update({'compiles': self.compiles_method,
+ 'links': self.links_method,
+ 'get_id': self.get_id_method,
+ 'sizeof': self.sizeof_method,
+ 'has_header': self.has_header_method,
+ 'run' : self.run_method,
+ 'has_function' : self.has_function_method,
+ 'has_member' : self.has_member_method,
+ 'has_type' : self.has_type_method,
+ 'alignment' : self.alignment_method,
+ 'version' : self.version_method,
+ 'cmd_array' : self.cmd_array_method,
+ })
+
+ def version_method(self, args, kwargs):
+ return self.compiler.version
+
+ def cmd_array_method(self, args, kwargs):
+ return self.compiler.exelist
+
+ def determine_args(self, kwargs):
+ nobuiltins = kwargs.get('no_builtin_args', False)
+ if not isinstance(nobuiltins, bool):
+ raise InterpreterException('Type of no_builtin_args not a boolean.')
+ args = []
+ if not nobuiltins:
+ opts = self.environment.coredata.compiler_options
+ args += self.compiler.get_option_compile_args(opts)
+ args += self.compiler.get_option_link_args(opts)
+ args += mesonlib.stringlistify(kwargs.get('args', []))
+ return args
+
+ def alignment_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Alignment method takes exactly one positional argument.')
+ check_stringlist(args)
+ typename = args[0]
+ extra_args = mesonlib.stringlistify(kwargs.get('args', []))
+ result = self.compiler.alignment(typename, self.environment, extra_args)
+ mlog.log('Checking for alignment of "', mlog.bold(typename), '": ', result, sep='')
+ return result
+
+ def run_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Run method takes exactly one positional argument.')
+ check_stringlist(args)
+ code = args[0]
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = self.determine_args(kwargs)
+ result = self.compiler.run(code, extra_args)
+ if len(testname) > 0:
+ if not result.compiled:
+ h = mlog.red('DID NOT COMPILE')
+ elif result.returncode == 0:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO (%d)' % result.returncode)
+ mlog.log('Checking if "', mlog.bold(testname), '" runs : ', h, sep='')
+ return TryRunResultHolder(result)
+
+ def get_id_method(self, args, kwargs):
+ return self.compiler.get_id()
+
+ def has_member_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Has_member takes exactly two arguments.')
+ check_stringlist(args)
+ typename = args[0]
+ membername = args[1]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_function must be a string.')
+ extra_args = self.determine_args(kwargs)
+ had = self.compiler.has_member(typename, membername, prefix, extra_args)
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking whether type "', mlog.bold(typename),
+ '" has member "', mlog.bold(membername), '": ', hadtxt, sep='')
+ return had
+
+ def has_function_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Has_function takes exactly one argument.')
+ check_stringlist(args)
+ funcname = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_function must be a string.')
+ extra_args = self.determine_args(kwargs)
+ had = self.compiler.has_function(funcname, prefix, self.environment, extra_args)
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking for function "', mlog.bold(funcname), '": ', hadtxt, sep='')
+ return had
+
+ def has_type_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Has_type takes exactly one argument.')
+ check_stringlist(args)
+ typename = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_type must be a string.')
+ extra_args = self.determine_args(kwargs)
+ had = self.compiler.has_type(typename, prefix, extra_args)
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking for type "', mlog.bold(typename), '": ', hadtxt, sep='')
+ return had
+
+ def sizeof_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Sizeof takes exactly one argument.')
+ check_stringlist(args)
+ element = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of sizeof must be a string.')
+ extra_args = self.determine_args(kwargs)
+ esize = self.compiler.sizeof(element, prefix, self.environment, extra_args)
+ mlog.log('Checking for size of "%s": %d' % (element, esize))
+ return esize
+
+ def compiles_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('compiles method takes exactly one argument.')
+ check_stringlist(args)
+ code = args[0]
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = self.determine_args(kwargs)
+ result = self.compiler.compiles(code, extra_args)
+ if len(testname) > 0:
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Checking if "', mlog.bold(testname), '" compiles : ', h, sep='')
+ return result
+
+ def links_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('links method takes exactly one argument.')
+ check_stringlist(args)
+ code = args[0]
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = self.determine_args(kwargs)
+ result = self.compiler.links(code, extra_args)
+ if len(testname) > 0:
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Checking if "', mlog.bold(testname), '" links : ', h, sep='')
+ return result
+
+ def has_header_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('has_header method takes exactly one argument.')
+ check_stringlist(args)
+ string = args[0]
+ extra_args = self.determine_args(kwargs)
+ haz = self.compiler.has_header(string, extra_args)
+ if haz:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Has header "%s":' % string, h)
+ return haz
+
+class ModuleState:
+ pass
+
+class ModuleHolder(InterpreterObject):
+ def __init__(self, modname, module, interpreter):
+ InterpreterObject.__init__(self)
+ self.modname = modname
+ self.held_object = module
+ self.interpreter = interpreter
+
+ def method_call(self, method_name, args, kwargs):
+ try:
+ fn = getattr(self.held_object, method_name)
+ except AttributeError:
+ raise InvalidArguments('Module %s does not have method %s.' % (self.modname, method_name))
+ state = ModuleState()
+ state.build_to_src = os.path.relpath(self.interpreter.environment.get_source_dir(),
+ self.interpreter.environment.get_build_dir())
+ state.subdir = self.interpreter.subdir
+ state.environment = self.interpreter.environment
+ state.project_name = self.interpreter.build.project_name
+ state.project_version = self.interpreter.build.dep_manifest[self.interpreter.active_projectname]
+ state.compilers = self.interpreter.build.compilers
+ state.targets = self.interpreter.build.targets
+ state.headers = self.interpreter.build.get_headers()
+ state.man = self.interpreter.build.get_man()
+ state.global_args = self.interpreter.build.global_args
+ value = fn(state, args, kwargs)
+ return self.interpreter.module_method_callback(value)
+
+class MesonMain(InterpreterObject):
+ def __init__(self, build, interpreter):
+ InterpreterObject.__init__(self)
+ self.build = build
+ self.interpreter = interpreter
+ self.methods.update({'get_compiler': self.get_compiler_method,
+ 'is_cross_build' : self.is_cross_build_method,
+ 'has_exe_wrapper' : self.has_exe_wrapper_method,
+ 'is_unity' : self.is_unity_method,
+ 'is_subproject' : self.is_subproject_method,
+ 'current_source_dir' : self.current_source_dir_method,
+ 'current_build_dir' : self.current_build_dir_method,
+ 'source_root' : self.source_root_method,
+ 'build_root' : self.build_root_method,
+ 'add_install_script' : self.add_install_script_method,
+ 'install_dependency_manifest': self.install_dependency_manifest_method,
+ 'project_version': self.project_version_method,
+ })
+
+ def add_install_script_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Set_install_script takes exactly one argument.')
+ check_stringlist(args)
+ scriptbase = args[0]
+ scriptfile = os.path.join(self.interpreter.environment.source_dir,
+ self.interpreter.subdir, scriptbase)
+ if not os.path.isfile(scriptfile):
+ raise InterpreterException('Can not find install script %s.' % scriptbase)
+ self.build.install_scripts.append(build.InstallScript([scriptfile]))
+
+ def current_source_dir_method(self, args, kwargs):
+ src = self.interpreter.environment.source_dir
+ sub = self.interpreter.subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ def current_build_dir_method(self, args, kwargs):
+ src = self.interpreter.environment.build_dir
+ sub = self.interpreter.subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ def source_root_method(self, args, kwargs):
+ return self.interpreter.environment.source_dir
+
+ def build_root_method(self, args, kwargs):
+ return self.interpreter.environment.build_dir
+
+ def has_exe_wrapper_method(self, args, kwargs):
+ if self.is_cross_build_method(None, None) and 'binaries' in self.build.environment.cross_info.config:
+ return 'exe_wrap' in self.build.environment.cross_info.config['binaries']
+ return True # This is semantically confusing.
+
+ def is_cross_build_method(self, args, kwargs):
+ return self.build.environment.is_cross_build()
+
+ def get_compiler_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('get_compiler_method must have one and only one argument.')
+ cname = args[0]
+ native = kwargs.get('native', None)
+ if native is None:
+ if self.build.environment.is_cross_build():
+ native = False
+ else:
+ native = True
+ if not isinstance(native, bool):
+ raise InterpreterException('Type of "native" must be a boolean.')
+ if native:
+ clist = self.build.compilers
+ else:
+ clist = self.build.cross_compilers
+ for c in clist:
+ if c.get_language() == cname:
+ return CompilerHolder(c, self.build.environment)
+ raise InterpreterException('Tried to access compiler for unspecified language "%s".' % cname)
+
+ def is_unity_method(self, args, kwargs):
+ return self.build.environment.coredata.get_builtin_option('unity')
+
+ def is_subproject_method(self, args, kwargs):
+ return self.interpreter.is_subproject()
+
+ def install_dependency_manifest_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Must specify manifest install file name')
+ if not isinstance(args[0], str):
+ raise InterpreterException('Argument must be a string.')
+ self.build.dep_manifest_name = args[0]
+
+ def project_version_method(self, args, kwargs):
+ return self.build.dep_manifest[self.interpreter.active_projectname]['version']
+
+class Interpreter():
+
+ def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects'):
+ self.build = build
+ self.backend = backend
+ self.subproject = subproject
+ self.subdir = subdir
+ self.source_root = build.environment.get_source_dir()
+ self.subproject_dir = subproject_dir
+ option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt')
+ if os.path.exists(option_file):
+ oi = optinterpreter.OptionInterpreter(self.subproject, \
+ self.build.environment.cmd_line_options.projectoptions)
+ oi.process(option_file)
+ self.build.environment.merge_options(oi.options)
+ mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
+ if not os.path.isfile(mesonfile):
+ raise InvalidArguments('Missing Meson file in %s' % mesonfile)
+ code = open(mesonfile).read()
+ if len(code.strip()) == 0:
+ raise InvalidCode('Builder file is empty.')
+ assert(isinstance(code, str))
+ try:
+ self.ast = mparser.Parser(code).parse()
+ except coredata.MesonException as me:
+ me.file = environment.build_filename
+ raise me
+ self.sanity_check_ast()
+ self.variables = {}
+ self.builtin = {}
+ self.builtin['build_machine'] = BuildMachine()
+ if not self.build.environment.is_cross_build():
+ self.builtin['host_machine'] = self.builtin['build_machine']
+ self.builtin['target_machine'] = self.builtin['build_machine']
+ else:
+ cross_info = self.build.environment.cross_info
+ if cross_info.has_host():
+ self.builtin['host_machine'] = CrossMachineInfo(cross_info.config['host_machine'])
+ else:
+ self.builtin['host_machine'] = self.builtin['build_machine']
+ if cross_info.has_target():
+ self.builtin['target_machine'] = CrossMachineInfo(cross_info.config['target_machine'])
+ else:
+ self.builtin['target_machine'] = self.builtin['host_machine']
+ self.builtin['meson'] = MesonMain(build, self)
+ self.environment = build.environment
+ self.build_func_dict()
+ self.build_def_files = [os.path.join(self.subdir, environment.build_filename)]
+ self.coredata = self.environment.get_coredata()
+ self.generators = []
+ self.visited_subdirs = {}
+ self.global_args_frozen = False
+ self.subprojects = {}
+ self.subproject_stack = []
+
+ def build_func_dict(self):
+ self.funcs = {'project' : self.func_project,
+ 'message' : self.func_message,
+ 'error' : self.func_error,
+ 'executable': self.func_executable,
+ 'dependency' : self.func_dependency,
+ 'static_library' : self.func_static_lib,
+ 'shared_library' : self.func_shared_lib,
+ 'library' : self.func_library,
+ 'jar' : self.func_jar,
+ 'build_target': self.func_build_target,
+ 'custom_target' : self.func_custom_target,
+ 'run_target' : self.func_run_target,
+ 'generator' : self.func_generator,
+ 'test' : self.func_test,
+ 'benchmark' : self.func_benchmark,
+ 'install_headers' : self.func_install_headers,
+ 'install_man' : self.func_install_man,
+ 'subdir' : self.func_subdir,
+ 'install_data' : self.func_install_data,
+ 'install_subdir' : self.func_install_subdir,
+ 'configure_file' : self.func_configure_file,
+ 'include_directories' : self.func_include_directories,
+ 'add_global_arguments' : self.func_add_global_arguments,
+ 'add_languages' : self.func_add_languages,
+ 'find_program' : self.func_find_program,
+ 'find_library' : self.func_find_library,
+ 'configuration_data' : self.func_configuration_data,
+ 'run_command' : self.func_run_command,
+ 'gettext' : self.func_gettext,
+ 'option' : self.func_option,
+ 'get_option' : self.func_get_option,
+ 'subproject' : self.func_subproject,
+ 'vcs_tag' : self.func_vcs_tag,
+ 'set_variable' : self.func_set_variable,
+ 'is_variable' : self.func_is_variable,
+ 'get_variable' : self.func_get_variable,
+ 'import' : self.func_import,
+ 'files' : self.func_files,
+ 'declare_dependency': self.func_declare_dependency,
+ 'assert': self.func_assert,
+ }
+
+ def module_method_callback(self, invalues):
+ unwrap_single = False
+ if invalues is None:
+ return
+ if not isinstance(invalues, list):
+ unwrap_single = True
+ invalues = [invalues]
+ outvalues = []
+ for v in invalues:
+ if isinstance(v, build.CustomTarget):
+ if v.name in self.build.targets:
+ raise InterpreterException('Tried to create target %s which already exists.' % v.name)
+ self.build.targets[v.name] = v
+ outvalues.append(CustomTargetHolder(v))
+ elif isinstance(v, int) or isinstance(v, str):
+ outvalues.append(v)
+ elif isinstance(v, build.Executable):
+ if v.name in self.build.targets:
+ raise InterpreterException('Tried to create target %s which already exists.' % v.name)
+ self.build.targets[v.name] = v
+ outvalues.append(ExecutableHolder(v))
+ elif isinstance(v, list):
+ outvalues.append(self.module_method_callback(v))
+ elif isinstance(v, build.GeneratedList):
+ outvalues.append(GeneratedListHolder(v))
+ elif isinstance(v, build.RunTarget):
+ if v.name in self.build.targets:
+ raise InterpreterException('Tried to create target %s which already exists.' % v.name)
+ self.build.targets[v.name] = v
+ elif isinstance(v, build.InstallScript):
+ self.build.install_scripts.append(v)
+ elif isinstance(v, build.Data):
+ self.build.data.append(v)
+ else:
+ print(v)
+ raise InterpreterException('Module returned a value of unknown type.')
+ if len(outvalues) == 1 and unwrap_single:
+ return outvalues[0]
+ return outvalues
+
+ def get_build_def_files(self):
+ return self.build_def_files
+
+ def get_variables(self):
+ return self.variables
+
+ def sanity_check_ast(self):
+ if not isinstance(self.ast, mparser.CodeBlockNode):
+ raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
+ if len(self.ast.lines) == 0:
+ raise InvalidCode('No statements in code.')
+ first = self.ast.lines[0]
+ if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project':
+ raise InvalidCode('First statement must be a call to project')
+
+ def run(self):
+ self.evaluate_codeblock(self.ast)
+ mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets))))
+
+ def evaluate_codeblock(self, node):
+ if node is None:
+ return
+ if not isinstance(node, mparser.CodeBlockNode):
+ e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.')
+ e.lineno = node.lineno
+ e.colno = node.colno
+ raise e
+ statements = node.lines
+ i = 0
+ while i < len(statements):
+ cur = statements[i]
+ try:
+ self.evaluate_statement(cur)
+ except Exception as e:
+ if not(hasattr(e, 'lineno')):
+ e.lineno = cur.lineno
+ e.colno = cur.colno
+ e.file = os.path.join(self.subdir, 'meson.build')
+ raise e
+ i += 1 # In THE FUTURE jump over blocks and stuff.
+
+ def get_variable(self, varname):
+ if varname in self.builtin:
+ return self.builtin[varname]
+ if varname in self.variables:
+ return self.variables[varname]
+ raise InvalidCode('Unknown variable "%s".' % varname)
+
+ def func_set_variable(self, node, args, kwargs):
+ if len(args) != 2:
+ raise InvalidCode('Set_variable takes two arguments.')
+ varname = args[0]
+ value = self.to_native(args[1])
+ self.set_variable(varname, value)
+
+ @noKwargs
+ def func_get_variable(self, node, args, kwargs):
+ if len(args)<1 or len(args)>2:
+ raise InvalidCode('Get_variable takes one or two arguments.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('First argument must be a string.')
+ try:
+ return self.variables[varname]
+ except KeyError:
+ pass
+ if len(args) == 2:
+ return args[1]
+ raise InterpreterException('Tried to get unknown variable "%s".' % varname)
+
+ @stringArgs
+ @noKwargs
+ def func_is_variable(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidCode('Is_variable takes two arguments.')
+ varname = args[0]
+ return varname in self.variables
+
+ @stringArgs
+ @noKwargs
+ def func_import(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidCode('Import takes one argument.')
+ modname = args[0]
+ if not modname in self.environment.coredata.modules:
+ module = importlib.import_module('mesonbuild.modules.' + modname).initialize()
+ self.environment.coredata.modules[modname] = module
+ return ModuleHolder(modname, self.environment.coredata.modules[modname], self)
+
+ @stringArgs
+ @noKwargs
+ def func_files(self, node, args, kwargs):
+ return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args]
+
+ @noPosargs
+ def func_declare_dependency(self, node, args, kwargs):
+ incs = kwargs.get('include_directories', [])
+ if not isinstance(incs, list):
+ incs = [incs]
+ libs = kwargs.get('link_with', [])
+ if not isinstance(libs, list):
+ libs = [libs]
+ sources = kwargs.get('sources', [])
+ if not isinstance(sources, list):
+ sources = [sources]
+ sources = self.source_strings_to_files(self.flatten(sources))
+ deps = kwargs.get('dependencies', [])
+ if not isinstance(deps, list):
+ deps = [deps]
+ final_deps = []
+ for d in deps:
+ try:
+ d = d.held_object
+ except Exception:
+ pass
+ if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary, dependencies.InternalDependency)):
+ raise InterpreterException('Dependencies must be external deps')
+ final_deps.append(d)
+ dep = dependencies.InternalDependency(incs, libs, sources, final_deps)
+ return InternalDependencyHolder(dep)
+
+ @noKwargs
+ def func_assert(self, node, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Assert takes exactly two arguments')
+ value, message = args
+ if not isinstance(value, bool):
+ raise InterpreterException('Assert value not bool.')
+ if not isinstance(message, str):
+ raise InterpreterException('Assert message not a string.')
+ if not value:
+ raise InterpreterException('Assert failed: ' + message)
+
+ def set_variable(self, varname, variable):
+ if variable is None:
+ raise InvalidCode('Can not assign None to variable.')
+ if not isinstance(varname, str):
+ raise InvalidCode('First argument to set_variable must be a string.')
+ if not self.is_assignable(variable):
+ raise InvalidCode('Assigned value not of assignable type.')
+ if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
+ raise InvalidCode('Invalid variable name: ' + varname)
+ if varname in self.builtin:
+ raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
+ self.variables[varname] = variable
+
+ def evaluate_statement(self, cur):
+ if isinstance(cur, mparser.FunctionNode):
+ return self.function_call(cur)
+ elif isinstance(cur, mparser.AssignmentNode):
+ return self.assignment(cur)
+ elif isinstance(cur, mparser.MethodNode):
+ return self.method_call(cur)
+ elif isinstance(cur, mparser.StringNode):
+ return cur.value
+ elif isinstance(cur, mparser.BooleanNode):
+ return cur.value
+ elif isinstance(cur, mparser.IfClauseNode):
+ return self.evaluate_if(cur)
+ elif isinstance(cur, mparser.IdNode):
+ return self.get_variable(cur.value)
+ elif isinstance(cur, mparser.ComparisonNode):
+ return self.evaluate_comparison(cur)
+ elif isinstance(cur, mparser.ArrayNode):
+ return self.evaluate_arraystatement(cur)
+ elif isinstance(cur, mparser.NumberNode):
+ return cur.value
+ elif isinstance(cur, mparser.AndNode):
+ return self.evaluate_andstatement(cur)
+ elif isinstance(cur, mparser.OrNode):
+ return self.evaluate_orstatement(cur)
+ elif isinstance(cur, mparser.NotNode):
+ return self.evaluate_notstatement(cur)
+ elif isinstance(cur, mparser.UMinusNode):
+ return self.evaluate_uminusstatement(cur)
+ elif isinstance(cur, mparser.ArithmeticNode):
+ return self.evaluate_arithmeticstatement(cur)
+ elif isinstance(cur, mparser.ForeachClauseNode):
+ return self.evaluate_foreach(cur)
+ elif isinstance(cur, mparser.PlusAssignmentNode):
+ return self.evaluate_plusassign(cur)
+ elif isinstance(cur, mparser.IndexNode):
+ return self.evaluate_indexing(cur)
+ elif self.is_elementary_type(cur):
+ return cur
+ else:
+ raise InvalidCode("Unknown statement.")
+
+ def validate_arguments(self, args, argcount, arg_types):
+ if argcount is not None:
+ if argcount != len(args):
+ raise InvalidArguments('Expected %d arguments, got %d.' %
+ (argcount, len(args)))
+ for i in range(min(len(args), len(arg_types))):
+ wanted = arg_types[i]
+ actual = args[i]
+ if wanted != None:
+ if not isinstance(actual, wanted):
+ raise InvalidArguments('Incorrect argument type.')
+
+ def func_run_command(self, node, args, kwargs):
+ if len(args) < 1:
+ raise InterpreterException('Not enough arguments')
+ cmd = args[0]
+ cargs = args[1:]
+ if isinstance(cmd, ExternalProgramHolder):
+ cmd = cmd.get_command()
+ elif isinstance(cmd, str):
+ cmd = [cmd]
+ else:
+ raise InterpreterException('First argument is of incorrect type.')
+ check_stringlist(cargs, 'Run_command arguments must be strings.')
+ args = cmd + cargs
+ in_builddir = kwargs.get('in_builddir', False)
+ if not isinstance(in_builddir, bool):
+ raise InterpreterException('in_builddir must be boolean.')
+ return RunProcess(args, self.environment.source_dir, self.environment.build_dir,
+ self.subdir, in_builddir)
+
+ @stringArgs
+ def func_gettext(self, nodes, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Gettext requires one positional argument (package name).')
+ packagename = args[0]
+ languages = kwargs.get('languages', None)
+ check_stringlist(languages, 'Argument languages must be a list of strings.')
+ # TODO: check that elements are strings
+ if len(self.build.pot) > 0:
+ raise InterpreterException('More than one gettext definition currently not supported.')
+ self.build.pot.append((packagename, languages, self.subdir))
+
+ def func_option(self, nodes, args, kwargs):
+ raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.')
+
+ @stringArgs
+ def func_subproject(self, nodes, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Subproject takes exactly one argument')
+ dirname = args[0]
+ return self.do_subproject(dirname, kwargs)
+
+ def do_subproject(self, dirname, kwargs):
+ if self.subdir != '':
+ segs = os.path.split(self.subdir)
+ if len(segs) != 2 or segs[0] != self.subproject_dir:
+ raise InterpreterException('Subprojects must be defined at the root directory.')
+ if dirname in self.subproject_stack:
+ fullstack = self.subproject_stack + [dirname]
+ incpath = ' => '.join(fullstack)
+ raise InterpreterException('Recursive include of subprojects: %s.' % incpath)
+ if dirname in self.subprojects:
+ return self.subprojects[dirname]
+ r = wrap.Resolver(os.path.join(self.build.environment.get_source_dir(), self.subproject_dir))
+ resolved = r.resolve(dirname)
+ if resolved is None:
+ raise InterpreterException('Subproject directory does not exist and can not be downloaded.')
+ subdir = os.path.join(self.subproject_dir, resolved)
+ os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
+ self.global_args_frozen = True
+ mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='')
+ subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir)
+ subi.subprojects = self.subprojects
+
+ subi.subproject_stack = self.subproject_stack + [dirname]
+ current_active = self.active_projectname
+ subi.run()
+ if 'version' in kwargs:
+ pv = subi.project_version
+ wanted = kwargs['version']
+ if not mesonlib.version_compare(pv, wanted):
+ raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted))
+ self.active_projectname = current_active
+ mlog.log('\nSubproject', mlog.bold(dirname), 'finished.')
+ self.build.subprojects[dirname] = True
+ self.subprojects.update(subi.subprojects)
+ self.subprojects[dirname] = SubprojectHolder(subi)
+ self.build_def_files += subi.build_def_files
+ return self.subprojects[dirname]
+
+ @stringArgs
+ @noKwargs
+ def func_get_option(self, nodes, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Argument required for get_option.')
+ optname = args[0]
+ try:
+ return self.environment.get_coredata().get_builtin_option(optname)
+ except RuntimeError:
+ pass
+ try:
+ return self.environment.coredata.compiler_options[optname].value
+ except KeyError:
+ pass
+ if optname not in coredata.builtin_options and self.is_subproject():
+ optname = self.subproject + ':' + optname
+ try:
+ return self.environment.coredata.user_options[optname].value
+ except KeyError:
+ raise InterpreterException('Tried to access unknown option "%s".' % optname)
+
+ @noKwargs
+ def func_configuration_data(self, node, args, kwargs):
+ if len(args) != 0:
+ raise InterpreterException('configuration_data takes no arguments')
+ return ConfigurationDataHolder()
+
+ def parse_default_options(self, default_options):
+ if not isinstance(default_options, list):
+ default_options = [default_options]
+ for option in default_options:
+ if not isinstance(option, str):
+ mlog.debug(option)
+ raise InterpreterException('Default options must be strings')
+ if '=' not in option:
+ raise InterpreterException('All default options must be of type key=value.')
+ key, value = option.split('=', 1)
+ builtin_options = self.coredata.builtin_options
+ if key in builtin_options:
+ if not hasattr(self.environment.cmd_line_options, value):
+ self.coredata.set_builtin_option(key, value)
+ # If this was set on the command line, do not override.
+ else:
+ newoptions = [option] + self.environment.cmd_line_options.projectoptions
+ self.environment.cmd_line_options.projectoptions = newoptions
+
+ @stringArgs
+ def func_project(self, node, args, kwargs):
+ if len(args) < 2:
+ raise InvalidArguments('Not enough arguments to project(). Needs at least the project name and one language')
+
+ if not self.is_subproject():
+ self.build.project_name = args[0]
+ if self.environment.first_invocation and 'default_options' in kwargs:
+ self.parse_default_options(kwargs['default_options'])
+ self.active_projectname = args[0]
+ self.project_version = kwargs.get('version', 'undefined')
+ proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
+ self.build.dep_manifest[args[0]] = {'version': self.project_version,
+ 'license': proj_license}
+ if self.subproject in self.build.projects:
+ raise InvalidCode('Second call to project().')
+ if not self.is_subproject() and 'subproject_dir' in kwargs:
+ self.subproject_dir = kwargs['subproject_dir']
+
+ if 'meson_version' in kwargs:
+ cv = coredata.version
+ pv = kwargs['meson_version']
+ if not mesonlib.version_compare(cv, pv):
+ raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
+ self.build.projects[self.subproject] = args[0]
+ mlog.log('Project name: ', mlog.bold(args[0]), sep='')
+ self.add_languages(node, args[1:])
+ langs = self.coredata.compilers.keys()
+ if 'vala' in langs:
+ if not 'c' in langs:
+ raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.')
+
+ @noKwargs
+ @stringArgs
+ def func_add_languages(self, node, args, kwargs):
+ self.add_languages(node, args)
+
+ @noKwargs
+ def func_message(self, node, args, kwargs):
+ # reduce arguments again to avoid flattening posargs
+ (posargs, _) = self.reduce_arguments(node.args)
+ if len(posargs) != 1:
+ raise InvalidArguments('Expected 1 argument, got %d' % len(posargs))
+
+ arg = posargs[0]
+ if isinstance(arg, list):
+ argstr = stringifyUserArguments(arg)
+ elif isinstance(arg, str):
+ argstr = arg
+ elif isinstance(arg, int):
+ argstr = str(arg)
+ else:
+ raise InvalidArguments('Function accepts only strings, integers, lists and lists thereof.')
+
+ mlog.log(mlog.bold('Message:'), argstr)
+ return
+
+
+ @noKwargs
+ def func_error(self, node, args, kwargs):
+ self.validate_arguments(args, 1, [str])
+ raise InterpreterException('Error encountered: ' + args[0])
+
+ def add_languages(self, node, args):
+ need_cross_compiler = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler()
+ for lang in args:
+ lang = lang.lower()
+ if lang in self.coredata.compilers:
+ comp = self.coredata.compilers[lang]
+ cross_comp = self.coredata.cross_compilers.get(lang, None)
+ else:
+ cross_comp = None
+ if lang == 'c':
+ comp = self.environment.detect_c_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.environment.detect_c_compiler(True)
+ elif lang == 'cpp':
+ comp = self.environment.detect_cpp_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.environment.detect_cpp_compiler(True)
+ elif lang == 'objc':
+ comp = self.environment.detect_objc_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.environment.detect_objc_compiler(True)
+ elif lang == 'objcpp':
+ comp = self.environment.detect_objcpp_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.environment.detect_objcpp_compiler(True)
+ elif lang == 'java':
+ comp = self.environment.detect_java_compiler()
+ if need_cross_compiler:
+ cross_comp = comp # Java is platform independent.
+ elif lang == 'cs':
+ comp = self.environment.detect_cs_compiler()
+ if need_cross_compiler:
+ cross_comp = comp # C# is platform independent.
+ elif lang == 'vala':
+ comp = self.environment.detect_vala_compiler()
+ if need_cross_compiler:
+ cross_comp = comp # Vala is too (I think).
+ elif lang == 'rust':
+ comp = self.environment.detect_rust_compiler()
+ if need_cross_compiler:
+ cross_comp = comp # FIXME, probably not correct.
+ elif lang == 'fortran':
+ comp = self.environment.detect_fortran_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.environment.detect_fortran_compiler(True)
+ elif lang == 'swift':
+ comp = self.environment.detect_swift_compiler()
+ if need_cross_compiler:
+ raise InterpreterException('Cross compilation with Swift is not working yet.')
+ #cross_comp = self.environment.detect_fortran_compiler(True)
+ else:
+ raise InvalidCode('Tried to use unknown language "%s".' % lang)
+ comp.sanity_check(self.environment.get_scratch_dir())
+ self.coredata.compilers[lang] = comp
+ if cross_comp is not None:
+ cross_comp.sanity_check(self.environment.get_scratch_dir())
+ self.coredata.cross_compilers[lang] = cross_comp
+ new_options = comp.get_options()
+ optprefix = lang + '_'
+ for i in new_options:
+ if not i.startswith(optprefix):
+ raise InterpreterException('Internal error, %s has incorrect prefix.' % i)
+ cmd_prefix = i + '='
+ for cmd_arg in self.environment.cmd_line_options.projectoptions:
+ if cmd_arg.startswith(cmd_prefix):
+ value = cmd_arg.split('=', 1)[1]
+ new_options[i].set_value(value)
+ new_options.update(self.coredata.compiler_options)
+ self.coredata.compiler_options = new_options
+ mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='')
+ if not comp.get_language() in self.coredata.external_args:
+ (ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp.get_language())
+ self.coredata.external_args[comp.get_language()] = ext_compile_args
+ self.coredata.external_link_args[comp.get_language()] = ext_link_args
+ self.build.add_compiler(comp)
+ if need_cross_compiler:
+ mlog.log('Cross %s compiler: ' % lang, mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='')
+ self.build.add_cross_compiler(cross_comp)
+ if self.environment.is_cross_build() and not need_cross_compiler:
+ self.build.add_cross_compiler(comp)
+
+ def func_find_program(self, node, args, kwargs):
+ self.validate_arguments(args, 1, [str])
+ required = kwargs.get('required', True)
+ if not isinstance(required, bool):
+ raise InvalidArguments('"required" argument must be a boolean.')
+ exename = args[0]
+ if exename in self.coredata.ext_progs and\
+ self.coredata.ext_progs[exename].found():
+ return ExternalProgramHolder(self.coredata.ext_progs[exename])
+ # Search for scripts relative to current subdir.
+ search_dir = os.path.join(self.environment.get_source_dir(), self.subdir)
+ extprog = dependencies.ExternalProgram(exename, search_dir=search_dir)
+ progobj = ExternalProgramHolder(extprog)
+ self.coredata.ext_progs[exename] = extprog
+ if required and not progobj.found():
+ raise InvalidArguments('Program "%s" not found.' % exename)
+ return progobj
+
+ def func_find_library(self, node, args, kwargs):
+ self.validate_arguments(args, 1, [str])
+ required = kwargs.get('required', True)
+ if not isinstance(required, bool):
+ raise InvalidArguments('"required" argument must be a boolean.')
+ libname = args[0]
+ # We do not cache found libraries because they can come
+ # and go between invocations wildly. As an example we
+ # may find the 64 bit version but need instead the 32 bit
+ # one that is not installed. If we cache the found path
+ # then we will never found the new one if it get installed.
+ # This causes a bit of a slowdown as libraries are rechecked
+ # on every regen, but since it is a fast operation it should be
+ # ok.
+ if 'dirs' in kwargs:
+ search_dirs = kwargs['dirs']
+ if not isinstance(search_dirs, list):
+ search_dirs = [search_dirs]
+ for i in search_dirs:
+ if not isinstance(i, str):
+ raise InvalidCode('Directory entry is not a string.')
+ if not os.path.isabs(i):
+ raise InvalidCode('Search directory %s is not an absolute path.' % i)
+ else:
+ search_dirs = None
+ result = self.environment.find_library(libname, search_dirs)
+ extlib = dependencies.ExternalLibrary(libname, result)
+ libobj = ExternalLibraryHolder(extlib)
+ if required and not libobj.found():
+ raise InvalidArguments('External library "%s" not found.' % libname)
+ return libobj
+
+ def func_dependency(self, node, args, kwargs):
+ self.validate_arguments(args, 1, [str])
+ name = args[0]
+ identifier = dependencies.get_dep_identifier(name, kwargs)
+ if identifier in self.coredata.deps:
+ dep = self.coredata.deps[identifier]
+ else:
+ dep = dependencies.Dependency() # Returns always false for dep.found()
+ if not dep.found():
+ try:
+ dep = dependencies.find_external_dependency(name, self.environment, kwargs)
+ except dependencies.DependencyException:
+ if 'fallback' in kwargs:
+ return self.dependency_fallback(kwargs)
+ raise
+ self.coredata.deps[identifier] = dep
+ return DependencyHolder(dep)
+
+ def dependency_fallback(self, kwargs):
+ fbinfo = kwargs['fallback']
+ check_stringlist(fbinfo)
+ if len(fbinfo) != 2:
+ raise InterpreterException('Fallback info must have exactly two items.')
+ dirname, varname = fbinfo
+ self.do_subproject(dirname, kwargs)
+ return self.subprojects[dirname].get_variable_method([varname], {})
+
+ def func_executable(self, node, args, kwargs):
+ return self.build_target(node, args, kwargs, ExecutableHolder)
+
+ def func_static_lib(self, node, args, kwargs):
+ return self.build_target(node, args, kwargs, StaticLibraryHolder)
+
+ def func_shared_lib(self, node, args, kwargs):
+ return self.build_target(node, args, kwargs, SharedLibraryHolder)
+
+ def func_library(self, node, args, kwargs):
+ if self.coredata.get_builtin_option('default_library') == 'shared':
+ return self.func_shared_lib(node, args, kwargs)
+ return self.func_static_lib(node, args, kwargs)
+
+ def func_jar(self, node, args, kwargs):
+ return self.build_target(node, args, kwargs, JarHolder)
+
+ def func_build_target(self, node, args, kwargs):
+ if 'target_type' not in kwargs:
+ raise InterpreterException('Missing target_type keyword argument')
+ target_type = kwargs.pop('target_type')
+ if target_type == 'executable':
+ return self.func_executable(node, args, kwargs)
+ elif target_type == 'shared_library':
+ return self.func_shared_lib(node, args, kwargs)
+ elif target_type == 'static_library':
+ return self.func_static_lib(node, args, kwargs)
+ elif target_type == 'library':
+ return self.func_library(node, args, kwargs)
+ elif target_type == 'jar':
+ return self.func_jar(node, args, kwargs)
+ else:
+ raise InterpreterException('Unknown target_type.')
+
+ def func_vcs_tag(self, node, args, kwargs):
+ fallback = kwargs.pop('fallback', None)
+ if not isinstance(fallback, str):
+ raise InterpreterException('Keyword argument fallback must exist and be a string.')
+ replace_string = kwargs.pop('replace_string', '@VCS_TAG@')
+ regex_selector = '(.*)' # default regex selector for custom command: use complete output
+ vcs_cmd = kwargs.get('command', None)
+ if vcs_cmd and not isinstance(vcs_cmd, list):
+ vcs_cmd = [vcs_cmd]
+ source_dir = os.path.normpath(os.path.join(self.environment.get_source_dir(), self.subdir))
+ if vcs_cmd:
+ # Is the command an executable in path or maybe a script in the source tree?
+ vcs_cmd[0] = shutil.which(vcs_cmd[0]) or os.path.join(source_dir, vcs_cmd[0])
+ else:
+ vcs = mesonlib.detect_vcs(source_dir)
+ if vcs:
+ mlog.log('Found %s repository at %s' % (vcs['name'], vcs['wc_dir']))
+ vcs_cmd = vcs['get_rev'].split()
+ regex_selector = vcs['rev_regex']
+ else:
+ vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string
+ # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command...
+ kwargs['command'] = [sys.executable,
+ self.environment.get_build_command(),
+ '--internal',
+ 'vcstagger',
+ '@INPUT0@',
+ '@OUTPUT0@',
+ fallback,
+ source_dir,
+ replace_string,
+ regex_selector] + vcs_cmd
+ kwargs.setdefault('build_always', True)
+ return self.func_custom_target(node, [kwargs['output']], kwargs)
+
+ @stringArgs
+ def func_custom_target(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Incorrect number of arguments')
+ name = args[0]
+ tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, kwargs))
+ self.add_target(name, tg.held_object)
+ return tg
+
+ @noKwargs
+ def func_run_target(self, node, args, kwargs):
+ if len(args) < 2:
+ raise InterpreterException('Incorrect number of arguments')
+ cleaned_args = []
+ for i in args:
+ try:
+ i = i.held_object
+ except AttributeError:
+ pass
+ if not isinstance(i, (str, build.BuildTarget, build.CustomTarget)):
+ mlog.debug('Wrong type:', str(i))
+ raise InterpreterException('Invalid argument to run_target.')
+ cleaned_args.append(i)
+ name = cleaned_args[0]
+ command = cleaned_args[1]
+ cmd_args = cleaned_args[2:]
+ tg = RunTargetHolder(name, command, cmd_args, self.subdir)
+ self.add_target(name, tg.held_object)
+ return tg
+
+ def func_generator(self, node, args, kwargs):
+ gen = GeneratorHolder(self, args, kwargs)
+ self.generators.append(gen)
+ return gen
+
+ def func_benchmark(self, node, args, kwargs):
+ self.add_test(node, args, kwargs, False)
+
+ def func_test(self, node, args, kwargs):
+ self.add_test(node, args, kwargs, True)
+
+ def add_test(self, node, args, kwargs, is_base_test):
+ if len(args) != 2:
+ raise InterpreterException('Incorrect number of arguments')
+ if not isinstance(args[0], str):
+ raise InterpreterException('First argument of test must be a string.')
+ if not isinstance(args[1], (ExecutableHolder, JarHolder, ExternalProgramHolder)):
+ raise InterpreterException('Second argument must be executable.')
+ par = kwargs.get('is_parallel', True)
+ if not isinstance(par, bool):
+ raise InterpreterException('Keyword argument is_parallel must be a boolean.')
+ cmd_args = kwargs.get('args', [])
+ if not isinstance(cmd_args, list):
+ cmd_args = [cmd_args]
+ for i in cmd_args:
+ if not isinstance(i, (str, mesonlib.File)):
+ raise InterpreterException('Command line arguments must be strings')
+ envlist = kwargs.get('env', [])
+ if not isinstance(envlist, list):
+ envlist = [envlist]
+ env = {}
+ for e in envlist:
+ if '=' not in e:
+ raise InterpreterException('Env var definition must be of type key=val.')
+ (k, val) = e.split('=', 1)
+ k = k.strip()
+ val = val.strip()
+ if ' ' in k:
+ raise InterpreterException('Env var key must not have spaces in it.')
+ env[k] = val
+ valgrind_args = kwargs.get('valgrind_args', [])
+ if not isinstance(valgrind_args, list):
+ valgrind_args = [valgrind_args]
+ for a in valgrind_args:
+ if not isinstance(a, str):
+ raise InterpreterException('Valgrind_arg not a string.')
+ should_fail = kwargs.get('should_fail', False)
+ if not isinstance(should_fail, bool):
+ raise InterpreterException('Keyword argument should_fail must be a boolean.')
+ timeout = kwargs.get('timeout', 30)
+ if 'workdir' in kwargs:
+ workdir = kwargs['workdir']
+ if not isinstance(workdir, str):
+ raise InterpreterException('Workdir keyword argument must be a string.')
+ if not os.path.isabs(workdir):
+ raise InterpreterException('Workdir keyword argument must be an absolute path.')
+ else:
+ workdir = None
+ if not isinstance(timeout, int):
+ raise InterpreterException('Timeout must be an integer.')
+ suite = mesonlib.stringlistify(kwargs.get('suite', ''))
+ if self.is_subproject():
+ newsuite = []
+ for s in suite:
+ newsuite.append(self.subproject.replace(' ', '_').replace('.', '_') + '.' + s)
+ suite = newsuite
+ t = Test(args[0], suite, args[1].held_object, par, cmd_args, env, should_fail, valgrind_args, timeout, workdir)
+ if is_base_test:
+ self.build.tests.append(t)
+ mlog.debug('Adding test "', mlog.bold(args[0]), '".', sep='')
+ else:
+ self.build.benchmarks.append(t)
+ mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='')
+
+ @stringArgs
+ def func_install_headers(self, node, args, kwargs):
+ h = Headers(self.subdir, args, kwargs)
+ self.build.headers.append(h)
+ return h
+
+ @stringArgs
+ def func_install_man(self, node, args, kwargs):
+ m = Man(self.subdir, args, kwargs)
+ self.build.man.append(m)
+ return m
+
+ @noKwargs
+ def func_subdir(self, node, args, kwargs):
+ self.validate_arguments(args, 1, [str])
+ if '..' in args[0]:
+ raise InvalidArguments('Subdir contains ..')
+ if self.subdir == '' and args[0] == self.subproject_dir:
+ raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.')
+ prev_subdir = self.subdir
+ subdir = os.path.join(prev_subdir, args[0])
+ if subdir in self.visited_subdirs:
+ raise InvalidArguments('Tried to enter directory "%s", which has already been visited.'\
+ % subdir)
+ self.visited_subdirs[subdir] = True
+ self.subdir = subdir
+ try:
+ os.makedirs(os.path.join(self.environment.build_dir, subdir))
+ except FileExistsError:
+ pass
+ buildfilename = os.path.join(self.subdir, environment.build_filename)
+ self.build_def_files.append(buildfilename)
+ absname = os.path.join(self.environment.get_source_dir(), buildfilename)
+ if not os.path.isfile(absname):
+ raise InterpreterException('Nonexistant build def file %s.' % buildfilename)
+ code = open(absname).read()
+ assert(isinstance(code, str))
+ try:
+ codeblock = mparser.Parser(code).parse()
+ except coredata.MesonException as me:
+ me.file = buildfilename
+ raise me
+ self.evaluate_codeblock(codeblock)
+ self.subdir = prev_subdir
+
+ @stringArgs
+ def func_install_data(self, node, args, kwargs):
+ data = DataHolder(True, self.subdir, args, kwargs)
+ self.build.data.append(data.held_object)
+ return data
+
+ @stringArgs
+ def func_install_subdir(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidArguments('Install_subdir requires exactly one argument.')
+ if not 'install_dir' in kwargs:
+ raise InvalidArguments('Missing keyword argument install_dir')
+ install_dir = kwargs['install_dir']
+ if not isinstance(install_dir, str):
+ raise InvalidArguments('Keyword argument install_dir not a string.')
+ idir = InstallDir(self.subdir, args[0], install_dir)
+ self.build.install_dirs.append(idir)
+ return idir
+
+ def func_configure_file(self, node, args, kwargs):
+ if len(args) > 0:
+ raise InterpreterException("configure_file takes only keyword arguments.")
+ if not 'input' in kwargs:
+ raise InterpreterException('Required keyword argument "input" not defined.')
+ if not 'output' in kwargs:
+ raise InterpreterException('Required keyword argument "output" not defined.')
+ inputfile = kwargs['input']
+ output = kwargs['output']
+ if not isinstance(inputfile, str):
+ raise InterpreterException('Input must be a string.')
+ if not isinstance(output, str):
+ raise InterpreterException('Output must be a string.')
+ if 'configuration' in kwargs:
+ conf = kwargs['configuration']
+ if not isinstance(conf, ConfigurationDataHolder):
+ raise InterpreterException('Argument "configuration" is not of type configuration_data')
+
+ conffile = os.path.join(self.subdir, inputfile)
+ if conffile not in self.build_def_files:
+ self.build_def_files.append(conffile)
+ os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
+ ifile_abs = os.path.join(self.environment.source_dir, self.subdir, inputfile)
+ ofile_abs = os.path.join(self.environment.build_dir, self.subdir, output)
+ mesonlib.do_conf_file(ifile_abs, ofile_abs, conf.held_object)
+ conf.mark_used()
+ elif 'command' in kwargs:
+ res = self.func_run_command(node, kwargs['command'], {})
+ if res.returncode != 0:
+ raise InterpreterException('Running configure command failed.\n%s\n%s' %
+ (res.stdout, res.stderr))
+ else:
+ raise InterpreterException('Configure_file must have either "configuration" or "command".')
+ if isinstance(kwargs.get('install_dir', None), str):
+ self.build.data.append(DataHolder(False, self.subdir, [output], kwargs).held_object)
+ return mesonlib.File.from_built_file(self.subdir, output)
+
+ @stringArgs
+ def func_include_directories(self, node, args, kwargs):
+ absbase = os.path.join(self.environment.get_source_dir(), self.subdir)
+ for a in args:
+ absdir = os.path.join(absbase, a)
+ if not os.path.isdir(absdir):
+ raise InvalidArguments('Include dir %s does not exist.' % a)
+ is_system = kwargs.get('is_system', False)
+ if not isinstance(is_system, bool):
+ raise InvalidArguments('Is_system must be boolean.')
+ i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system))
+ return i
+
+ @stringArgs
+ def func_add_global_arguments(self, node, args, kwargs):
+ if self.subproject != '':
+ raise InvalidCode('Global arguments can not be set in subprojects because there is no way to make that reliable.')
+ if self.global_args_frozen:
+ raise InvalidCode('Tried to set global arguments after a build target has been declared.\nThis is not permitted. Please declare all global arguments before your targets.')
+ if not 'language' in kwargs:
+ raise InvalidCode('Missing language definition in add_global_arguments')
+ lang = kwargs['language'].lower()
+ if lang in self.build.global_args:
+ self.build.global_args[lang] += args
+ else:
+ self.build.global_args[lang] = args
+
+ def flatten(self, args):
+ if isinstance(args, mparser.StringNode):
+ return args.value
+ if isinstance(args, str):
+ return args
+ if isinstance(args, InterpreterObject):
+ return args
+ if isinstance(args, int):
+ return args
+ result = []
+ for a in args:
+ if isinstance(a, list):
+ rest = self.flatten(a)
+ result = result + rest
+ elif isinstance(a, mparser.StringNode):
+ result.append(a.value)
+ else:
+ result.append(a)
+ return result
+
+ def source_strings_to_files(self, sources):
+ results = []
+ for s in sources:
+ if isinstance(s, mesonlib.File) or isinstance(s, GeneratedListHolder) or \
+ isinstance(s, CustomTargetHolder):
+ pass
+ elif isinstance(s, str):
+ s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)
+ else:
+ raise InterpreterException("Source item is not string or File-type object.")
+ results.append(s)
+ return results
+
+ def add_target(self, name, tobj):
+ if name in coredata.forbidden_target_names:
+ raise InvalidArguments('Target name "%s" is reserved for Meson\'s internal use. Please rename.'\
+ % name)
+ # To permit an executable and a shared library to have the
+ # same name, such as "foo.exe" and "libfoo.a".
+ idname = tobj.get_id()
+ if idname in self.build.targets:
+ raise InvalidCode('Tried to create target "%s", but a target of that name already exists.' % name)
+ self.build.targets[idname] = tobj
+ if idname not in self.coredata.target_guids:
+ self.coredata.target_guids[idname] = str(uuid.uuid4()).upper()
+
+ def build_target(self, node, args, kwargs, targetholder):
+ name = args[0]
+ sources = args[1:]
+ if self.environment.is_cross_build():
+ if kwargs.get('native', False):
+ is_cross = False
+ else:
+ is_cross = True
+ else:
+ is_cross = False
+ try:
+ kw_src = self.flatten(kwargs['sources'])
+ if not isinstance(kw_src, list):
+ kw_src = [kw_src]
+ except KeyError:
+ kw_src = []
+ sources += kw_src
+ sources = self.source_strings_to_files(sources)
+ objs = self.flatten(kwargs.get('objects', []))
+ kwargs['dependencies'] = self.flatten(kwargs.get('dependencies', []))
+ if not isinstance(objs, list):
+ objs = [objs]
+ self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
+ if targetholder is ExecutableHolder:
+ targetclass = build.Executable
+ elif targetholder is SharedLibraryHolder:
+ targetclass = build.SharedLibrary
+ elif targetholder is StaticLibraryHolder:
+ targetclass = build.StaticLibrary
+ elif targetholder is JarHolder:
+ targetclass = build.Jar
+ else:
+ mlog.debug('Unknown target type:', str(targetholder))
+ raise RuntimeError('Unreachable code')
+ target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs)
+ l = targetholder(target, self)
+ self.add_target(name, l.held_object)
+ self.global_args_frozen = True
+ return l
+
+ def check_sources_exist(self, subdir, sources):
+ for s in sources:
+ if not isinstance(s, str):
+ continue # This means a generated source and they always exist.
+ fname = os.path.join(subdir, s)
+ if not os.path.isfile(fname):
+ raise InterpreterException('Tried to add non-existing source %s.' % s)
+
+ def function_call(self, node):
+ func_name = node.func_name
+ (posargs, kwargs) = self.reduce_arguments(node.args)
+ if func_name in self.funcs:
+ return self.funcs[func_name](node, self.flatten(posargs), kwargs)
+ else:
+ raise InvalidCode('Unknown function "%s".' % func_name)
+
+ def is_assignable(self, value):
+ if isinstance(value, InterpreterObject) or \
+ isinstance(value, dependencies.Dependency) or\
+ isinstance(value, str) or\
+ isinstance(value, int) or \
+ isinstance(value, list) or \
+ isinstance(value, mesonlib.File):
+ return True
+ return False
+
+ def assignment(self, node):
+ assert(isinstance(node, mparser.AssignmentNode))
+ var_name = node.var_name
+ if not isinstance(var_name, str):
+ raise InvalidArguments('Tried to assign value to a non-variable.')
+ value = self.evaluate_statement(node.value)
+ value = self.to_native(value)
+ if not self.is_assignable(value):
+ raise InvalidCode('Tried to assign an invalid value to variable.')
+ self.set_variable(var_name, value)
+ return value
+
+ def reduce_arguments(self, args):
+ assert(isinstance(args, mparser.ArgumentNode))
+ if args.incorrect_order():
+ raise InvalidArguments('All keyword arguments must be after positional arguments.')
+ reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
+ reduced_kw = {}
+ for key in args.kwargs.keys():
+ if not isinstance(key, str):
+ raise InvalidArguments('Keyword argument name is not a string.')
+ a = args.kwargs[key]
+ reduced_kw[key] = self.evaluate_statement(a)
+ if not isinstance(reduced_pos, list):
+ reduced_pos = [reduced_pos]
+ return (reduced_pos, reduced_kw)
+
+ def string_method_call(self, obj, method_name, args):
+ obj = self.to_native(obj)
+ (posargs, _) = self.reduce_arguments(args)
+ if method_name == 'strip':
+ return obj.strip()
+ elif method_name == 'format':
+ return self.format_string(obj, args)
+ elif method_name == 'split':
+ if len(posargs) > 1:
+ raise InterpreterException('Split() must have at most one argument.')
+ elif len(posargs) == 1:
+ s = posargs[0]
+ if not isinstance(s, str):
+ raise InterpreterException('Split() argument must be a string')
+ return obj.split(s)
+ else:
+ return obj.split()
+ elif method_name == 'startswith' or method_name == 'endswith':
+ s = posargs[0]
+ if not isinstance(s, str):
+ raise InterpreterException('Argument must be a string.')
+ if method_name == 'startswith':
+ return obj.startswith(s)
+ return obj.endswith(s)
+ raise InterpreterException('Unknown method "%s" for a string.' % method_name)
+
+ def to_native(self, arg):
+ if isinstance(arg, mparser.StringNode) or \
+ isinstance(arg, mparser.NumberNode) or \
+ isinstance(arg, mparser.BooleanNode):
+ return arg.value
+ return arg
+
+ def format_string(self, templ, args):
+ templ = self.to_native(templ)
+ if isinstance(args, mparser.ArgumentNode):
+ args = args.arguments
+ for (i, arg) in enumerate(args):
+ arg = self.to_native(self.evaluate_statement(arg))
+ if isinstance(arg, bool): # Python boolean is upper case.
+ arg = str(arg).lower()
+ templ = templ.replace('@{}@'.format(i), str(arg))
+ return templ
+
+ def method_call(self, node):
+ invokable = node.source_object
+ if isinstance(invokable, mparser.IdNode):
+ object_name = invokable.value
+ obj = self.get_variable(object_name)
+ else:
+ obj = self.evaluate_statement(invokable)
+ method_name = node.name
+ if method_name == 'extract_objects' and self.environment.coredata.get_builtin_option('unity'):
+ raise InterpreterException('Single object files can not be extracted in Unity builds.')
+ args = node.args
+ if isinstance(obj, mparser.StringNode):
+ obj = obj.get_value()
+ if isinstance(obj, str):
+ return self.string_method_call(obj, method_name, args)
+ if isinstance(obj, list):
+ return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0])
+ if not isinstance(obj, InterpreterObject):
+ raise InvalidArguments('Variable "%s" is not callable.' % object_name)
+ (args, kwargs) = self.reduce_arguments(args)
+ if method_name == 'extract_objects':
+ self.validate_extraction(obj.held_object)
+ return obj.method_call(method_name, self.flatten(args), kwargs)
+
+ # Only permit object extraction from the same subproject
+ def validate_extraction(self, buildtarget):
+ if not self.subdir.startswith(self.subproject_dir):
+ if buildtarget.subdir.startswith(self.subproject_dir):
+ raise InterpreterException('Tried to extract objects from a subproject target.')
+ else:
+ if not buildtarget.subdir.startswith(self.subproject_dir):
+ raise InterpreterException('Tried to extract objects from the main project from a subproject.')
+ if self.subdir.split('/')[1] != buildtarget.subdir.split('/')[1]:
+ raise InterpreterException('Tried to extract objects from a different subproject.')
+
+ def array_method_call(self, obj, method_name, args):
+ if method_name == 'contains':
+ return self.check_contains(obj, args)
+ elif method_name == 'length':
+ return len(obj)
+ elif method_name == 'get':
+ index = args[0]
+ if not isinstance(index, int):
+ raise InvalidArguments('Array index must be a number.')
+ if index < -len(obj) or index >= len(obj):
+ raise InvalidArguments('Array index %s is out of bounds for array of size %d.' % (index, len(obj)))
+ return obj[index]
+ raise InterpreterException('Arrays do not have a method called "%s".' % method_name)
+
+ def check_contains(self, obj, args):
+ if len(args) != 1:
+ raise InterpreterException('Contains method takes exactly one argument.')
+ item = args[0]
+ for element in obj:
+ if isinstance(element, list):
+ found = self.check_contains(element, args)
+ if found:
+ return True
+ try:
+ if element == item:
+ return True
+ except Exception:
+ pass
+ return False
+
+ def evaluate_if(self, node):
+ assert(isinstance(node, mparser.IfClauseNode))
+ for i in node.ifs:
+ result = self.evaluate_statement(i.condition)
+ if not(isinstance(result, bool)):
+ print(result)
+ raise InvalidCode('If clause does not evaluate to true or false.')
+ if result:
+ self.evaluate_codeblock(i.block)
+ return
+ if not isinstance(node.elseblock, mparser.EmptyNode):
+ self.evaluate_codeblock(node.elseblock)
+
+ def evaluate_foreach(self, node):
+ assert(isinstance(node, mparser.ForeachClauseNode))
+ varname = node.varname.value
+ items = self.evaluate_statement(node.items)
+ if not isinstance(items, list):
+ raise InvalidArguments('Items of foreach loop is not an array')
+ for item in items:
+ self.set_variable(varname, item)
+ self.evaluate_codeblock(node.block)
+
+ def evaluate_plusassign(self, node):
+ assert(isinstance(node, mparser.PlusAssignmentNode))
+ varname = node.var_name
+ addition = self.evaluate_statement(node.value)
+ # Remember that all variables are immutable. We must always create a
+ # full new variable and then assign it.
+ old_variable = self.get_variable(varname)
+ if not isinstance(old_variable, list):
+ raise InvalidArguments('The += operator currently only works with arrays.')
+ # Add other data types here.
+ else:
+ if isinstance(addition, list):
+ new_value = old_variable + addition
+ else:
+ new_value = old_variable + [addition]
+ self.set_variable(varname, new_value)
+
+ def evaluate_indexing(self, node):
+ assert(isinstance(node, mparser.IndexNode))
+ iobject = self.evaluate_statement(node.iobject)
+ if not isinstance(iobject, list):
+ raise InterpreterException('Tried to index a non-array object.')
+ index = self.evaluate_statement(node.index)
+ if not isinstance(index, int):
+ raise InterpreterException('Index value is not an integer.')
+ if index < -len(iobject) or index >= len(iobject):
+ raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
+ return iobject[index]
+
+ def is_elementary_type(self, v):
+ if isinstance(v, (int, float, str, bool, list)):
+ return True
+ return False
+
+ def evaluate_comparison(self, node):
+ v1 = self.evaluate_statement(node.left)
+ v2 = self.evaluate_statement(node.right)
+ if self.is_elementary_type(v1):
+ val1 = v1
+ else:
+ val1 = v1.value
+ if self.is_elementary_type(v2):
+ val2 = v2
+ else:
+ val2 = v2.value
+ if node.ctype == '==':
+ return val1 == val2
+ elif node.ctype == '!=':
+ return val1 != val2
+ else:
+ raise InvalidCode('You broke me.')
+
+ def evaluate_andstatement(self, cur):
+ l = self.evaluate_statement(cur.left)
+ if isinstance(l, mparser.BooleanNode):
+ l = l.value
+ if not isinstance(l, bool):
+ raise InterpreterException('First argument to "and" is not a boolean.')
+ if not l:
+ return False
+ r = self.evaluate_statement(cur.right)
+ if isinstance(r, mparser.BooleanNode):
+ r = r.value
+ if not isinstance(r, bool):
+ raise InterpreterException('Second argument to "and" is not a boolean.')
+ return r
+
+ def evaluate_orstatement(self, cur):
+ l = self.evaluate_statement(cur.left)
+ if isinstance(l, mparser.BooleanNode):
+ l = l.get_value()
+ if not isinstance(l, bool):
+ raise InterpreterException('First argument to "or" is not a boolean.')
+ if l:
+ return True
+ r = self.evaluate_statement(cur.right)
+ if isinstance(r, mparser.BooleanNode):
+ r = r.get_value()
+ if not isinstance(r, bool):
+ raise InterpreterException('Second argument to "or" is not a boolean.')
+ return r
+
+ def evaluate_notstatement(self, cur):
+ v = self.evaluate_statement(cur.value)
+ if isinstance(v, mparser.BooleanNode):
+ v = v.value
+ if not isinstance(v, bool):
+ raise InterpreterException('Argument to "not" is not a boolean.')
+ return not v
+
+ def evaluate_uminusstatement(self, cur):
+ v = self.evaluate_statement(cur.value)
+ if isinstance(v, mparser.NumberNode):
+ v = v.value
+ if not isinstance(v, int):
+ raise InterpreterException('Argument to negation is not an integer.')
+ return -v
+
+ def evaluate_arithmeticstatement(self, cur):
+ l = self.to_native(self.evaluate_statement(cur.left))
+ r = self.to_native(self.evaluate_statement(cur.right))
+
+ if cur.operation == 'add':
+ try:
+ return l + r
+ except Exception as e:
+ raise InvalidCode('Invalid use of addition: ' + str(e))
+ elif cur.operation == 'sub':
+ if not isinstance(l, int) or not isinstance(r, int):
+ raise InvalidCode('Subtraction works only with integers.')
+ return l - r
+ elif cur.operation == 'mul':
+ if not isinstance(l, int) or not isinstance(r, int):
+ raise InvalidCode('Multiplication works only with integers.')
+ return l * r
+ elif cur.operation == 'div':
+ if not isinstance(l, int) or not isinstance(r, int):
+ raise InvalidCode('Division works only with integers.')
+ return l // r
+ else:
+ raise InvalidCode('You broke me.')
+
+ def evaluate_arraystatement(self, cur):
+ (arguments, kwargs) = self.reduce_arguments(cur.args)
+ if len(kwargs) > 0:
+ raise InvalidCode('Keyword arguments are invalid in array construction.')
+ return arguments
+
+ def is_subproject(self):
+ return self.subproject != ''
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
new file mode 100644
index 0000000..f174425
--- /dev/null
+++ b/mesonbuild/mconf.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+
+# Copyright 2014-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
+import argparse
+from . import coredata, mesonlib
+from .coredata import build_types, warning_levels, libtypelist
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('-D', action='append', default=[], dest='sets',
+ help='Set an option to the given value.')
+parser.add_argument('directory', nargs='*')
+
+class ConfException(coredata.MesonException):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+class Conf:
+ def __init__(self, build_dir):
+ self.build_dir = build_dir
+ self.coredata_file = os.path.join(build_dir, 'meson-private/coredata.dat')
+ self.build_file = os.path.join(build_dir, 'meson-private/build.dat')
+ if not os.path.isfile(self.coredata_file) or not os.path.isfile(self.build_file):
+ raise ConfException('Directory %s does not seem to be a Meson build directory.' % build_dir)
+ self.coredata = pickle.load(open(self.coredata_file, 'rb'))
+ self.build = pickle.load(open(self.build_file, 'rb'))
+ if self.coredata.version != coredata.version:
+ raise ConfException('Version mismatch (%s vs %s)' %
+ (coredata.version, self.coredata.version))
+
+ def save(self):
+ # Only called if something has changed so overwrite unconditionally.
+ pickle.dump(self.coredata, open(self.coredata_file, 'wb'))
+ # We don't write the build file because any changes to it
+ # are erased when Meson is executed the nex time, i.e. the next
+ # time Ninja is run.
+
+ def print_aligned(self, arr):
+ if len(arr) == 0:
+ return
+ titles = ['Option', 'Description', 'Current Value', '']
+ longest_name = len(titles[0])
+ longest_descr = len(titles[1])
+ longest_value = len(titles[2])
+ longest_possible_value = len(titles[3])
+ for x in arr:
+ longest_name = max(longest_name, len(x[0]))
+ longest_descr = max(longest_descr, len(x[1]))
+ longest_value = max(longest_value, len(str(x[2])))
+ longest_possible_value = max(longest_possible_value, len(x[3]))
+
+ if longest_possible_value > 0:
+ titles[3] = 'Possible Values'
+ print(' %s%s %s%s %s%s %s' % (titles[0], ' '*(longest_name - len(titles[0])), titles[1], ' '*(longest_descr - len(titles[1])), titles[2], ' '*(longest_value - len(titles[2])), titles[3]))
+ print(' %s%s %s%s %s%s %s' % ('-'*len(titles[0]), ' '*(longest_name - len(titles[0])), '-'*len(titles[1]), ' '*(longest_descr - len(titles[1])), '-'*len(titles[2]), ' '*(longest_value - len(titles[2])), '-'*len(titles[3])))
+ for i in arr:
+ name = i[0]
+ descr = i[1]
+ value = i[2]
+ if isinstance(value, bool):
+ value = 'true' if value else 'false'
+ possible_values = i[3]
+ namepad = ' '*(longest_name - len(name))
+ descrpad = ' '*(longest_descr - len(descr))
+ valuepad = ' '*(longest_value - len(str(value)))
+ f = ' %s%s %s%s %s%s %s' % (name, namepad, descr, descrpad, value, valuepad, possible_values)
+ print(f)
+
+ def set_options(self, options):
+ for o in options:
+ if '=' not in o:
+ raise ConfException('Value "%s" not of type "a=b".' % o)
+ (k, v) = o.split('=', 1)
+ if self.coredata.is_builtin_option(k):
+ self.coredata.set_builtin_option(k, v)
+ elif k in self.coredata.user_options:
+ tgt = self.coredata.user_options[k]
+ tgt.set_value(v)
+ elif k in self.coredata.compiler_options:
+ tgt = self.coredata.compiler_options[k]
+ tgt.set_value(v)
+ elif k.endswith('linkargs'):
+ lang = k[:-8]
+ if not lang in self.coredata.external_link_args:
+ raise ConfException('Unknown language %s in linkargs.' % lang)
+ # TODO, currently split on spaces, make it so that user
+ # can pass in an array string.
+ newvalue = v.split()
+ self.coredata.external_link_args[lang] = newvalue
+ elif k.endswith('args'):
+ lang = k[:-4]
+ if not lang in self.coredata.external_args:
+ raise ConfException('Unknown language %s in compile args' % lang)
+ # TODO same fix as above
+ newvalue = v.split()
+ self.coredata.external_args[lang] = newvalue
+ else:
+ raise ConfException('Unknown option %s.' % k)
+
+
+ def print_conf(self):
+ print('Core properties:')
+ print(' Source dir', self.build.environment.source_dir)
+ print(' Build dir ', self.build.environment.build_dir)
+ print('')
+ print('Core options:')
+ carr = []
+ booleans = '[true, false]'
+ carr.append(['buildtype', 'Build type', self.coredata.get_builtin_option('buildtype'), build_types])
+ carr.append(['warning_level', 'Warning level', self.coredata.get_builtin_option('warning_level'), warning_levels])
+ carr.append(['strip', 'Strip on install', self.coredata.get_builtin_option('strip'), booleans])
+ carr.append(['coverage', 'Coverage report', self.coredata.get_builtin_option('coverage'), booleans])
+ carr.append(['use_pch', 'Precompiled headers', self.coredata.get_builtin_option('use_pch'), booleans])
+ carr.append(['unity', 'Unity build', self.coredata.get_builtin_option('unity'), booleans])
+ carr.append(['default_library', 'Default library type', self.coredata.get_builtin_option('default_library'), libtypelist])
+ self.print_aligned(carr)
+ print('')
+ print('Compiler arguments:')
+ for (lang, args) in self.coredata.external_args.items():
+ print(' ' + lang + 'args', str(args))
+ print('')
+ print('Linker args:')
+ for (lang, args) in self.coredata.external_link_args.items():
+ print(' ' + lang + 'linkargs', str(args))
+ print('')
+ print('Compiler options:')
+ okeys = sorted(self.coredata.compiler_options.keys())
+ if len(okeys) == 0:
+ print(' No compiler options\n')
+ else:
+ coarr = []
+ for k in okeys:
+ o = self.coredata.compiler_options[k]
+ coarr.append([k, o.description, o.value, ''])
+ self.print_aligned(coarr)
+ print('')
+ print('Directories:')
+ parr = []
+ parr.append(['prefix', 'Install prefix', self.coredata.get_builtin_option('prefix'), ''])
+ parr.append(['libdir', 'Library directory', self.coredata.get_builtin_option('libdir'), ''])
+ parr.append(['bindir', 'Binary directory', self.coredata.get_builtin_option('bindir'), ''])
+ parr.append(['includedir', 'Header directory', self.coredata.get_builtin_option('includedir'), ''])
+ parr.append(['datadir', 'Data directory', self.coredata.get_builtin_option('datadir'), ''])
+ parr.append(['mandir', 'Man page directory', self.coredata.get_builtin_option('mandir'), ''])
+ parr.append(['localedir', 'Locale file directory', self.coredata.get_builtin_option('localedir'), ''])
+ self.print_aligned(parr)
+ print('')
+ print('Project options:')
+ if len(self.coredata.user_options) == 0:
+ print(' This project does not have any options')
+ else:
+ options = self.coredata.user_options
+ keys = list(options.keys())
+ keys.sort()
+ optarr = []
+ for key in keys:
+ opt = options[key]
+ if (opt.choices is None) or (len(opt.choices) == 0):
+ # Zero length list or string
+ choices = '';
+ else:
+ # A non zero length list or string, convert to string
+ choices = str(opt.choices);
+ optarr.append([key, opt.description, opt.value, choices])
+ self.print_aligned(optarr)
+
+def run(args):
+ args = mesonlib.expand_arguments(args)
+ if not args:
+ sys.exit(1)
+ options = parser.parse_args(args)
+ if len(options.directory) > 1:
+ print('%s <build directory>' % args[0])
+ print('If you omit the build directory, the current directory is substituted.')
+ return 1
+ if len(options.directory) == 0:
+ builddir = os.getcwd()
+ else:
+ builddir = options.directory[0]
+ try:
+ c = Conf(builddir)
+ if len(options.sets) > 0:
+ c.set_options(options.sets)
+ c.save()
+ else:
+ c.print_conf()
+ except ConfException as e:
+ print('Meson configurator encountered an error:\n')
+ print(e)
+ return(1)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
new file mode 100644
index 0000000..2ab5ce4
--- /dev/null
+++ b/mesonbuild/mesonlib.py
@@ -0,0 +1,284 @@
+# Copyright 2012-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.
+
+"""A library of random helper functionality."""
+
+import platform, subprocess, operator, os, shutil, re, sys
+
+from glob import glob
+
+from .coredata import MesonException
+
+class File:
+ def __init__(self, is_built, subdir, fname):
+ self.is_built = is_built
+ self.subdir = subdir
+ self.fname = fname
+
+ @staticmethod
+ def from_source_file(source_root, subdir, fname):
+ if not os.path.isfile(os.path.join(source_root, subdir, fname)):
+ raise MesonException('File %s does not exist.' % fname)
+ return File(False, subdir, fname)
+
+ @staticmethod
+ def from_built_file(subdir, fname):
+ return File(True, subdir, fname)
+
+ @staticmethod
+ def from_absolute_file(fname):
+ return File(False, '', fname)
+
+ def rel_to_builddir(self, build_to_src):
+ if self.is_built:
+ return os.path.join(self.subdir, self.fname)
+ else:
+ return os.path.join(build_to_src, self.subdir, self.fname)
+
+ def endswith(self, ending):
+ return self.fname.endswith(ending)
+
+ def split(self, s):
+ return self.fname.split(s)
+
+ def __eq__(self, other):
+ return (self.fname, self.subdir, self.is_built) == (other.fname, other.subdir, other.is_built)
+
+ def __hash__(self):
+ return hash((self.fname, self.subdir, self.is_built))
+
+def flatten(item):
+ if not isinstance(item, list):
+ return item
+ result = []
+ for i in item:
+ if isinstance(i, list):
+ result += flatten(i)
+ else:
+ result.append(i)
+ return result
+
+def is_osx():
+ return platform.system().lower() == 'darwin'
+
+def is_linux():
+ return platform.system().lower() == 'linux'
+
+def is_windows():
+ platname = platform.system().lower()
+ return platname == 'windows' or 'mingw' in platname
+
+def is_32bit():
+ return not(sys.maxsize > 2**32)
+
+def is_debianlike():
+ try:
+ open('/etc/debian_version', 'r')
+ return True
+ except FileNotFoundError:
+ return False
+
+def exe_exists(arglist):
+ try:
+ p = subprocess.Popen(arglist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p.communicate()
+ if p.returncode == 0:
+ return True
+ except FileNotFoundError:
+ pass
+ return False
+
+def detect_vcs(source_dir):
+ vcs_systems = [
+ dict(name = 'git', cmd = 'git', repo_dir = '.git', get_rev = 'git describe --dirty=+', rev_regex = '(.*)', dep = '.git/logs/HEAD'),
+ dict(name = 'mercurial', cmd = 'hg', repo_dir = '.hg', get_rev = 'hg id -n', rev_regex = '(.*)', dep = '.hg/dirstate'),
+ dict(name = 'subversion', cmd = 'svn', repo_dir = '.svn', get_rev = 'svn info', rev_regex = 'Revision: (.*)', dep = '.svn/wc.db'),
+ dict(name = 'bazaar', cmd = 'bzr', repo_dir = '.bzr', get_rev = 'bzr revno', rev_regex = '(.*)', dep = '.bzr'),
+ ]
+
+ segs = source_dir.replace('\\', '/').split('/')
+ for i in range(len(segs), -1, -1):
+ curdir = '/'.join(segs[:i])
+ for vcs in vcs_systems:
+ if os.path.isdir(os.path.join(curdir, vcs['repo_dir'])) and shutil.which(vcs['cmd']):
+ vcs['wc_dir'] = curdir
+ return vcs
+ return None
+
+numpart = re.compile('[0-9.]+')
+
+def version_compare(vstr1, vstr2):
+ match = numpart.match(vstr1.strip())
+ if match is None:
+ raise MesonException('Unconparable version string %s.' % vstr1)
+ vstr1 = match.group(0)
+ if vstr2.startswith('>='):
+ cmpop = operator.ge
+ vstr2 = vstr2[2:]
+ elif vstr2.startswith('<='):
+ cmpop = operator.le
+ vstr2 = vstr2[2:]
+ elif vstr2.startswith('!='):
+ cmpop = operator.ne
+ vstr2 = vstr2[2:]
+ elif vstr2.startswith('=='):
+ cmpop = operator.eq
+ vstr2 = vstr2[2:]
+ elif vstr2.startswith('='):
+ cmpop = operator.eq
+ vstr2 = vstr2[1:]
+ elif vstr2.startswith('>'):
+ cmpop = operator.gt
+ vstr2 = vstr2[1:]
+ elif vstr2.startswith('<'):
+ cmpop = operator.lt
+ vstr2 = vstr2[1:]
+ else:
+ cmpop = operator.eq
+ varr1 = [int(x) for x in vstr1.split('.')]
+ varr2 = [int(x) for x in vstr2.split('.')]
+ return cmpop(varr1, varr2)
+
+def default_libdir():
+ try:
+ archpath = subprocess.check_output(['dpkg-architecture', '-qDEB_HOST_MULTIARCH']).decode().strip()
+ return 'lib/' + archpath
+ except:
+ pass
+ if os.path.isdir('/usr/lib64'):
+ return 'lib64'
+ return 'lib'
+
+def get_library_dirs():
+ if is_windows():
+ return ['C:/mingw/lib'] # Fixme
+ if is_osx():
+ return ['/usr/lib'] # Fix me as well.
+ # The following is probably Debian/Ubuntu specific.
+ # /usr/local/lib is first because it contains stuff
+ # installed by the sysadmin and is probably more up-to-date
+ # than /usr/lib. If you feel that this search order is
+ # problematic, please raise the issue on the mailing list.
+ unixdirs = ['/usr/local/lib', '/usr/lib', '/lib']
+ plat = subprocess.check_output(['uname', '-m']).decode().strip()
+ # This is a terrible hack. I admit it and I'm really sorry.
+ # I just don't know what the correct solution is.
+ if plat == 'i686':
+ plat = 'i386'
+ if plat.startswith('arm'):
+ plat = 'arm'
+ unixdirs += glob('/usr/lib/' + plat + '*')
+ if os.path.exists('/usr/lib64'):
+ unixdirs.append('/usr/lib64')
+ unixdirs += glob('/lib/' + plat + '*')
+ if os.path.exists('/lib64'):
+ unixdirs.append('/lib64')
+ unixdirs += glob('/lib/' + plat + '*')
+ return unixdirs
+
+
+def do_replacement(regex, line, confdata):
+ match = re.search(regex, line)
+ while match:
+ varname = match.group(1)
+ if varname in confdata.keys():
+ var = confdata.get(varname)
+ if isinstance(var, str):
+ pass
+ elif isinstance(var, int):
+ var = str(var)
+ else:
+ raise RuntimeError('Tried to replace a variable with something other than a string or int.')
+ else:
+ var = ''
+ line = line.replace('@' + varname + '@', var)
+ match = re.search(regex, line)
+ return line
+
+def do_mesondefine(line, confdata):
+ arr = line.split()
+ if len(arr) != 2:
+ raise MesonException('#mesondefine does not contain exactly two tokens: %s', line.strip())
+ varname = arr[1]
+ try:
+ v = confdata.get(varname)
+ except KeyError:
+ return '/* undef %s */\n' % varname
+ if isinstance(v, bool):
+ if v:
+ return '#define %s\n' % varname
+ else:
+ return '#undef %s\n' % varname
+ elif isinstance(v, int):
+ return '#define %s %d\n' % (varname, v)
+ elif isinstance(v, str):
+ return '#define %s %s\n' % (varname, v)
+ else:
+ raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)
+
+
+def do_conf_file(src, dst, confdata):
+ data = open(src).readlines()
+ regex = re.compile('@(.*?)@')
+ result = []
+ for line in data:
+ if line.startswith('#mesondefine'):
+ line = do_mesondefine(line, confdata)
+ else:
+ line = do_replacement(regex, line, confdata)
+ result.append(line)
+ dst_tmp = dst + '~'
+ open(dst_tmp, 'w').writelines(result)
+ shutil.copymode(src, dst_tmp)
+ replace_if_different(dst, dst_tmp)
+
+
+def replace_if_different(dst, dst_tmp):
+ # If contents are identical, don't touch the file to prevent
+ # unnecessary rebuilds.
+ try:
+ if open(dst, 'r').read() == open(dst_tmp, 'r').read():
+ os.unlink(dst_tmp)
+ return
+ except FileNotFoundError:
+ pass
+ os.replace(dst_tmp, dst)
+
+def stringlistify(item):
+ if isinstance(item, str):
+ item = [item]
+ if not isinstance(item, list):
+ raise MesonException('Item is not an array')
+ for i in item:
+ if not isinstance(i, str):
+ raise MesonException('List item not a string.')
+ return item
+
+def expand_arguments(args):
+ expended_args = []
+ for arg in args:
+ if not arg.startswith('@'):
+ expended_args.append(arg)
+ continue
+
+ args_file = arg[1:]
+ try:
+ with open(args_file) as f:
+ extended_args = f.read().split()
+ expended_args += extended_args
+ except Exception as e:
+ print('Error expanding command line arguments, %s not found' % args_file)
+ print(e)
+ return None
+ return expended_args
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
new file mode 100644
index 0000000..82f30fe
--- /dev/null
+++ b/mesonbuild/mesonmain.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python3
+
+# Copyright 2012-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, stat, traceback, pickle, argparse
+import datetime
+import os.path
+from . import environment, interpreter, mesonlib
+from . import build
+import platform
+from . import mlog, coredata
+
+from .coredata import MesonException, build_types, layouts, warning_levels, libtypelist
+
+backendlist = ['ninja', 'vs2010', 'xcode']
+
+parser = argparse.ArgumentParser()
+
+default_warning = '1'
+
+if mesonlib.is_windows():
+ def_prefix = 'c:/'
+else:
+ def_prefix = '/usr/local'
+
+parser.add_argument('--prefix', default=def_prefix, dest='prefix',
+ help='the installation prefix (default: %(default)s)')
+parser.add_argument('--libdir', default=mesonlib.default_libdir(), dest='libdir',
+ help='the installation subdir of libraries (default: %(default)s)')
+parser.add_argument('--bindir', default='bin', dest='bindir',
+ help='the installation subdir of executables (default: %(default)s)')
+parser.add_argument('--includedir', default='include', dest='includedir',
+ help='relative path of installed headers (default: %(default)s)')
+parser.add_argument('--datadir', default='share', dest='datadir',
+ help='relative path to the top of data file subdirectory (default: %(default)s)')
+parser.add_argument('--mandir', default='share/man', dest='mandir',
+ help='relative path of man files (default: %(default)s)')
+parser.add_argument('--localedir', default='share/locale', dest='localedir',
+ help='relative path of locale data (default: %(default)s)')
+parser.add_argument('--backend', default='ninja', dest='backend', choices=backendlist,
+ help='backend to use (default: %(default)s)')
+parser.add_argument('--buildtype', default='debug', choices=build_types, dest='buildtype',
+ help='build type go use (default: %(default)s)')
+parser.add_argument('--strip', action='store_true', dest='strip', default=False,\
+ help='strip targets on install (default: %(default)s)')
+parser.add_argument('--enable-gcov', action='store_true', dest='coverage', default=False,\
+ help='measure test coverage')
+parser.add_argument('--disable-pch', action='store_false', dest='use_pch', default=True,\
+ help='do not use precompiled headers')
+parser.add_argument('--unity', action='store_true', dest='unity', default=False,\
+ help='unity build')
+parser.add_argument('--werror', action='store_true', dest='werror', default=False,\
+ help='Treat warnings as errors')
+parser.add_argument('--layout', choices=layouts, dest='layout', default='mirror',\
+ help='Build directory layout.')
+parser.add_argument('--default-library', choices=libtypelist, dest='default_library',
+ default='shared', help='Default library type.')
+parser.add_argument('--warnlevel', default=default_warning, dest='warning_level', choices=warning_levels,\
+ help='Level of compiler warnings to use (larger is more, default is %(default)s)')
+parser.add_argument('--cross-file', default=None, dest='cross_file',
+ help='file describing cross compilation environment')
+parser.add_argument('-D', action='append', dest='projectoptions', default=[],
+ help='Set project options.')
+parser.add_argument('-v', '--version', action='store_true', dest='print_version', default=False,
+ help='Print version.')
+parser.add_argument('directories', nargs='*')
+
+class MesonApp():
+
+ def __init__(self, dir1, dir2, script_file, handshake, options):
+ (self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake)
+ if not os.path.isabs(options.prefix):
+ raise RuntimeError('--prefix must be an absolute path.')
+ self.meson_script_file = script_file
+ self.options = options
+
+ def has_build_file(self, dirname):
+ fname = os.path.join(dirname, environment.build_filename)
+ return os.path.exists(fname)
+
+ def validate_core_dirs(self, dir1, dir2):
+ ndir1 = os.path.abspath(dir1)
+ ndir2 = os.path.abspath(dir2)
+ if not stat.S_ISDIR(os.stat(ndir1).st_mode):
+ raise RuntimeError('%s is not a directory' % dir1)
+ if not stat.S_ISDIR(os.stat(ndir2).st_mode):
+ raise RuntimeError('%s is not a directory' % dir2)
+ if os.path.samefile(dir1, dir2):
+ raise RuntimeError('Source and build directories must not be the same. Create a pristine build directory.')
+ if self.has_build_file(ndir1):
+ if self.has_build_file(ndir2):
+ raise RuntimeError('Both directories contain a build file %s.' % environment.build_filename)
+ return (ndir1, ndir2)
+ if self.has_build_file(ndir2):
+ return (ndir2, ndir1)
+ raise RuntimeError('Neither directory contains a build file %s.' % environment.build_filename)
+
+ def validate_dirs(self, dir1, dir2, handshake):
+ (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2)
+ priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat')
+ if os.path.exists(priv_dir):
+ if not handshake:
+ msg = '''Trying to run Meson on a build directory that has already been configured.
+If you want to build it, just run your build command (e.g. ninja) inside the
+build directory. Meson will autodetect any changes in your setup and regenerate
+itself as required.'''
+ raise RuntimeError(msg)
+ else:
+ if handshake:
+ raise RuntimeError('Something went terribly wrong. Please file a bug.')
+ return (src_dir, build_dir)
+
+ def generate(self):
+ env = environment.Environment(self.source_dir, self.build_dir, self.meson_script_file, self.options)
+ mlog.initialize(env.get_log_dir())
+ mlog.debug('Build started at', datetime.datetime.now().isoformat())
+ mlog.debug('Python binary:', sys.executable)
+ mlog.debug('Python system:', platform.system())
+ mlog.log(mlog.bold('The Meson build system'))
+ mlog.log('Version:', coredata.version)
+ mlog.log('Source dir:', mlog.bold(self.source_dir))
+ mlog.log('Build dir:', mlog.bold(self.build_dir))
+ if env.is_cross_build():
+ mlog.log('Build type:', mlog.bold('cross build'))
+ else:
+ mlog.log('Build type:', mlog.bold('native build'))
+ b = build.Build(env)
+ if self.options.backend == 'ninja':
+ from . import ninjabackend
+ g = ninjabackend.NinjaBackend(b)
+ elif self.options.backend == 'vs2010':
+ from . import vs2010backend
+ g = vs2010backend.Vs2010Backend(b)
+ elif self.options.backend == 'xcode':
+ from . import xcodebackend
+ g = xcodebackend.XCodeBackend(b)
+ else:
+ raise RuntimeError('Unknown backend "%s".' % self.options.backend)
+
+ intr = interpreter.Interpreter(b, g)
+ if env.is_cross_build():
+ mlog.log('Host machine cpu family:', mlog.bold(intr.builtin['host_machine'].cpu_family_method([], {})))
+ mlog.log('Host machine cpu:', mlog.bold(intr.builtin['host_machine'].cpu_method([], {})))
+ mlog.log('Target machine cpu family:', mlog.bold(intr.builtin['target_machine'].cpu_family_method([], {})))
+ mlog.log('Target machine cpu:', mlog.bold(intr.builtin['target_machine'].cpu_method([], {})))
+ mlog.log('Build machine cpu family:', mlog.bold(intr.builtin['build_machine'].cpu_family_method([], {})))
+ mlog.log('Build machine cpu:', mlog.bold(intr.builtin['build_machine'].cpu_method([], {})))
+ intr.run()
+ g.generate(intr)
+ env.generating_finished()
+ dumpfile = os.path.join(env.get_scratch_dir(), 'build.dat')
+ pickle.dump(b, open(dumpfile, 'wb'))
+
+def run_script_command(args):
+ cmdname = args[0]
+ cmdargs = args[1:]
+ if cmdname == 'test':
+ import mesonbuild.scripts.meson_test as abc
+ cmdfunc = abc.run
+ elif cmdname == 'benchmark':
+ import mesonbuild.scripts.meson_benchmark as abc
+ cmdfunc = abc.run
+ elif cmdname == 'install':
+ import mesonbuild.scripts.meson_install as abc
+ cmdfunc = abc.run
+ elif cmdname == 'commandrunner':
+ import mesonbuild.scripts.commandrunner as abc
+ cmdfunc = abc.run
+ elif cmdname == 'delsuffix':
+ import mesonbuild.scripts.delwithsuffix as abc
+ cmdfunc = abc.run
+ elif cmdname == 'depfixer':
+ import mesonbuild.scripts.depfixer as abc
+ cmdfunc = abc.run
+ elif cmdname == 'dirchanger':
+ import mesonbuild.scripts.dirchanger as abc
+ cmdfunc = abc.run
+ elif cmdname == 'gtkdoc':
+ import meson.scripts.gtkdochelper as abc
+ cmdfunc = abc.run
+ elif cmdname == 'regencheck':
+ import mesonbuild.scripts.regen_checker as abc
+ cmdfunc = abc.run
+ elif cmdname == 'symbolextractor':
+ import mesonbuild.scripts.symbolextractor as abc
+ cmdfunc = abc.run
+ elif cmdname == 'vcstagger':
+ import mesonbuild.scripts.vcstagger as abc
+ cmdfunc = abc.run
+ else:
+ raise MesonException('Unknown internal command {}.'.format(cmdname))
+ return cmdfunc(cmdargs)
+
+def run(mainfile, args):
+ if sys.version_info < (3, 3):
+ print('Meson works correctly only with python 3.3+.')
+ print('You have python %s.' % sys.version)
+ print('Please update your environment')
+ return 1
+ if args[0] == '--internal':
+ if args[1] != 'regenerate':
+ sys.exit(run_script_command(args[1:]))
+ args = args[2:]
+ handshake = True
+ else:
+ handshake = False
+ args = mesonlib.expand_arguments(args)
+ if not args:
+ return 1
+ options = parser.parse_args(args)
+ if options.print_version:
+ print(coredata.version)
+ return 0
+ args = options.directories
+ if len(args) == 0 or len(args) > 2:
+ print('%s <source directory> <build directory>' % sys.argv[0])
+ print('If you omit either directory, the current directory is substituted.')
+ return 1
+ dir1 = args[0]
+ if len(args) > 1:
+ dir2 = args[1]
+ else:
+ dir2 = '.'
+ while os.path.islink(mainfile):
+ resolved = os.readlink(mainfile)
+ if resolved[0] != '/':
+ mainfile = os.path.join(os.path.dirname(mainfile), resolved)
+ else:
+ mainfile = resolved
+ try:
+ app = MesonApp(dir1, dir2, mainfile, handshake, options)
+ except Exception as e:
+ # Log directory does not exist, so just print
+ # to stdout.
+ print('Error during basic setup:\n')
+ print(e)
+ return 1
+ try:
+ app.generate()
+ except Exception as e:
+ if isinstance(e, MesonException):
+ if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'):
+ mlog.log(mlog.red('\nMeson encountered an error in file %s, line %d, column %d:' % (e.file, e.lineno, e.colno)))
+ else:
+ mlog.log(mlog.red('\nMeson encountered an error:'))
+ mlog.log(e)
+ else:
+ traceback.print_exc()
+ return 1
+ return 0
diff --git a/mesonbuild/mesonmain.ui b/mesonbuild/mesonmain.ui
new file mode 100644
index 0000000..209584b
--- /dev/null
+++ b/mesonbuild/mesonmain.ui
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>740</width>
+ <height>613</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Meson</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Project</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="project_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Source directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="srcdir_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Build directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="builddir_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Build type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="buildtype_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Backend</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="backend_label">
+ <property name="text">
+ <string>Ninja</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="2">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <widget class="QWidget" name="core_tab">
+ <attribute name="title">
+ <string>Core data</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QTreeView" name="core_view"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="path_tab">
+ <attribute name="title">
+ <string>Paths</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QTreeView" name="path_view"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="option_tab">
+ <attribute name="title">
+ <string>Options</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <layout class="QFormLayout" name="option_form"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="dependency_tab">
+ <attribute name="title">
+ <string>Dependencies</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QTreeView" name="dep_view"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="target_tab">
+ <attribute name="title">
+ <string>Build targets</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QTreeView" name="target_view"/>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="save_button">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="compile_button">
+ <property name="text">
+ <string>Compile</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="test_button">
+ <property name="text">
+ <string>Run tests</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="install_button">
+ <property name="text">
+ <string>Install</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="clean_button">
+ <property name="text">
+ <string>Clean</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>740</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionSave"/>
+ <addaction name="actionQuit"/>
+ </widget>
+ <addaction name="menuFile"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionSave">
+ <property name="text">
+ <string>&amp;Save</string>
+ </property>
+ </action>
+ <action name="actionQuit">
+ <property name="text">
+ <string>&amp;Quit</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/mesonbuild/mesonrunner.ui b/mesonbuild/mesonrunner.ui
new file mode 100644
index 0000000..942c6bd
--- /dev/null
+++ b/mesonbuild/mesonrunner.ui
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>rundialog</class>
+ <widget class="QDialog" name="rundialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>581</width>
+ <height>368</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>External process output</string>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="timelabel">
+ <property name="text">
+ <string>Compile time: 0:0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="termbutton">
+ <property name="text">
+ <string>Terminate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QTextEdit" name="console">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/mesonbuild/mesonstart.ui b/mesonbuild/mesonstart.ui
new file mode 100644
index 0000000..c6c5f96
--- /dev/null
+++ b/mesonbuild/mesonstart.ui
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>644</width>
+ <height>192</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Meson</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Source directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="source_entry">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="source_browse_button">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Build directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="build_entry">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="build_browse_button">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Cross file</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="cross_entry">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="cross_browse_button">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="generate_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Generate</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>644</width>
+ <height>25</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/mesonbuild/mgui.py b/mesonbuild/mgui.py
new file mode 100644
index 0000000..6e57ce7
--- /dev/null
+++ b/mesonbuild/mgui.py
@@ -0,0 +1,565 @@
+#!/usr/bin/env python3
+
+# Copyright 2013-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 sys, os, pickle, time, shutil
+from . import build, coredata, environment, mesonlib
+from PyQt5 import uic
+from PyQt5.QtWidgets import QApplication, QMainWindow, QHeaderView
+from PyQt5.QtWidgets import QComboBox, QCheckBox
+from PyQt5.QtCore import QAbstractItemModel, QModelIndex, QVariant, QTimer
+import PyQt5.QtCore
+import PyQt5.QtWidgets
+
+priv_dir = os.path.split(os.path.abspath(os.path.realpath(__file__)))[0]
+
+class PathModel(QAbstractItemModel):
+ def __init__(self, coredata):
+ super().__init__()
+ self.coredata = coredata
+ self.names = ['Prefix', 'Library dir', 'Binary dir', 'Include dir', 'Data dir',\
+ 'Man dir', 'Locale dir']
+ self.attr_name = ['prefix', 'libdir', 'bindir', 'includedir', 'datadir', \
+ 'mandir', 'localedir']
+
+ def args(self, index):
+ if index.column() == 1:
+ editable = PyQt5.QtCore.Qt.ItemIsEditable
+ else:
+ editable= 0
+ return PyQt5.QtCore.Qt.ItemIsSelectable | PyQt5.QtCore.Qt.ItemIsEnabled | editable
+
+ def rowCount(self, index):
+ if index.isValid():
+ return 0
+ return len(self.names)
+
+ def columnCount(self, index):
+ return 2
+
+ def headerData(self, section, orientation, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ if section == 1:
+ return QVariant('Path')
+ return QVariant('Type')
+
+ def index(self, row, column, parent):
+ return self.createIndex(row, column)
+
+ def data(self, index, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ row = index.row()
+ column = index.column()
+ if column == 0:
+ return self.names[row]
+ return getattr(self.coredata, self.attr_name[row])
+
+ def parent(self, index):
+ return QModelIndex()
+
+ def setData(self, index, value, role):
+ if role != PyQt5.QtCore.Qt.EditRole:
+ return False
+ row = index.row()
+ column = index.column()
+ s = str(value)
+ setattr(self.coredata, self.attr_name[row], s)
+ self.dataChanged.emit(self.createIndex(row, column), self.createIndex(row, column))
+ return True
+
+class TargetModel(QAbstractItemModel):
+ def __init__(self, builddata):
+ super().__init__()
+ self.targets = []
+ for target in builddata.get_targets().values():
+ name = target.get_basename()
+ num_sources = len(target.get_sources()) + len(target.get_generated_sources())
+ if isinstance(target, build.Executable):
+ typename = 'executable'
+ elif isinstance(target, build.SharedLibrary):
+ typename = 'shared library'
+ elif isinstance(target, build.StaticLibrary):
+ typename = 'static library'
+ elif isinstance(target, build.CustomTarget):
+ typename = 'custom'
+ else:
+ typename = 'unknown'
+ if target.should_install():
+ installed = 'Yes'
+ else:
+ installed = 'No'
+ self.targets.append((name, typename, installed, num_sources))
+
+ def args(self, index):
+ return PyQt5.QtCore.Qt.ItemIsSelectable | PyQt5.QtCore.Qt.ItemIsEnabled
+
+ def rowCount(self, index):
+ if index.isValid():
+ return 0
+ return len(self.targets)
+
+ def columnCount(self, index):
+ return 4
+
+ def headerData(self, section, orientation, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ if section == 3:
+ return QVariant('Source files')
+ if section == 2:
+ return QVariant('Installed')
+ if section == 1:
+ return QVariant('Type')
+ return QVariant('Name')
+
+ def data(self, index, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ row = index.row()
+ column = index.column()
+ return self.targets[row][column]
+
+ def index(self, row, column, parent):
+ return self.createIndex(row, column)
+
+ def parent(self, index):
+ return QModelIndex()
+
+class DependencyModel(QAbstractItemModel):
+ def __init__(self, coredata):
+ super().__init__()
+ self.deps = []
+ for k in coredata.deps.keys():
+ bd = coredata.deps[k]
+ name = k
+ found = bd.found()
+ if found:
+ cflags = str(bd.get_compile_args())
+ libs = str(bd.get_link_args())
+ found = 'yes'
+ else:
+ cflags = ''
+ libs = ''
+ found = 'no'
+ self.deps.append((name, found, cflags, libs))
+
+ def args(self, index):
+ return PyQt5.QtCore.Qt.ItemIsSelectable | PyQt5.QtCore.Qt.ItemIsEnabled
+
+ def rowCount(self, index):
+ if index.isValid():
+ return 0
+ return len(self.deps)
+
+ def columnCount(self, index):
+ return 4
+
+ def headerData(self, section, orientation, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ if section == 3:
+ return QVariant('Link args')
+ if section == 2:
+ return QVariant('Compile args')
+ if section == 1:
+ return QVariant('Found')
+ return QVariant('Name')
+
+ def data(self, index, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ row = index.row()
+ column = index.column()
+ return self.deps[row][column]
+
+ def index(self, row, column, parent):
+ return self.createIndex(row, column)
+
+ def parent(self, index):
+ return QModelIndex()
+
+class CoreModel(QAbstractItemModel):
+ def __init__(self, core_data):
+ super().__init__()
+ self.elems = []
+ for langname, comp in core_data.compilers.items():
+ self.elems.append((langname + ' compiler', str(comp.get_exelist())))
+ for langname, comp in core_data.cross_compilers.items():
+ self.elems.append((langname + ' cross compiler', str(comp.get_exelist())))
+
+ def args(self, index):
+ return PyQt5.QtCore.Qt.ItemIsSelectable | PyQt5.QtCore.Qt.ItemIsEnabled
+
+ def rowCount(self, index):
+ if index.isValid():
+ return 0
+ return len(self.elems)
+
+ def columnCount(self, index):
+ return 2
+
+ def headerData(self, section, orientation, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ if section == 1:
+ return QVariant('Value')
+ return QVariant('Name')
+
+ def data(self, index, role):
+ if role != PyQt5.QtCore.Qt.DisplayRole:
+ return QVariant()
+ row = index.row()
+ column = index.column()
+ return self.elems[row][column]
+
+ def index(self, row, column, parent):
+ return self.createIndex(row, column)
+
+ def parent(self, index):
+ return QModelIndex()
+
+class OptionForm:
+ def __init__(self, coredata, form):
+ self.coredata = coredata
+ self.form = form
+ form.addRow(PyQt5.QtWidgets.QLabel("Meson options"))
+ combo = QComboBox()
+ combo.addItem('plain')
+ combo.addItem('debug')
+ combo.addItem('debugoptimized')
+ combo.addItem('release')
+ combo.setCurrentText(self.coredata.get_builtin_option('buildtype'))
+ combo.currentTextChanged.connect(self.build_type_changed)
+ self.form.addRow('Build type', combo)
+ strip = QCheckBox("")
+ strip.setChecked(self.coredata.get_builtin_option('strip'))
+ strip.stateChanged.connect(self.strip_changed)
+ self.form.addRow('Strip on install', strip)
+ coverage = QCheckBox("")
+ coverage.setChecked(self.coredata.get_builtin_option('coverage'))
+ coverage.stateChanged.connect(self.coverage_changed)
+ self.form.addRow('Enable coverage', coverage)
+ pch = QCheckBox("")
+ pch.setChecked(self.coredata.get_builtin_option('use_pch'))
+ pch.stateChanged.connect(self.pch_changed)
+ self.form.addRow('Enable pch', pch)
+ unity = QCheckBox("")
+ unity.setChecked(self.coredata.get_builtin_option('unity'))
+ unity.stateChanged.connect(self.unity_changed)
+ self.form.addRow('Unity build', unity)
+ form.addRow(PyQt5.QtWidgets.QLabel("Project options"))
+ self.set_user_options()
+
+ def set_user_options(self):
+ options = self.coredata.user_options
+ keys = list(options.keys())
+ keys.sort()
+ self.opt_keys = keys
+ self.opt_widgets = []
+ for key in keys:
+ opt = options[key]
+ if isinstance(opt, mesonlib.UserStringOption):
+ w = PyQt5.QtWidgets.QLineEdit(opt.value)
+ w.textChanged.connect(self.user_option_changed)
+ elif isinstance(opt, mesonlib.UserBooleanOption):
+ w = QCheckBox('')
+ w.setChecked(opt.value)
+ w.stateChanged.connect(self.user_option_changed)
+ elif isinstance(opt, mesonlib.UserComboOption):
+ w = QComboBox()
+ for i in opt.choices:
+ w.addItem(i)
+ w.setCurrentText(opt.value)
+ w.currentTextChanged.connect(self.user_option_changed)
+ else:
+ raise RuntimeError("Unknown option type")
+ self.opt_widgets.append(w)
+ self.form.addRow(opt.description, w)
+
+ def user_option_changed(self, dummy=None):
+ for i in range(len(self.opt_keys)):
+ key = self.opt_keys[i]
+ w = self.opt_widgets[i]
+ if isinstance(w, PyQt5.QtWidgets.QLineEdit):
+ newval = w.text()
+ elif isinstance(w, QComboBox):
+ newval = w.currentText()
+ elif isinstance(w, QCheckBox):
+ if w.checkState() == 0:
+ newval = False
+ else:
+ newval = True
+ else:
+ raise RuntimeError('Unknown widget type')
+ self.coredata.user_options[key].set_value(newval)
+
+ def build_type_changed(self, newtype):
+ self.coredata.buildtype = newtype
+
+ def strip_changed(self, newState):
+ if newState == 0:
+ ns = False
+ else:
+ ns = True
+ self.coredata.strip = ns
+
+ def coverage_changed(self, newState):
+ if newState == 0:
+ ns = False
+ else:
+ ns = True
+ self.coredata.coverage = ns
+
+ def pch_changed(self, newState):
+ if newState == 0:
+ ns = False
+ else:
+ ns = True
+ self.coredata.use_pch = ns
+
+ def unity_changed(self, newState):
+ if newState == 0:
+ ns = False
+ else:
+ ns = True
+ self.coredata.unity = ns
+
+class ProcessRunner():
+ def __init__(self, rundir, cmdlist):
+ self.cmdlist = cmdlist
+ self.ui = uic.loadUi(os.path.join(priv_dir, 'mesonrunner.ui'))
+ self.timer = QTimer(self.ui)
+ self.timer.setInterval(1000)
+ self.timer.timeout.connect(self.timeout)
+ self.process = PyQt5.QtCore.QProcess()
+ self.process.setProcessChannelMode(PyQt5.QtCore.QProcess.MergedChannels)
+ self.process.setWorkingDirectory(rundir)
+ self.process.readyRead.connect(self.read_data)
+ self.process.finished.connect(self.finished)
+ self.ui.termbutton.clicked.connect(self.terminated)
+ self.return_value = 100
+
+ def run(self):
+ self.process.start(self.cmdlist[0], self.cmdlist[1:])
+ self.timer.start()
+ self.start_time = time.time()
+ return self.ui.exec()
+
+ def read_data(self):
+ while(self.process.canReadLine()):
+ txt = bytes(self.process.readLine()).decode('utf8')
+ self.ui.console.append(txt)
+
+ def finished(self):
+ self.read_data()
+ self.ui.termbutton.setText('Done')
+ self.timer.stop()
+ self.return_value = self.process.exitCode()
+
+ def terminated(self, foo):
+ self.process.kill()
+ self.timer.stop()
+ self.ui.done(self.return_value)
+
+ def timeout(self):
+ now = time.time()
+ duration = int(now - self.start_time)
+ msg = 'Elapsed time: %d:%d' % (duration // 60, duration % 60)
+ self.ui.timelabel.setText(msg)
+
+class MesonGui():
+ def __init__(self, respawner, build_dir):
+ self.respawner = respawner
+ uifile = os.path.join(priv_dir, 'mesonmain.ui')
+ self.ui = uic.loadUi(uifile)
+ self.coredata_file = os.path.join(build_dir, 'meson-private/coredata.dat')
+ self.build_file = os.path.join(build_dir, 'meson-private/build.dat')
+ if not os.path.exists(self.coredata_file):
+ print("Argument is not build directory.")
+ sys.exit(1)
+ self.coredata = pickle.load(open(self.coredata_file, 'rb'))
+ self.build = pickle.load(open(self.build_file, 'rb'))
+ self.build_dir = self.build.environment.build_dir
+ self.src_dir = self.build.environment.source_dir
+ self.build_models()
+ self.options = OptionForm(self.coredata, self.ui.option_form)
+ self.ui.show()
+
+ def hide(self):
+ self.ui.hide()
+
+ def geometry(self):
+ return self.ui.geometry()
+
+ def move(self, x, y):
+ return self.ui.move(x, y)
+
+ def size(self):
+ return self.ui.size()
+
+ def resize(self, s):
+ return self.ui.resize(s)
+
+ def build_models(self):
+ self.path_model = PathModel(self.coredata)
+ self.target_model = TargetModel(self.build)
+ self.dep_model = DependencyModel(self.coredata)
+ self.core_model = CoreModel(self.coredata)
+ self.fill_data()
+ self.ui.core_view.setModel(self.core_model)
+ hv = QHeaderView(1)
+ hv.setModel(self.core_model)
+ self.ui.core_view.setHeader(hv)
+ self.ui.path_view.setModel(self.path_model)
+ hv = QHeaderView(1)
+ hv.setModel(self.path_model)
+ self.ui.path_view.setHeader(hv)
+ self.ui.target_view.setModel(self.target_model)
+ hv = QHeaderView(1)
+ hv.setModel(self.target_model)
+ self.ui.target_view.setHeader(hv)
+ self.ui.dep_view.setModel(self.dep_model)
+ hv = QHeaderView(1)
+ hv.setModel(self.dep_model)
+ self.ui.dep_view.setHeader(hv)
+ self.ui.compile_button.clicked.connect(self.compile)
+ self.ui.test_button.clicked.connect(self.run_tests)
+ self.ui.install_button.clicked.connect(self.install)
+ self.ui.clean_button.clicked.connect(self.clean)
+ self.ui.save_button.clicked.connect(self.save)
+
+ def fill_data(self):
+ self.ui.project_label.setText(self.build.projects[''])
+ self.ui.srcdir_label.setText(self.src_dir)
+ self.ui.builddir_label.setText(self.build_dir)
+ if self.coredata.cross_file is None:
+ btype = 'Native build'
+ else:
+ btype = 'Cross build'
+ self.ui.buildtype_label.setText(btype)
+
+ def run_process(self, cmdlist):
+ cmdlist = [shutil.which(environment.detect_ninja())] + cmdlist
+ dialog = ProcessRunner(self.build.environment.build_dir, cmdlist)
+ dialog.run()
+ # All processes (at the moment) may change cache state
+ # so reload.
+ self.respawner.respawn()
+
+ def compile(self, foo):
+ self.run_process([])
+
+ def run_tests(self, foo):
+ self.run_process(['test'])
+
+ def install(self, foo):
+ self.run_process(['install'])
+
+ def clean(self, foo):
+ self.run_process(['clean'])
+
+ def save(self, foo):
+ pickle.dump(self.coredata, open(self.coredata_file, 'wb'))
+
+class Starter():
+ def __init__(self, sdir):
+ uifile = os.path.join(priv_dir, 'mesonstart.ui')
+ self.ui = uic.loadUi(uifile)
+ self.ui.source_entry.setText(sdir)
+ self.dialog = PyQt5.QtWidgets.QFileDialog()
+ if len(sdir) == 0:
+ self.dialog.setDirectory(os.getcwd())
+ else:
+ self.dialog.setDirectory(sdir)
+ self.ui.source_browse_button.clicked.connect(self.src_browse_clicked)
+ self.ui.build_browse_button.clicked.connect(self.build_browse_clicked)
+ self.ui.cross_browse_button.clicked.connect(self.cross_browse_clicked)
+ self.ui.source_entry.textChanged.connect(self.update_button)
+ self.ui.build_entry.textChanged.connect(self.update_button)
+ self.ui.generate_button.clicked.connect(self.generate)
+ self.update_button()
+ self.ui.show()
+
+ def generate(self):
+ srcdir = self.ui.source_entry.text()
+ builddir = self.ui.build_entry.text()
+ cross = self.ui.cross_entry.text()
+ cmdlist = [os.path.join(os.path.split(__file__)[0], 'meson.py'), srcdir, builddir]
+ if cross != '':
+ cmdlist += ['--cross', cross]
+ pr = ProcessRunner(os.getcwd(), cmdlist)
+ rvalue = pr.run()
+ if rvalue == 0:
+ os.execl(__file__, 'dummy', builddir)
+
+ def update_button(self):
+ if self.ui.source_entry.text() == '' or self.ui.build_entry.text() == '':
+ self.ui.generate_button.setEnabled(False)
+ else:
+ self.ui.generate_button.setEnabled(True)
+
+ def src_browse_clicked(self):
+ self.dialog.setFileMode(2)
+ if self.dialog.exec():
+ self.ui.source_entry.setText(self.dialog.selectedFiles()[0])
+
+ def build_browse_clicked(self):
+ self.dialog.setFileMode(2)
+ if self.dialog.exec():
+ self.ui.build_entry.setText(self.dialog.selectedFiles()[0])
+
+ def cross_browse_clicked(self):
+ self.dialog.setFileMode(1)
+ if self.dialog.exec():
+ self.ui.cross_entry.setText(self.dialog.selectedFiles()[0])
+
+# Rather than rewrite all classes and arrays to be
+# updateable, just rebuild the entire GUI from
+# scratch whenever data on disk changes.
+
+class MesonGuiRespawner():
+ def __init__(self, arg):
+ self.arg = arg
+ self.gui = MesonGui(self, self.arg)
+
+ def respawn(self):
+ geo = self.gui.geometry()
+ s = self.gui.size()
+ self.gui.hide()
+ self.gui = MesonGui(self, self.arg)
+ self.gui.move(geo.x(), geo.y())
+ self.gui.resize(s)
+ # Garbage collection takes care of the old gui widget
+
+
+def run(args): # SPECIAL, Qt wants all args, including command name.
+ app = QApplication(sys.argv)
+ if len(args) == 1:
+ arg = ""
+ elif len(args) == 2:
+ arg = sys.argv[1]
+ else:
+ print(sys.argv[0], "<build or source dir>")
+ return 1
+ if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')):
+ guirespawner = MesonGuiRespawner(arg)
+ else:
+ runner = Starter(arg)
+ return app.exec_()
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv))
diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py
new file mode 100644
index 0000000..b088117
--- /dev/null
+++ b/mesonbuild/mintro.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python3
+
+# Copyright 2014-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 is a helper script for IDE developers. It allows you to
+extract information such as list of targets, files, compiler flags,
+tests and so on. All output is in JSON for simple parsing.
+
+Currently only works for the Ninja backend. Others use generated
+project files and don't need this info."""
+
+import json, pickle
+from . import coredata, build, mesonlib
+import argparse
+import sys, os
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--targets', action='store_true', dest='list_targets', default=False,
+ help='List top level targets.')
+parser.add_argument('--target-files', action='store', dest='target_files', default=None,
+ help='List source files for a given target.')
+parser.add_argument('--buildsystem-files', action='store_true', dest='buildsystem_files', default=False,
+ help='List files that make up the build system.')
+parser.add_argument('--buildoptions', action='store_true', dest='buildoptions', default=False,
+ help='List all build options.')
+parser.add_argument('--tests', action='store_true', dest='tests', default=False,
+ help='List all unit tests.')
+parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False,
+ help='List all benchmarks.')
+parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False,
+ help='list external dependencies.')
+parser.add_argument('args', nargs='+')
+
+def list_targets(coredata, builddata):
+ tlist = []
+ for (idname, target) in builddata.get_targets().items():
+ t = {}
+ t['name'] = target.get_basename()
+ t['id'] = idname
+ fname = target.get_filename()
+ if isinstance(fname, list):
+ fname = [os.path.join(target.subdir, x) for x in fname]
+ else:
+ fname = os.path.join(target.subdir, fname)
+ t['filename'] = fname
+ if isinstance(target, build.Executable):
+ typename = 'executable'
+ elif isinstance(target, build.SharedLibrary):
+ typename = 'shared library'
+ elif isinstance(target, build.StaticLibrary):
+ typename = 'static library'
+ elif isinstance(target, build.CustomTarget):
+ typename = 'custom'
+ elif isinstance(target, build.RunTarget):
+ typename = 'run'
+ else:
+ typename = 'unknown'
+ t['type'] = typename
+ if target.should_install():
+ t['installed'] = True
+ else:
+ t['installed'] = False
+ tlist.append(t)
+ print(json.dumps(tlist))
+
+def list_target_files(target_name, coredata, builddata):
+ try:
+ t = builddata.targets[target_name]
+ sources = t.sources + t.extra_files
+ subdir = t.subdir
+ except KeyError:
+ print("Unknown target %s." % target_name)
+ sys.exit(1)
+ sources = [os.path.join(i.subdir, i.fname) for i in sources]
+ print(json.dumps(sources))
+
+def list_buildoptions(coredata, builddata):
+ buildtype= {'choices': ['plain', 'debug', 'debugoptimized', 'release'],
+ 'type' : 'combo',
+ 'value' : coredata.buildtype,
+ 'description' : 'Build type',
+ 'name' : 'type'}
+ strip = {'value' : coredata.strip,
+ 'type' : 'boolean',
+ 'description' : 'Strip on install',
+ 'name' : 'strip'}
+ coverage = {'value': coredata.coverage,
+ 'type' : 'boolean',
+ 'description' : 'Enable coverage',
+ 'name' : 'coverage'}
+ pch = {'value' : coredata.use_pch,
+ 'type' : 'boolean',
+ 'description' : 'Use precompiled headers',
+ 'name' : 'pch'}
+ unity = {'value' : coredata.unity,
+ 'type' : 'boolean',
+ 'description' : 'Unity build',
+ 'name' : 'unity'}
+ optlist = [buildtype, strip, coverage, pch, unity]
+ add_keys(optlist, coredata.user_options)
+ add_keys(optlist, coredata.compiler_options)
+ print(json.dumps(optlist))
+
+def add_keys(optlist, options):
+ keys = list(options.keys())
+ keys.sort()
+ for key in keys:
+ opt = options[key]
+ optdict = {}
+ optdict['name'] = key
+ optdict['value'] = opt.value
+ if isinstance(opt, mesonlib.UserStringOption):
+ typestr = 'string'
+ elif isinstance(opt, mesonlib.UserBooleanOption):
+ typestr = 'boolean'
+ elif isinstance(opt, mesonlib.UserComboOption):
+ optdict['choices'] = opt.choices
+ typestr = 'combo'
+ elif isinstance(opt, mesonlib.UserStringArrayOption):
+ typestr = 'stringarray'
+ else:
+ raise RuntimeError("Unknown option type")
+ optdict['type'] = typestr
+ optdict['description'] = opt.description
+ optlist.append(optdict)
+
+def list_buildsystem_files(coredata, builddata):
+ src_dir = builddata.environment.get_source_dir()
+ # I feel dirty about this. But only slightly.
+ filelist = []
+ for root, _, files in os.walk(src_dir):
+ for f in files:
+ if f == 'meson.build' or f == 'meson_options.txt':
+ filelist.append(os.path.relpath(os.path.join(root, f), src_dir))
+ print(json.dumps(filelist))
+
+def list_deps(coredata):
+ result = {}
+ for d in coredata.deps.values():
+ if d.found():
+ args = {'compile_args': d.get_compile_args(),
+ 'link_args': d.get_link_args()}
+ result[d.name] = args
+ print(json.dumps(result))
+
+def list_tests(testdata):
+ result = []
+ for t in testdata:
+ to = {}
+ if isinstance(t.fname, str):
+ fname = [t.fname]
+ else:
+ fname = t.fname
+ to['cmd'] = fname + t.cmd_args
+ to['env'] = t.env
+ to['name'] = t.name
+ to['workdir'] = t.workdir
+ to['timeout'] = t.timeout
+ to['suite'] = t.suite
+ result.append(to)
+ print(json.dumps(result))
+
+def run(args):
+ options = parser.parse_args(args)
+ if len(options.args) > 1:
+ print('Too many arguments')
+ return 1
+ elif len(options.args) == 1:
+ bdir = options.args[0]
+ else:
+ bdir = ''
+ corefile = os.path.join(bdir, 'meson-private/coredata.dat')
+ buildfile = os.path.join(bdir, 'meson-private/build.dat')
+ testfile = os.path.join(bdir, 'meson-private/meson_test_setup.dat')
+ benchmarkfile = os.path.join(bdir, 'meson-private/meson_benchmark_setup.dat')
+ coredata = pickle.load(open(corefile, 'rb'))
+ builddata = pickle.load(open(buildfile, 'rb'))
+ testdata = pickle.load(open(testfile, 'rb'))
+ benchmarkdata = pickle.load(open(benchmarkfile, 'rb'))
+ if options.list_targets:
+ list_targets(coredata, builddata)
+ elif options.target_files is not None:
+ list_target_files(options.target_files, coredata, builddata)
+ elif options.buildsystem_files:
+ list_buildsystem_files(coredata, builddata)
+ elif options.buildoptions:
+ list_buildoptions(coredata, builddata)
+ elif options.tests:
+ list_tests(testdata)
+ elif options.benchmarks:
+ list_tests(benchmarkdata)
+ elif options.dependencies:
+ list_deps(coredata)
+ else:
+ print('No command specified')
+ return 1
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
new file mode 100644
index 0000000..2807c2b
--- /dev/null
+++ b/mesonbuild/mlog.py
@@ -0,0 +1,81 @@
+# 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, os, platform
+
+"""This is (mostly) a standalone module used to write logging
+information about Meson runs. Some output goes to screen,
+some to logging dir and some goes to both."""
+
+colorize_console = platform.system().lower() != 'windows' and os.isatty(sys.stdout.fileno())
+log_dir = None
+log_file = None
+
+def initialize(logdir):
+ global log_dir, log_file
+ log_dir = logdir
+ log_file = open(os.path.join(logdir, 'meson-log.txt'), 'w')
+
+def shutdown():
+ global log_file
+ if log_file is not None:
+ log_file.close()
+
+class AnsiDecorator():
+ plain_code = "\033[0m"
+
+ def __init__(self, text, code):
+ self.text = text
+ self.code = code
+
+ def get_text(self, with_codes):
+ if with_codes:
+ return self.code + self.text + AnsiDecorator.plain_code
+ return self.text
+
+def bold(text):
+ return AnsiDecorator(text, "\033[1m")
+
+def red(text):
+ return AnsiDecorator(text, "\033[1;31m")
+
+def green(text):
+ return AnsiDecorator(text, "\033[1;32m")
+
+def cyan(text):
+ return AnsiDecorator(text, "\033[1;36m")
+
+def process_markup(args, keep):
+ arr = []
+ for arg in args:
+ if isinstance(arg, str):
+ arr.append(arg)
+ elif isinstance(arg, AnsiDecorator):
+ arr.append(arg.get_text(keep))
+ else:
+ arr.append(str(arg))
+ return arr
+
+def debug(*args, **kwargs):
+ arr = process_markup(args, False)
+ if log_file is not None:
+ print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+
+def log(*args, **kwargs):
+ arr = process_markup(args, False)
+ if log_file is not None:
+ print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+ if colorize_console:
+ arr = process_markup(args, True)
+ print(*arr, **kwargs)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
new file mode 100644
index 0000000..e552b84
--- /dev/null
+++ b/mesonbuild/modules/gnome.py
@@ -0,0 +1,330 @@
+# 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.
+
+'''This module provides helper functions for Gnome/GLib related
+functionality such as gobject-introspection and gresources.'''
+
+from .. import build
+import os, sys
+import subprocess
+from ..coredata import MesonException
+from .. import mlog
+import xml.etree.ElementTree as ET
+from ..mesonlib import File
+
+girwarning_printed = False
+
+class GnomeModule:
+
+ def compile_resources(self, state, args, kwargs):
+ cmd = ['glib-compile-resources', '@INPUT@', '--generate']
+ if 'source_dir' in kwargs:
+ resource_loc = os.path.join(state.subdir, kwargs.pop('source_dir'))
+ d = os.path.join(state.build_to_src, resource_loc)
+ cmd += ['--sourcedir', d]
+ else:
+ resource_loc = state.subdir
+ if 'c_name' in kwargs:
+ cmd += ['--c-name', kwargs.pop('c_name')]
+ cmd += ['--target', '@OUTPUT@']
+ kwargs['command'] = cmd
+ output_c = args[0] + '.c'
+ output_h = args[0] + '.h'
+ resfile = args[1]
+ kwargs['depend_files'] = self.parse_gresource_xml(state, resfile, resource_loc)
+ kwargs['input'] = resfile
+ kwargs['output'] = output_c
+ target_c = build.CustomTarget(args[0]+'_c', state.subdir, kwargs)
+ kwargs['output'] = output_h
+ target_h = build.CustomTarget(args[0] + '_h', state.subdir, kwargs)
+ return [target_c, target_h]
+
+ def parse_gresource_xml(self, state, fobj, resource_loc):
+ if isinstance(fobj, File):
+ fname = fobj.fname
+ subdir = fobj.subdir
+ else:
+ fname = fobj
+ subdir = state.subdir
+ abspath = os.path.join(state.environment.source_dir, state.subdir, fname)
+ relative_part = os.path.split(fname)[0]
+ try:
+ tree = ET.parse(abspath)
+ root = tree.getroot()
+ result = []
+ for child in root[0]:
+ if child.tag != 'file':
+ mlog.log("Warning, malformed rcc file: ", os.path.join(state.subdir, fname))
+ break
+ else:
+ relfname = os.path.join(resource_loc, child.text)
+ absfname = os.path.join(state.environment.source_dir, relfname)
+ if os.path.isfile(absfname):
+ result.append(relfname)
+ else:
+ mlog.log('Warning, resource file points to nonexisting file %s.' % relfname)
+ return result
+ except Exception:
+ return []
+
+ def generate_gir(self, state, args, kwargs):
+ if len(args) != 1:
+ raise MesonException('Gir takes one argument')
+ girtarget = args[0]
+ while hasattr(girtarget, 'held_object'):
+ girtarget = girtarget.held_object
+ if not isinstance(girtarget, (build.Executable, build.SharedLibrary)):
+ raise MesonException('Gir target must be an executable or shared library')
+ try:
+ pkgstr = subprocess.check_output(['pkg-config', '--cflags', 'gobject-introspection-1.0'])
+ except Exception:
+ global girwarning_printed
+ if not girwarning_printed:
+ mlog.log(mlog.bold('Warning:'), 'gobject-introspection dependency was not found, disabling gir generation.')
+ girwarning_printed = True
+ return []
+ pkgargs = pkgstr.decode().strip().split()
+ ns = kwargs.pop('namespace')
+ nsversion = kwargs.pop('nsversion')
+ libsources = kwargs.pop('sources')
+ girfile = '%s-%s.gir' % (ns, nsversion)
+ depends = [girtarget]
+
+ scan_command = ['g-ir-scanner', '@INPUT@']
+ scan_command += pkgargs
+ scan_command += ['--no-libtool', '--namespace='+ns, '--nsversion=' + nsversion, '--warn-all',
+ '--output', '@OUTPUT@']
+
+ extra_args = kwargs.pop('extra_args', [])
+ if not isinstance(extra_args, list):
+ extra_args = [extra_args]
+ scan_command += extra_args
+
+ for incdirs in girtarget.include_dirs:
+ for incdir in incdirs.get_incdirs():
+ scan_command += ['-I%s' % os.path.join(state.environment.get_source_dir(), incdir)]
+
+ if 'link_with' in kwargs:
+ link_with = kwargs.pop('link_with')
+ if not isinstance(link_with, list):
+ link_with = [link_with]
+ for link in link_with:
+ lib = link.held_object
+ scan_command += ['-l%s' % lib.name]
+ if isinstance(lib, build.SharedLibrary):
+ scan_command += ['-L%s' %
+ os.path.join(state.environment.get_build_dir(),
+ lib.subdir)]
+ depends.append(lib)
+
+ if 'includes' in kwargs:
+ includes = kwargs.pop('includes')
+ if isinstance(includes, str):
+ scan_command += ['--include=%s' % includes]
+ elif isinstance(includes, list):
+ scan_command += ['--include=%s' % inc for inc in includes]
+ else:
+ raise MesonException('Gir includes must be str or list')
+ if state.global_args.get('c'):
+ scan_command += ['--cflags-begin']
+ scan_command += state.global_args['c']
+ scan_command += ['--cflags-end']
+ if kwargs.get('symbol_prefix'):
+ sym_prefix = kwargs.pop('symbol_prefix')
+ if not isinstance(sym_prefix, str):
+ raise MesonException('Gir symbol prefix must be str')
+ scan_command += ['--symbol-prefix=%s' % sym_prefix]
+ if kwargs.get('identifier_prefix'):
+ identifier_prefix = kwargs.pop('identifier_prefix')
+ if not isinstance(identifier_prefix, str):
+ raise MesonException('Gir identifier prefix must be str')
+ scan_command += ['--identifier-prefix=%s' % identifier_prefix]
+ if kwargs.get('export_packages'):
+ pkgs = kwargs.pop('export_packages')
+ if isinstance(pkgs, str):
+ scan_command += ['--pkg-export=%s' % pkgs]
+ elif isinstance(pkgs, list):
+ scan_command += ['--pkg-export=%s' % pkg for pkg in pkgs]
+ else:
+ raise MesonException('Gir export packages must be str or list')
+
+ deps = None
+ if 'dependencies' in kwargs:
+ deps = kwargs.pop('dependencies')
+ if not isinstance (deps, list):
+ deps = [deps]
+ for dep in deps:
+ girdir = dep.held_object.get_variable ("girdir")
+ if girdir:
+ scan_command += ["--add-include-path=%s" % girdir]
+ for lib in dep.held_object.libs:
+ if os.path.isabs(lib) and dep.held_object.is_libtool:
+ scan_command += ["-L%s" % os.path.dirname(lib)]
+ libname = os.path.basename(lib)
+ if libname.startswith("lib"):
+ libname = libname[3:]
+ libname = libname.split(".so")[0]
+ lib = "-l%s" % libname
+ scan_command += [lib]
+
+ inc_dirs = None
+ if kwargs.get('include_directories'):
+ inc_dirs = kwargs.pop('include_directories')
+ if not isinstance(inc_dirs, list):
+ inc_dirs = [inc_dirs]
+ for ind in inc_dirs:
+ if isinstance(ind.held_object, build.IncludeDirs):
+ scan_command += ['--add-include-path=%s' % inc for inc in ind.held_object.get_incdirs()]
+ else:
+ raise MesonException('Gir include dirs should be include_directories()')
+ if isinstance(girtarget, build.Executable):
+ scan_command += ['--program', girtarget]
+ elif isinstance(girtarget, build.SharedLibrary):
+ scan_command += ["-L@PRIVATE_OUTDIR_ABS_%s@" % girtarget.get_id()]
+ libname = girtarget.get_basename()
+ scan_command += ['--library', libname]
+ scankwargs = {'output' : girfile,
+ 'input' : libsources,
+ 'command' : scan_command,
+ 'depends' : depends,
+ }
+ if kwargs.get('install'):
+ scankwargs['install'] = kwargs['install']
+ scankwargs['install_dir'] = os.path.join(state.environment.get_datadir(), 'gir-1.0')
+ scan_target = GirTarget(girfile, state.subdir, scankwargs)
+
+ typelib_output = '%s-%s.typelib' % (ns, nsversion)
+ typelib_cmd = ['g-ir-compiler', scan_target, '--output', '@OUTPUT@']
+ if inc_dirs:
+ for incd in inc_dirs:
+ typelib_cmd += ['--includedir=%s' % inc for inc in
+ incd.held_object.get_incdirs()]
+ if deps:
+ for dep in deps:
+ girdir = dep.held_object.get_variable ("girdir")
+ if girdir:
+ typelib_cmd += ["--includedir=%s" % girdir]
+
+ kwargs['output'] = typelib_output
+ kwargs['command'] = typelib_cmd
+ # Note that this can't be libdir, because e.g. on Debian it points to
+ # lib/x86_64-linux-gnu but the girepo dir is always under lib.
+ kwargs['install_dir'] = 'lib/girepository-1.0'
+ typelib_target = TypelibTarget(typelib_output, state.subdir, kwargs)
+ return [scan_target, typelib_target]
+
+ def compile_schemas(self, state, args, kwargs):
+ if len(args) != 0:
+ raise MesonException('Compile_schemas does not take positional arguments.')
+ srcdir = os.path.join(state.build_to_src, state.subdir)
+ outdir = state.subdir
+ cmd = ['glib-compile-schemas', '--targetdir', outdir, srcdir]
+ kwargs['command'] = cmd
+ kwargs['input'] = []
+ kwargs['output'] = 'gschemas.compiled'
+ if state.subdir == '':
+ targetname = 'gsettings-compile'
+ else:
+ targetname = 'gsettings-compile-' + state.subdir
+ target_g = build.CustomTarget(targetname, state.subdir, kwargs)
+ return target_g
+
+ def gtkdoc(self, state, args, kwargs):
+ if len(args) != 1:
+ raise MesonException('Gtkdoc must have one positional argument.')
+ modulename = args[0]
+ if not isinstance(modulename, str):
+ raise MesonException('Gtkdoc arg must be string.')
+ if not 'src_dir' in kwargs:
+ raise MesonException('Keyword argument src_dir missing.')
+ main_file = kwargs.get('main_sgml', '')
+ if not isinstance(main_file, str):
+ raise MesonException('Main sgml keyword argument must be a string.')
+ main_xml = kwargs.get('main_xml', '')
+ if not isinstance(main_xml, str):
+ raise MesonException('Main xml keyword argument must be a string.')
+ if main_xml != '':
+ if main_file != '':
+ raise MesonException('You can only specify main_xml or main_sgml, not both.')
+ main_file = main_xml
+ src_dir = kwargs['src_dir']
+ targetname = modulename + '-doc'
+ command = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../gtkdochelper.py"))
+ if hasattr(src_dir, 'held_object'):
+ src_dir= src_dir.held_object
+ if not isinstance(src_dir, build.IncludeDirs):
+ raise MesonException('Invalidt keyword argument for src_dir.')
+ incdirs = src_dir.get_incdirs()
+ if len(incdirs) != 1:
+ raise MesonException('Argument src_dir has more than one directory specified.')
+ header_dir = os.path.join(state.environment.get_source_dir(), src_dir.get_curdir(), incdirs[0])
+ else:
+ header_dir = os.path.normpath(os.path.join(state.subdir, src_dir))
+ args = ['--sourcedir=' + state.environment.get_source_dir(),
+ '--builddir=' + state.environment.get_build_dir(),
+ '--subdir=' + state.subdir,
+ '--headerdir=' + header_dir,
+ '--mainfile=' + main_file,
+ '--modulename=' + modulename]
+ args += self.unpack_args('--htmlargs=', 'html_args', kwargs)
+ args += self.unpack_args('--scanargs=', 'scan_args', kwargs)
+ res = [build.RunTarget(targetname, command, args, state.subdir)]
+ if kwargs.get('install', True):
+ res.append(build.InstallScript([command] + args))
+ return res
+
+ def unpack_args(self, arg, kwarg_name, kwargs):
+ try:
+ new_args = kwargs[kwarg_name]
+ if not isinstance(new_args, list):
+ new_args = [new_args]
+ for i in new_args:
+ if not isinstance(i, str):
+ raise MesonException('html_args values must be strings.')
+ except KeyError:
+ return[]
+ if len(new_args) > 0:
+ return [arg + '@@'.join(new_args)]
+ return []
+
+ def gdbus_codegen(self, state, args, kwargs):
+ if len(args) != 2:
+ raise MesonException('Gdbus_codegen takes two arguments, name and xml file.')
+ namebase = args[0]
+ xml_file = args[1]
+ cmd = ['gdbus-codegen']
+ if 'interface_prefix' in kwargs:
+ cmd += ['--interface-prefix', kwargs.pop('interface_prefix')]
+ if 'namespace' in kwargs:
+ cmd += ['--c-namespace', kwargs.pop('namespace')]
+ cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@']
+ outputs = [namebase + '.c', namebase + '.h']
+ custom_kwargs = {'input' : xml_file,
+ 'output' : outputs,
+ 'command' : cmd
+ }
+ return build.CustomTarget(namebase + '-gdbus', state.subdir, custom_kwargs)
+
+def initialize():
+ mlog.log('Warning, glib compiled dependencies will not work until this upstream issue is fixed:',
+ mlog.bold('https://bugzilla.gnome.org/show_bug.cgi?id=745754'))
+ return GnomeModule()
+
+class GirTarget(build.CustomTarget):
+ def __init__(self, name, subdir, kwargs):
+ super().__init__(name, subdir, kwargs)
+
+class TypelibTarget(build.CustomTarget):
+ def __init__(self, name, subdir, kwargs):
+ super().__init__(name, subdir, kwargs)
diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py
new file mode 100644
index 0000000..c9247e6
--- /dev/null
+++ b/mesonbuild/modules/modtest.py
@@ -0,0 +1,21 @@
+# 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.
+
+class TestModule:
+
+ def print_hello(self, state, args, kwargs):
+ print('Hello from a Meson module')
+
+def initialize():
+ return TestModule()
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
new file mode 100644
index 0000000..f18decf
--- /dev/null
+++ b/mesonbuild/modules/pkgconfig.py
@@ -0,0 +1,82 @@
+# 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.
+
+from .. import coredata, build
+from .. import mesonlib
+import os
+
+class PkgConfigModule:
+
+ def print_hello(self, state, args, kwargs):
+ print('Hello from a Meson module')
+
+ def generate_pkgconfig_file(self, state, libraries, subdirs, name, description, version, filebase):
+ outdir = state.environment.scratch_dir
+ fname = os.path.join(outdir, filebase + '.pc')
+ ofile = open(fname, 'w')
+ coredata = state.environment.get_coredata()
+ ofile.write('prefix=%s\n' % coredata.get_builtin_option('prefix'))
+ ofile.write('libdir=${prefix}/%s\n' % coredata.get_builtin_option('libdir'))
+ ofile.write('includedir=${prefix}/%s\n\n' % coredata.get_builtin_option('includedir'))
+ ofile.write('Name: %s\n' % name)
+ if len(description) > 0:
+ ofile.write('Description: %s\n' % description)
+ if len(version) > 0:
+ ofile.write('Version: %s\n' % version)
+ ofile.write('Libs: -L${libdir} ')
+ for l in libraries:
+ ofile.write('-l%s ' % l.name)
+ ofile.write('\n')
+ ofile.write('CFlags: ')
+ for h in subdirs:
+ if h == '.':
+ h = ''
+ ofile.write(os.path.join('-I${includedir}', h))
+ ofile.write(' ')
+ ofile.write('\n')
+
+ def generate(self, state, args, kwargs):
+ if len(args) > 0:
+ raise coredata.MesonException('Pkgconfig_gen takes no positional arguments.')
+ libs = kwargs.get('libraries', [])
+ if not isinstance(libs, list):
+ libs = [libs]
+ processed_libs = []
+ for l in libs:
+ if hasattr(l, 'held_object'):
+ l = l.held_object
+ if not (isinstance(l, build.SharedLibrary) or isinstance(l, build.StaticLibrary)):
+ raise coredata.MesonException('Library argument not a library object.')
+ processed_libs.append(l)
+ libs = processed_libs
+ subdirs = mesonlib.stringlistify(kwargs.get('subdirs', ['.']))
+ version = kwargs.get('version', '')
+ if not isinstance(version, str):
+ raise coredata.MesonException('Version must be a string.')
+ name = kwargs.get('name', None)
+ if not isinstance(name, str):
+ raise coredata.MesonException('Name not specified.')
+ filebase = kwargs.get('filebase', name)
+ if not isinstance(filebase, str):
+ raise coredata.MesonException('Filebase must be a string.')
+ description = kwargs.get('description', None)
+ if not isinstance(description, str):
+ raise coredata.MesonException('Description is not a string.')
+ pcfile = filebase + '.pc'
+ pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'pkgconfig')
+ self.generate_pkgconfig_file(state, libs, subdirs, name, description, version, filebase)
+ return build.Data(False, state.environment.get_scratch_dir(), [pcfile], pkgroot)
+
+def initialize():
+ return PkgConfigModule()
diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py
new file mode 100644
index 0000000..162b553
--- /dev/null
+++ b/mesonbuild/modules/qt4.py
@@ -0,0 +1,155 @@
+# 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.
+
+from .. import dependencies, mlog
+import os, subprocess
+from .. import build
+from ..coredata import MesonException
+import xml.etree.ElementTree as ET
+
+class Qt4Module():
+ def __init__(self):
+ mlog.log('Detecting Qt tools.')
+ # The binaries have different names on different
+ # distros. Joy.
+ self.moc = dependencies.ExternalProgram('moc-qt4', silent=True)
+ if not self.moc.found():
+ self.moc = dependencies.ExternalProgram('moc', silent=True)
+ self.uic = dependencies.ExternalProgram('uic-qt4', silent=True)
+ if not self.uic.found():
+ self.uic = dependencies.ExternalProgram('uic', silent=True)
+ self.rcc = dependencies.ExternalProgram('rcc-qt4', silent=True)
+ if not self.rcc.found():
+ self.rcc = dependencies.ExternalProgram('rcc', silent=True)
+ # Moc, uic and rcc write their version strings to stderr.
+ # Moc and rcc return a non-zero result when doing so.
+ # What kind of an idiot thought that was a good idea?
+ if self.moc.found():
+ mp = subprocess.Popen(self.moc.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = mp.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'Qt Meta' in stderr:
+ moc_ver = stderr
+ else:
+ raise MesonException('Moc preprocessor is not for Qt 4. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' % \
+ (' '.join(self.moc.fullpath), moc_ver.split()[-1]))
+ else:
+ mlog.log(' moc:', mlog.red('NO'))
+ if self.uic.found():
+ up = subprocess.Popen(self.uic.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = up.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'version 4.' in stderr:
+ uic_ver = stderr
+ else:
+ raise MesonException('Uic compiler is not for Qt4. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' % \
+ (' '.join(self.uic.fullpath), uic_ver.split()[-1]))
+ else:
+ mlog.log(' uic:', mlog.red('NO'))
+ if self.rcc.found():
+ rp = subprocess.Popen(self.rcc.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = rp.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'version 4.' in stderr:
+ rcc_ver = stderr
+ else:
+ raise MesonException('Rcc compiler is not for Qt 4. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'\
+ % (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
+ else:
+ mlog.log(' rcc:', mlog.red('NO'))
+
+ def parse_qrc(self, state, fname):
+ abspath = os.path.join(state.environment.source_dir, state.subdir, fname)
+ relative_part = os.path.split(fname)[0]
+ try:
+ tree = ET.parse(abspath)
+ root = tree.getroot()
+ result = []
+ for child in root[0]:
+ if child.tag != 'file':
+ mlog.log("Warning, malformed rcc file: ", os.path.join(state.subdir, fname))
+ break
+ else:
+ result.append(os.path.join(state.subdir, relative_part, child.text))
+ return result
+ except Exception:
+ return []
+
+ def preprocess(self, state, args, kwargs):
+ rcc_files = kwargs.pop('qresources', [])
+ if not isinstance(rcc_files, list):
+ rcc_files = [rcc_files]
+ ui_files = kwargs.pop('ui_files', [])
+ if not isinstance(ui_files, list):
+ ui_files = [ui_files]
+ moc_headers = kwargs.pop('moc_headers', [])
+ if not isinstance(moc_headers, list):
+ moc_headers = [moc_headers]
+ moc_sources = kwargs.pop('moc_sources', [])
+ if not isinstance(moc_sources, list):
+ moc_sources = [moc_sources]
+ srctmp = kwargs.pop('sources', [])
+ if not isinstance(srctmp, list):
+ srctmp = [srctmp]
+ sources = args[1:] + srctmp
+ if len(rcc_files) > 0:
+ rcc_kwargs = {'output' : '@BASENAME@.cpp',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ rcc_gen = build.Generator([self.rcc], rcc_kwargs)
+ rcc_output = build.GeneratedList(rcc_gen)
+ qrc_deps = []
+ for i in rcc_files:
+ qrc_deps += self.parse_qrc(state, i)
+ rcc_output.extra_depends = qrc_deps
+ [rcc_output.add_file(os.path.join(state.subdir, a)) for a in rcc_files]
+ sources.append(rcc_output)
+ if len(ui_files) > 0:
+ ui_kwargs = {'output' : 'ui_@BASENAME@.h',
+ 'arguments' : ['-o', '@OUTPUT@', '@INPUT@']}
+ ui_gen = build.Generator([self.uic], ui_kwargs)
+ ui_output = build.GeneratedList(ui_gen)
+ [ui_output.add_file(os.path.join(state.subdir, a)) for a in ui_files]
+ sources.append(ui_output)
+ if len(moc_headers) > 0:
+ moc_kwargs = {'output' : 'moc_@BASENAME@.cpp',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ moc_gen = build.Generator([self.moc], moc_kwargs)
+ moc_output = build.GeneratedList(moc_gen)
+ [moc_output.add_file(os.path.join(state.subdir, a)) for a in moc_headers]
+ sources.append(moc_output)
+ if len(moc_sources) > 0:
+ moc_kwargs = {'output' : '@BASENAME@.moc',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ moc_gen = build.Generator([self.moc], moc_kwargs)
+ moc_output = build.GeneratedList(moc_gen)
+ [moc_output.add_file(os.path.join(state.subdir, a)) for a in moc_sources]
+ sources.append(moc_output)
+ return sources
+
+def initialize():
+ mlog.log('Warning, rcc dependencies will not work properly until this upstream issue is fixed:',
+ mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460'))
+ return Qt4Module()
diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py
new file mode 100644
index 0000000..81edc76
--- /dev/null
+++ b/mesonbuild/modules/qt5.py
@@ -0,0 +1,162 @@
+# 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.
+
+from .. import dependencies, mlog
+import os, subprocess
+from .. import build
+from ..coredata import MesonException
+import xml.etree.ElementTree as ET
+
+class Qt5Module():
+
+ def __init__(self):
+ mlog.log('Detecting Qt tools.')
+ # The binaries have different names on different
+ # distros. Joy.
+ self.moc = dependencies.ExternalProgram('moc-qt5', silent=True)
+ if not self.moc.found():
+ self.moc = dependencies.ExternalProgram('moc', silent=True)
+ self.uic = dependencies.ExternalProgram('uic-qt5', silent=True)
+ if not self.uic.found():
+ self.uic = dependencies.ExternalProgram('uic', silent=True)
+ self.rcc = dependencies.ExternalProgram('rcc-qt5', silent=True)
+ if not self.rcc.found():
+ self.rcc = dependencies.ExternalProgram('rcc', silent=True)
+ # Moc, uic and rcc write their version strings to stderr.
+ # Moc and rcc return a non-zero result when doing so.
+ # What kind of an idiot thought that was a good idea?
+ if self.moc.found():
+ mp = subprocess.Popen(self.moc.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = mp.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'Qt 5' in stderr:
+ moc_ver = stderr
+ elif '5.' in stdout:
+ moc_ver = stdout
+ else:
+ raise MesonException('Moc preprocessor is not for Qt 5. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' % \
+ (' '.join(self.moc.fullpath), moc_ver.split()[-1]))
+ else:
+ mlog.log(' moc:', mlog.red('NO'))
+ if self.uic.found():
+ up = subprocess.Popen(self.uic.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = up.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'version 5.' in stderr:
+ uic_ver = stderr
+ elif '5.' in stdout:
+ uic_ver = stdout
+ else:
+ raise MesonException('Uic compiler is not for Qt 5. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' % \
+ (' '.join(self.uic.fullpath), uic_ver.split()[-1]))
+ else:
+ mlog.log(' uic:', mlog.red('NO'))
+ if self.rcc.found():
+ rp = subprocess.Popen(self.rcc.get_command() + ['-v'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = rp.communicate()
+ stdout = stdout.decode().strip()
+ stderr = stderr.decode().strip()
+ if 'version 5.' in stderr:
+ rcc_ver = stderr
+ elif '5.' in stdout:
+ rcc_ver = stdout
+ else:
+ raise MesonException('Rcc compiler is not for Qt 5. Output:\n%s\n%s' %
+ (stdout, stderr))
+ mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'\
+ % (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
+ else:
+ mlog.log(' rcc:', mlog.red('NO'))
+
+ def parse_qrc(self, state, fname):
+ abspath = os.path.join(state.environment.source_dir, state.subdir, fname)
+ relative_part = os.path.split(fname)[0]
+ try:
+ tree = ET.parse(abspath)
+ root = tree.getroot()
+ result = []
+ for child in root[0]:
+ if child.tag != 'file':
+ mlog.log("Warning, malformed rcc file: ", os.path.join(state.subdir, fname))
+ break
+ else:
+ result.append(os.path.join(state.subdir, relative_part, child.text))
+ return result
+ except Exception:
+ return []
+
+ def preprocess(self, state, args, kwargs):
+ rcc_files = kwargs.pop('qresources', [])
+ if not isinstance(rcc_files, list):
+ rcc_files = [rcc_files]
+ ui_files = kwargs.pop('ui_files', [])
+ if not isinstance(ui_files, list):
+ ui_files = [ui_files]
+ moc_headers = kwargs.pop('moc_headers', [])
+ if not isinstance(moc_headers, list):
+ moc_headers = [moc_headers]
+ moc_sources = kwargs.pop('moc_sources', [])
+ if not isinstance(moc_sources, list):
+ moc_sources = [moc_sources]
+ srctmp = kwargs.pop('sources', [])
+ if not isinstance(srctmp, list):
+ srctmp = [srctmp]
+ sources = args[1:] + srctmp
+ if len(rcc_files) > 0:
+ rcc_kwargs = {'output' : '@BASENAME@.cpp',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ rcc_gen = build.Generator([self.rcc], rcc_kwargs)
+ rcc_output = build.GeneratedList(rcc_gen)
+ qrc_deps = []
+ for i in rcc_files:
+ qrc_deps += self.parse_qrc(state, i)
+ rcc_output.extra_depends = qrc_deps
+ [rcc_output.add_file(os.path.join(state.subdir, a)) for a in rcc_files]
+ sources.append(rcc_output)
+ if len(ui_files) > 0:
+ ui_kwargs = {'output' : 'ui_@BASENAME@.h',
+ 'arguments' : ['-o', '@OUTPUT@', '@INPUT@']}
+ ui_gen = build.Generator([self.uic], ui_kwargs)
+ ui_output = build.GeneratedList(ui_gen)
+ [ui_output.add_file(os.path.join(state.subdir, a)) for a in ui_files]
+ sources.append(ui_output)
+ if len(moc_headers) > 0:
+ moc_kwargs = {'output' : 'moc_@BASENAME@.cpp',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ moc_gen = build.Generator([self.moc], moc_kwargs)
+ moc_output = build.GeneratedList(moc_gen)
+ [moc_output.add_file(os.path.join(state.subdir, a)) for a in moc_headers]
+ sources.append(moc_output)
+ if len(moc_sources) > 0:
+ moc_kwargs = {'output' : '@BASENAME@.moc',
+ 'arguments' : ['@INPUT@', '-o', '@OUTPUT@']}
+ moc_gen = build.Generator([self.moc], moc_kwargs)
+ moc_output = build.GeneratedList(moc_gen)
+ [moc_output.add_file(os.path.join(state.subdir, a)) for a in moc_sources]
+ sources.append(moc_output)
+ return sources
+
+def initialize():
+ mlog.log('Warning, rcc dependencies will not work properly until this upstream issue is fixed:',
+ mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460'))
+ return Qt5Module()
diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py
new file mode 100644
index 0000000..a2c0502
--- /dev/null
+++ b/mesonbuild/modules/rpm.py
@@ -0,0 +1,163 @@
+# 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.
+
+'''This module provides helper functions for RPM related
+functionality such as generating template RPM spec file.'''
+
+from .. import build
+from .. import compilers
+from .. import datetime
+from .. import mlog
+from .. import modules.gnome
+import os
+
+class RPMModule:
+
+ def generate_spec_template(self, state, args, kwargs):
+ compiler_deps = set()
+ for compiler in state.compilers:
+ if isinstance(compiler, compilers.GnuCCompiler):
+ compiler_deps.add('gcc')
+ elif isinstance(compiler, compilers.GnuCPPCompiler):
+ compiler_deps.add('gcc-c++')
+ elif isinstance(compiler, compilers.ValaCompiler):
+ compiler_deps.add('vala')
+ elif isinstance(compiler, compilers.GnuFortranCompiler):
+ compiler_deps.add('gcc-gfortran')
+ elif isinstance(compiler, compilers.GnuObjCCompiler):
+ compiler_deps.add('gcc-objc')
+ elif compiler == compilers.GnuObjCPPCompiler:
+ compiler_deps.add('gcc-objc++')
+ else:
+ mlog.log('RPM spec file will not created, generating not allowed for:',
+ mlog.bold(compiler.get_id()))
+ return
+ proj = state.project_name.replace(' ', '_').replace('\t', '_')
+ so_installed = False
+ devel_subpkg = False
+ files = set()
+ files_devel = set()
+ to_delete = set()
+ for target in state.targets.values():
+ if isinstance(target, build.Executable) and target.need_install:
+ files.add('%%{_bindir}/%s' % target.get_filename())
+ elif isinstance(target, build.SharedLibrary) and target.need_install:
+ files.add('%%{_libdir}/%s' % target.get_filename())
+ for alias in target.get_aliaslist():
+ if alias.endswith('.so'):
+ files_devel.add('%%{_libdir}/%s' % alias)
+ else:
+ files.add('%%{_libdir}/%s' % alias)
+ so_installed = True
+ elif isinstance(target, build.StaticLibrary) and target.need_install:
+ to_delete.add('%%{buildroot}%%{_libdir}/%s' % target.get_filename())
+ mlog.log('Warning, removing', mlog.bold(target.get_filename()),
+ 'from package because packaging static libs not recommended')
+ elif isinstance(target, modules.gnome.GirTarget) and target.should_install():
+ files_devel.add('%%{_datadir}/gir-1.0/%s' % target.get_filename()[0])
+ elif isinstance(target, modules.gnome.TypelibTarget) and target.should_install():
+ files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0])
+ for header in state.headers:
+ if len(header.get_install_subdir()) > 0:
+ files_devel.add('%%{_includedir}/%s/' % header.get_install_subdir())
+ else:
+ for hdr_src in header.get_sources():
+ files_devel.add('%%{_includedir}/%s' % hdr_src)
+ for man in state.man:
+ for man_file in man.get_sources():
+ files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file))
+ for pkgconfig in state.pkgconfig_gens:
+ files_devel.add('%%{_libdir}/pkgconfig/%s.pc' % pkgconfig.filebase)
+ if len(files_devel) > 0:
+ devel_subpkg = True
+ fn = open('%s.spec' % os.path.join(state.environment.get_build_dir(), proj), 'w+')
+ fn.write('Name: %s\n' % proj)
+ fn.write('Version: # FIXME\n')
+ fn.write('Release: 1%{?dist}\n')
+ fn.write('Summary: # FIXME\n')
+ fn.write('License: # FIXME\n')
+ fn.write('\n')
+ fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n')
+ fn.write('\n')
+ for compiler in compiler_deps:
+ fn.write('BuildRequires: %s\n' % compiler)
+ for dep in state.environment.coredata.deps:
+ fn.write('BuildRequires: pkgconfig(%s)\n' % dep)
+ for lib in state.environment.coredata.ext_libs.values():
+ fn.write('BuildRequires: %s # FIXME\n' % lib.fullpath)
+ mlog.log('Warning, replace', mlog.bold(lib.fullpath), 'with real package.',
+ 'You can use following command to find package which contains this lib:',
+ mlog.bold('dnf provides %s' % lib.fullpath))
+ for prog in state.environment.coredata.ext_progs.values():
+ if not prog.found():
+ fn.write('BuildRequires: /usr/bin/%s # FIXME\n' % prog.get_name())
+ else:
+ fn.write('BuildRequires: %s\n' % ' '.join(prog.fullpath))
+ fn.write('BuildRequires: meson\n')
+ fn.write('\n')
+ fn.write('%description\n')
+ fn.write('\n')
+ if devel_subpkg:
+ fn.write('%package devel\n')
+ fn.write('Summary: Development files for %{name}\n')
+ fn.write('Requires: %{name}%{?_isa} = %{version}-%{release}\n')
+ fn.write('\n')
+ fn.write('%description devel\n')
+ fn.write('Development files for %{name}.\n')
+ fn.write('\n')
+ fn.write('%prep\n')
+ fn.write('%autosetup\n')
+ fn.write('rm -rf rpmbuilddir && mkdir rpmbuilddir\n')
+ fn.write('\n')
+ fn.write('%build\n')
+ fn.write('pushd rpmbuilddir\n')
+ fn.write(' %meson ..\n')
+ fn.write(' ninja-build -v\n')
+ fn.write('popd\n')
+ fn.write('\n')
+ fn.write('%install\n')
+ fn.write('pushd rpmbuilddir\n')
+ fn.write(' DESTDIR=%{buildroot} ninja-build -v install\n')
+ fn.write('popd\n')
+ if len(to_delete) > 0:
+ fn.write('rm -rf %s\n' % ' '.join(to_delete))
+ fn.write('\n')
+ fn.write('%check\n')
+ fn.write('pushd rpmbuilddir\n')
+ fn.write(' ninja-build -v test\n')
+ fn.write('popd\n')
+ fn.write('\n')
+ fn.write('%files\n')
+ for f in files:
+ fn.write('%s\n' % f)
+ fn.write('\n')
+ if devel_subpkg:
+ fn.write('%files devel\n')
+ for f in files_devel:
+ fn.write('%s\n' % f)
+ fn.write('\n')
+ if so_installed:
+ fn.write('%post -p /sbin/ldconfig\n')
+ fn.write('\n')
+ fn.write('%postun -p /sbin/ldconfig\n')
+ fn.write('\n')
+ fn.write('%changelog\n')
+ fn.write('* %s meson <meson@example.com> - \n' % datetime.date.today().strftime('%a %b %d %Y'))
+ fn.write('- \n')
+ fn.write('\n')
+ fn.close()
+ mlog.log('RPM spec template written to %s.spec.\n' % proj)
+
+def initialize():
+ return RPMModule()
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
new file mode 100644
index 0000000..a785250
--- /dev/null
+++ b/mesonbuild/modules/windows.py
@@ -0,0 +1,47 @@
+# 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.
+
+from .. import mesonlib, dependencies, build
+from ..coredata import MesonException
+import os
+
+class WindowsModule:
+
+ def detect_compiler(self, compilers):
+ for c in compilers:
+ if c.language == 'c' or c.language == 'cpp':
+ return c
+ raise MesonException('Resource compilation requires a C or C++ compiler.')
+
+ def compile_resources(self, state, args, kwargs):
+ comp = self.detect_compiler(state.compilers)
+ extra_args = mesonlib.stringlistify(kwargs.get('args', []))
+ if comp.id == 'msvc':
+ rescomp = dependencies.ExternalProgram('rc', silent=True)
+ res_args = extra_args + ['/nologo', '/fo@OUTPUT@', '@INPUT@']
+ suffix = 'res'
+ else:
+ rescomp = dependencies.ExternalProgram('windres', silent=True)
+ res_args = extra_args + ['@INPUT@', '@OUTPUT@']
+ suffix = 'o'
+ res_files = mesonlib.stringlistify(args)
+ res_kwargs = {'output' : '@BASENAME@.' + suffix,
+ 'arguments': res_args}
+ res_gen = build.Generator([rescomp], res_kwargs)
+ res_output = build.GeneratedList(res_gen)
+ [res_output.add_file(os.path.join(state.subdir, a)) for a in res_files]
+ return res_output
+
+def initialize():
+ return WindowsModule()
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
new file mode 100644
index 0000000..1d569d5
--- /dev/null
+++ b/mesonbuild/mparser.py
@@ -0,0 +1,565 @@
+# Copyright 2014-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 re
+from .coredata import MesonException
+
+class ParseException(MesonException):
+ def __init__(self, text, lineno, colno):
+ super().__init__(text)
+ self.lineno = lineno
+ self.colno = colno
+
+class Token:
+ def __init__(self, tid, lineno, colno, value):
+ self.tid = tid
+ self.lineno = lineno
+ self.colno = colno
+ self.value = value
+
+ def __eq__(self, other):
+ if isinstance(other, str):
+ return self.tid == other
+ return self.tid == other.tid
+
+class Lexer:
+ def __init__(self):
+ self.keywords = {'true', 'false', 'if', 'else', 'elif',
+ 'endif', 'and', 'or', 'not', 'foreach', 'endforeach'}
+ self.token_specification = [
+ # Need to be sorted longest to shortest.
+ ('ignore', re.compile(r'[ \t]')),
+ ('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')),
+ ('number', re.compile(r'\d+')),
+ ('eol_cont', re.compile(r'\\\n')),
+ ('eol', re.compile(r'\n')),
+ ('multiline_string', re.compile(r"'''(.|\n)*?'''", re.M)),
+ ('comment', re.compile(r'\#.*')),
+ ('lparen', re.compile(r'\(')),
+ ('rparen', re.compile(r'\)')),
+ ('lbracket', re.compile(r'\[')),
+ ('rbracket', re.compile(r'\]')),
+ ('dblquote', re.compile(r'"')),
+ ('string', re.compile(r"'([^'\\]|(\\.))*'")),
+ ('comma', re.compile(r',')),
+ ('plusassign', re.compile(r'\+=')),
+ ('dot', re.compile(r'\.')),
+ ('plus', re.compile(r'\+')),
+ ('dash', re.compile(r'-')),
+ ('star', re.compile(r'\*')),
+ ('fslash', re.compile(r'/')),
+ ('colon', re.compile(r':')),
+ ('equal', re.compile(r'==')),
+ ('nequal', re.compile(r'\!=')),
+ ('assign', re.compile(r'=')),
+ ]
+
+ def lex(self, code):
+ lineno = 1
+ line_start = 0
+ loc = 0;
+ par_count = 0
+ bracket_count = 0
+ col = 0
+ while(loc < len(code)):
+ matched = False
+ value = None
+ for (tid, reg) in self.token_specification:
+ mo = reg.match(code, loc)
+ if mo:
+ curline = lineno
+ col = mo.start()-line_start
+ matched = True
+ loc = mo.end()
+ match_text = mo.group()
+ if tid == 'ignore' or tid == 'comment':
+ break
+ elif tid == 'lparen':
+ par_count += 1
+ elif tid == 'rparen':
+ par_count -= 1
+ elif tid == 'lbracket':
+ bracket_count += 1
+ elif tid == 'rbracket':
+ bracket_count -= 1
+ elif tid == 'dblquote':
+ raise ParseException('Double quotes are not supported. Use single quotes.', lineno, col)
+ elif tid == 'string':
+ value = match_text[1:-1].replace(r"\'", "'").replace(r" \\ ".strip(), r" \ ".strip())\
+ .replace("\\n", "\n")
+ elif tid == 'multiline_string':
+ tid = 'string'
+ value = match_text[3:-3]
+ lines = match_text.split('\n')
+ if len(lines) > 1:
+ lineno += len(lines) - 1
+ line_start = mo.end() - len(lines[-1])
+ elif tid == 'number':
+ value = int(match_text)
+ elif tid == 'eol' or tid == 'eol_cont':
+ lineno += 1
+ line_start = loc
+ if par_count > 0 or bracket_count > 0:
+ break
+ elif tid == 'id':
+ if match_text in self.keywords:
+ tid = match_text
+ else:
+ value = match_text
+ yield Token(tid, curline, col, value)
+ break
+ if not matched:
+ raise ParseException('lexer', lineno, col)
+
+class BooleanNode:
+ def __init__(self, token, value):
+ self.lineno = token.lineno
+ self.colno = token.colno
+ self.value = value
+ assert(isinstance(self.value, bool))
+
+class IdNode:
+ def __init__(self, token):
+ self.lineno = token.lineno
+ self.colno = token.colno
+ self.value = token.value
+ assert(isinstance(self.value, str))
+
+ def __str__(self):
+ return "Id node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
+
+class NumberNode:
+ def __init__(self, token):
+ self.lineno = token.lineno
+ self.colno = token.colno
+ self.value = token.value
+ assert(isinstance(self.value, int))
+
+class StringNode:
+ def __init__(self, token):
+ self.lineno = token.lineno
+ self.colno = token.colno
+ self.value = token.value
+ assert(isinstance(self.value, str))
+
+ def __str__(self):
+ return "String node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
+
+class ArrayNode:
+ def __init__(self, args):
+ self.lineno = args.lineno
+ self.colno = args.colno
+ self.args = args
+
+class EmptyNode:
+ def __init__(self):
+ self.lineno = 0
+ self.colno = 0
+ self.value = None
+
+class OrNode:
+ def __init__(self, lineno, colno, left, right):
+ self.lineno = lineno
+ self.colno = colno
+ self.left = left
+ self.right = right
+
+class AndNode:
+ def __init__(self, lineno, colno, left, right):
+ self.lineno = lineno
+ self.colno = colno
+ self.left = left
+ self.right = right
+
+class ComparisonNode:
+ def __init__(self, lineno, colno, ctype, left, right):
+ self.lineno = lineno
+ self.colno = colno
+ self.left = left
+ self.right = right
+ self.ctype = ctype
+
+class ArithmeticNode:
+ def __init__(self, lineno, colno, operation, left, right):
+ self.lineno = lineno
+ self.colno = colno
+ self.left = left
+ self.right = right
+ self.operation = operation
+
+class NotNode:
+ def __init__(self, lineno, colno, value):
+ self.lineno = lineno
+ self.colno = colno
+ self.value = value
+
+class CodeBlockNode:
+ def __init__(self, lineno, colno):
+ self.lineno = lineno
+ self.colno = colno
+ self.lines = []
+
+class IndexNode:
+ def __init__(self, iobject, index):
+ self.iobject = iobject
+ self.index = index
+ self.lineno = iobject.lineno
+ self.colno = iobject.colno
+
+class MethodNode:
+ def __init__(self, lineno, colno, source_object, name, args):
+ self.lineno = lineno
+ self.colno = colno
+ self.source_object = source_object
+ self.name = name
+ assert(isinstance(self.name, str))
+ self.args = args
+
+class FunctionNode:
+ def __init__(self, lineno, colno, func_name, args):
+ self.lineno = lineno
+ self.colno = colno
+ self.func_name = func_name
+ assert(isinstance(func_name, str))
+ self.args = args
+
+class AssignmentNode:
+ def __init__(self, lineno, colno, var_name, value):
+ self.lineno = lineno
+ self.colno = colno
+ self.var_name = var_name
+ assert(isinstance(var_name, str))
+ self.value = value
+
+class PlusAssignmentNode:
+ def __init__(self, lineno, colno, var_name, value):
+ self.lineno = lineno
+ self.colno = colno
+ self.var_name = var_name
+ assert(isinstance(var_name, str))
+ self.value = value
+
+class ForeachClauseNode():
+ def __init__(self, lineno, colno, varname, items, block):
+ self.lineno = lineno
+ self.colno = colno
+ self.varname = varname
+ self.items = items
+ self.block = block
+
+class IfClauseNode():
+ def __init__(self, lineno, colno):
+ self.lineno = lineno
+ self.colno = colno
+ self.ifs = []
+ self.elseblock = EmptyNode()
+
+class UMinusNode():
+ def __init__(self, lineno, colno, value):
+ self.lineno = lineno
+ self.colno = colno
+ self.value = value
+
+class IfNode():
+ def __init__(self, lineno, colno, condition, block):
+ self.lineno = lineno
+ self.colno = colno
+ self.condition = condition
+ self.block = block
+
+class ArgumentNode():
+ def __init__(self, token):
+ self.lineno = token.lineno
+ self.colno = token.colno
+ self.arguments = []
+ self.kwargs = {}
+ self.order_error = False
+
+ def prepend(self, statement):
+ if self.num_kwargs() > 0:
+ self.order_error = True
+ if not isinstance(statement, EmptyNode):
+ self.arguments = [statement] + self.arguments
+
+ def append(self, statement):
+ if self.num_kwargs() > 0:
+ self.order_error = True
+ if not isinstance(statement, EmptyNode):
+ self.arguments = self.arguments + [statement]
+
+ def set_kwarg(self, name, value):
+ self.kwargs[name] = value
+
+ def num_args(self):
+ return len(self.arguments)
+
+ def num_kwargs(self):
+ return len(self.kwargs)
+
+ def incorrect_order(self):
+ return self.order_error
+
+ def __len__(self):
+ return self.num_args() # Fixme
+
+# Recursive descent parser for Meson's definition language.
+# Very basic apart from the fact that we have many precedence
+# levels so there are not enough words to describe them all.
+# Enter numbering:
+#
+# 1 assignment
+# 2 or
+# 3 and
+# 4 comparison
+# 5 arithmetic
+# 6 negation
+# 7 funcall, method call
+# 8 parentheses
+# 9 plain token
+
+class Parser:
+ def __init__(self, code):
+ self.stream = Lexer().lex(code)
+ self.getsym()
+
+ def getsym(self):
+ try:
+ self.current = next(self.stream)
+ except StopIteration:
+ self.current = Token('eof', 0, 0, None)
+
+ def accept(self, s):
+ if self.current.tid == s:
+ self.getsym()
+ return True
+ return False
+
+ def expect(self, s):
+ if self.accept(s):
+ return True
+ raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.current.lineno, self.current.colno)
+
+ def parse(self):
+ block = self.codeblock()
+ self.expect('eof')
+ return block
+
+ def statement(self):
+ return self.e1()
+
+ def e1(self):
+ left = self.e2()
+ if self.accept('plusassign'):
+ value = self.e1()
+ if not isinstance(left, IdNode):
+ raise ParseException('Plusassignment target must be an id.', left.lineno, left.colno)
+ return PlusAssignmentNode(left.lineno, left.colno, left.value, value)
+ elif self.accept('assign'):
+ value = self.e1()
+ if not isinstance(left, IdNode):
+ raise ParseException('Assignment target must be an id.',
+ left.lineno, left.colno)
+ return AssignmentNode(left.lineno, left.colno, left.value, value)
+ return left
+
+ def e2(self):
+ left = self.e3()
+ while self.accept('or'):
+ left = OrNode(left.lineno, left.colno, left, self.e3())
+ return left
+
+ def e3(self):
+ left = self.e4()
+ while self.accept('and'):
+ left = AndNode(left.lineno, left.colno, left, self.e4())
+ return left
+
+ def e4(self):
+ left = self.e5()
+ if self.accept('equal'):
+ return ComparisonNode(left.lineno, left.colno, '==', left, self.e5())
+ if self.accept('nequal'):
+ return ComparisonNode(left.lineno, left.colno, '!=', left, self.e5())
+ return left
+
+ def e5(self):
+ return self.e5add()
+
+ def e5add(self):
+ left = self.e5sub()
+ if self.accept('plus'):
+ return ArithmeticNode(left.lineno, left.colno, 'add', left, self.e5add())
+ return left
+
+ def e5sub(self):
+ left = self.e5mul()
+ if self.accept('dash'):
+ return ArithmeticNode(left.lineno, left.colno, 'sub', left, self.e5sub())
+ return left
+
+ def e5mul(self):
+ left = self.e5div()
+ if self.accept('star'):
+ return ArithmeticNode(left.lineno, left.colno, 'mul', left, self.e5mul())
+ return left
+
+ def e5div(self):
+ left = self.e6()
+ if self.accept('fslash'):
+ return ArithmeticNode(left.lineno, left.colno, 'div', left, self.e5div())
+ return left
+
+ def e6(self):
+ if self.accept('not'):
+ return NotNode(self.current.lineno, self.current.colno, self.e7())
+ if self.accept('dash'):
+ return UMinusNode(self.current.lineno, self.current.colno, self.e7())
+ return self.e7()
+
+ def e7(self):
+ left = self.e8()
+ if self.accept('lparen'):
+ args = self.args()
+ self.expect('rparen')
+ if not isinstance(left, IdNode):
+ raise ParseException('Function call must be applied to plain id',
+ left.lineno, left.colno)
+ left = FunctionNode(left.lineno, left.colno, left.value, args)
+ go_again = True
+ while go_again:
+ go_again = False
+ if self.accept('dot'):
+ go_again = True
+ left = self.method_call(left)
+ if self.accept('lbracket'):
+ go_again = True
+ left = self.index_call(left)
+ return left
+
+ def e8(self):
+ if self.accept('lparen'):
+ e = self.statement()
+ self.expect('rparen')
+ return e
+ elif self.accept('lbracket'):
+ args = self.args()
+ self.expect('rbracket')
+ return ArrayNode(args)
+ else:
+ return self.e9()
+
+ def e9(self):
+ t = self.current
+ if self.accept('true'):
+ return BooleanNode(t, True);
+ if self.accept('false'):
+ return BooleanNode(t, False)
+ if self.accept('id'):
+ return IdNode(t)
+ if self.accept('number'):
+ return NumberNode(t)
+ if self.accept('string'):
+ return StringNode(t)
+ return EmptyNode()
+
+ def args(self):
+ s = self.statement()
+ a = ArgumentNode(s)
+
+ while not isinstance(s, EmptyNode):
+ if self.accept('comma'):
+ a.append(s)
+ elif self.accept('colon'):
+ if not isinstance(s, IdNode):
+ raise ParseException('Keyword argument must be a plain identifier.',
+ s.lineno, s.colno)
+ a.set_kwarg(s.value, self.statement())
+ if not self.accept('comma'):
+ return a
+ else:
+ a.append(s)
+ return a
+ s = self.statement()
+ return a
+
+ def method_call(self, source_object):
+ methodname = self.e9()
+ if not(isinstance(methodname, IdNode)):
+ raise ParseException('Method name must be plain id',
+ self.current.lineno, self.current.colno)
+ self.expect('lparen')
+ args = self.args()
+ self.expect('rparen')
+ method = MethodNode(methodname.lineno, methodname.colno, source_object, methodname.value, args)
+ if self.accept('dot'):
+ return self.method_call(method)
+ return method
+
+ def index_call(self, source_object):
+ index_statement = self.statement()
+ self.expect('rbracket')
+ return IndexNode(source_object, index_statement)
+
+ def foreachblock(self):
+ t = self.current
+ self.expect('id')
+ varname = t
+ self.expect('colon')
+ items = self.statement()
+ block = self.codeblock()
+ return ForeachClauseNode(varname.lineno, varname.colno, varname, items, block)
+
+ def ifblock(self):
+ condition = self.statement()
+ clause = IfClauseNode(condition.lineno, condition.colno)
+ block = self.codeblock()
+ clause.ifs.append(IfNode(clause.lineno, clause.colno, condition, block))
+ self.elseifblock(clause)
+ clause.elseblock = self.elseblock()
+ return clause
+
+ def elseifblock(self, clause):
+ while self.accept('elif'):
+ s = self.statement()
+ self.expect('eol')
+ b = self.codeblock()
+ clause.ifs.append(IfNode(s.lineno, s.colno, s, b))
+
+ def elseblock(self):
+ if self.accept('else'):
+ self.expect('eol')
+ return self.codeblock()
+
+ def line(self):
+ if self.current == 'eol':
+ return EmptyNode()
+ if self.accept('if'):
+ block = self.ifblock()
+ self.expect('endif')
+ return block
+ if self.accept('foreach'):
+ block = self.foreachblock()
+ self.expect('endforeach')
+ return block
+ return self.statement()
+
+ def codeblock(self):
+ block = CodeBlockNode(self.current.lineno, self.current.colno)
+ cond = True
+ while cond:
+ curline = self.line()
+ if not isinstance(curline, EmptyNode):
+ block.lines.append(curline)
+ cond = self.accept('eol')
+ return block
diff --git a/mesonbuild/ninjabackend.py b/mesonbuild/ninjabackend.py
new file mode 100644
index 0000000..36c5ce9
--- /dev/null
+++ b/mesonbuild/ninjabackend.py
@@ -0,0 +1,1819 @@
+# Copyright 2012-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.
+
+from . import backends
+from . import environment, mesonlib
+from . import build
+from . import mlog
+from . import dependencies
+from .mesonlib import File
+from .backends import InstallData
+from .build import InvalidArguments
+from .coredata import MesonException
+import os, sys, pickle, re
+import subprocess, shutil
+
+if mesonlib.is_windows():
+ quote_char = '"'
+ execute_wrapper = 'cmd /c'
+else:
+ quote_char = "'"
+ execute_wrapper = ''
+
+def ninja_quote(text):
+ return text.replace(' ', '$ ').replace(':', '$:')
+
+class RawFilename():
+ def __init__(self, fname):
+ self.fname = fname
+
+ def split(self, c):
+ return self.fname.split(c)
+
+ def startswith(self, s):
+ return self.fname.startswith(s)
+
+class NinjaBuildElement():
+ def __init__(self, outfilenames, rule, infilenames):
+ if isinstance(outfilenames, str):
+ self.outfilenames = [outfilenames]
+ else:
+ self.outfilenames = outfilenames
+ assert(isinstance(rule, str))
+ self.rule = rule
+ if isinstance(infilenames, str):
+ self.infilenames = [infilenames]
+ else:
+ self.infilenames = infilenames
+ self.deps = []
+ self.orderdeps = []
+ self.elems = []
+
+ def add_dep(self, dep):
+ if isinstance(dep, list):
+ self.deps += dep
+ else:
+ self.deps.append(dep)
+
+ def add_orderdep(self, dep):
+ if isinstance(dep, list):
+ self.orderdeps += dep
+ else:
+ self.orderdeps.append(dep)
+
+ def add_item(self, name, elems):
+ if isinstance(elems, str):
+ elems = [elems]
+ self.elems.append((name, elems))
+
+ def write(self, outfile):
+ line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]),\
+ self.rule,
+ ' '.join([ninja_quote(i) for i in self.infilenames]))
+ if len(self.deps) > 0:
+ line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps])
+ if len(self.orderdeps) > 0:
+ line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps])
+ line += '\n'
+ # This is the only way I could find to make this work on all
+ # platforms including Windows command shell. Slash is a dir separator
+ # on Windows, too, so all characters are unambiguous and, more importantly,
+ # do not require quoting.
+ line = line.replace('\\', '/')
+ outfile.write(line)
+
+ for e in self.elems:
+ (name, elems) = e
+ should_quote = True
+ if name == 'DEPFILE' or name == 'DESC' or name == 'pool':
+ should_quote = False
+ line = ' %s = ' % name
+ q_templ = quote_char + "%s" + quote_char
+ noq_templ = "%s"
+ newelems = []
+ for i in elems:
+ if not should_quote or i == '&&': # Hackety hack hack
+ templ = noq_templ
+ else:
+ templ = q_templ
+ i = i.replace('\\', '\\\\')
+ if quote_char == '"':
+ i = i.replace('"', '\\"')
+ newelems.append(templ % ninja_quote(i))
+ line += ' '.join(newelems)
+ line += '\n'
+ outfile.write(line)
+ outfile.write('\n')
+
+class NinjaBackend(backends.Backend):
+
+ def __init__(self, build):
+ super().__init__(build)
+ self.source_suffix_in_objs = True
+ self.ninja_filename = 'build.ninja'
+ self.fortran_deps = {}
+ self.all_outputs = {}
+
+ def check_outputs(self, elem):
+ for n in elem.outfilenames:
+ if n in self.all_outputs:
+ raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n)
+ self.all_outputs[n] = True
+
+ def detect_vs_dep_prefix(self, outfile, tempfilename):
+ '''VS writes its dependency in a locale dependent format.
+ Detect the search prefix to use.'''
+ if shutil.which('cl') is None:
+ return outfile
+ outfile.close()
+ open(os.path.join(self.environment.get_scratch_dir(), 'incdetect.c'),
+ 'w').write('''#include<stdio.h>
+int dummy;
+''')
+
+ pc = subprocess.Popen(['cl', '/showIncludes', '/c', 'incdetect.c'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=self.environment.get_scratch_dir())
+
+ (stdo, _) = pc.communicate()
+
+ for line in stdo.split(b'\r\n'):
+ if line.endswith(b'stdio.h'):
+ matchstr = b':'.join(line.split(b':')[0:2]) + b':'
+ binfile = open(tempfilename, 'ab')
+ binfile.write(b'msvc_deps_prefix = ' + matchstr + b'\r\n')
+ binfile.close()
+ return open(tempfilename, 'a')
+ raise MesonException('Could not determine vs dep dependency prefix string.')
+
+ def generate(self, interp):
+ self.interpreter = interp
+ outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename)
+ tempfilename = outfilename + '~'
+ outfile = open(tempfilename, 'w')
+ outfile.write('# This is the build file for project "%s"\n' % self.build.get_project())
+ outfile.write('# It is autogenerated by the Meson build system.\n')
+ outfile.write('# Do not edit by hand.\n\n')
+ outfile.write('ninja_required_version = 1.5.1\n\n')
+ outfile = self.detect_vs_dep_prefix(outfile, tempfilename)
+ self.generate_rules(outfile)
+ self.generate_phony(outfile)
+ outfile.write('# Build rules for targets\n\n')
+ [self.generate_target(t, outfile) for t in self.build.get_targets().values()]
+ if len(self.build.pot) > 0:
+ outfile.write('# Build rules for localisation.\n\n')
+ self.generate_po(outfile)
+ outfile.write('# Test rules\n\n')
+ self.generate_tests(outfile)
+ outfile.write('# Install rules\n\n')
+ self.generate_install(outfile)
+ if self.environment.coredata.get_builtin_option('coverage'):
+ outfile.write('# Coverage rules\n\n')
+ self.generate_coverage_rules(outfile)
+ outfile.write('# Suffix\n\n')
+ self.generate_ending(outfile)
+ # Only ovewrite the old build file after the new one has been
+ # fully created.
+ outfile.close()
+ os.replace(tempfilename, outfilename)
+ self.generate_compdb()
+
+ # http://clang.llvm.org/docs/JSONCompilationDatabase.html
+ def generate_compdb(self):
+ ninja_exe = environment.detect_ninja()
+ builddir = self.environment.get_build_dir()
+ jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir)
+ open(os.path.join(builddir, 'compile_commands.json'), 'wb').write(jsondb)
+
+ # Get all generated headers. Any source file might need them so
+ # we need to add an order dependency to them.
+ def get_generated_headers(self, target):
+ header_deps = []
+ for gensource in target.get_generated_sources():
+ if isinstance(gensource, build.CustomTarget):
+ continue
+ for src in gensource.get_outfilelist():
+ if self.environment.is_header(src):
+ header_deps.append(os.path.join(self.get_target_private_dir(target), src))
+ for dep in target.link_targets:
+ if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
+ header_deps += self.get_generated_headers(dep)
+ return header_deps
+
+ def generate_target(self, target, outfile):
+ if isinstance(target, build.CustomTarget):
+ self.generate_custom_target(target, outfile)
+ if isinstance(target, build.RunTarget):
+ self.generate_run_target(target, outfile)
+ name = target.get_id()
+ gen_src_deps = []
+ if name in self.processed_targets:
+ return
+ if isinstance(target, build.Jar):
+ self.generate_jar_target(target, outfile)
+ return
+ if 'rust' in self.environment.coredata.compilers.keys() and self.has_rust(target):
+ self.generate_rust_target(target, outfile)
+ return
+ if 'cs' in self.environment.coredata.compilers.keys() and self.has_cs(target):
+ self.generate_cs_target(target, outfile)
+ return
+ if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target):
+ gen_src_deps += self.generate_vala_compile(target, outfile)
+ if 'swift' in self.environment.coredata.compilers.keys() and self.has_swift(target):
+ self.generate_swift_target(target, outfile)
+ return
+ self.scan_fortran_module_outputs(target)
+ # The following deals with C/C++ compilation.
+ (gen_src, gen_other_deps) = self.process_dep_gens(outfile, target)
+ gen_src_deps += gen_src
+ self.process_target_dependencies(target, outfile)
+ self.generate_custom_generator_rules(target, outfile)
+ outname = self.get_target_filename(target)
+ obj_list = []
+ use_pch = self.environment.coredata.get_builtin_option('use_pch')
+ is_unity = self.environment.coredata.get_builtin_option('unity')
+ if use_pch and target.has_pch():
+ pch_objects = self.generate_pch(target, outfile)
+ else:
+ pch_objects = []
+ header_deps = gen_other_deps
+ unity_src = []
+ unity_deps = [] # Generated sources that must be built before compiling a Unity target.
+ header_deps += self.get_generated_headers(target)
+ for gensource in target.get_generated_sources():
+ if isinstance(gensource, build.CustomTarget):
+ for src in gensource.output:
+ src = os.path.join(self.get_target_dir(gensource), src)
+ if self.environment.is_source(src) and not self.environment.is_header(src):
+ if is_unity:
+ unity_deps.append(os.path.join(self.environment.get_build_dir(), RawFilename(src)))
+ else:
+ obj_list.append(self.generate_single_compile(target, outfile, RawFilename(src), True,
+ header_deps))
+ elif self.environment.is_object(src):
+ obj_list.append(src)
+ elif self.environment.is_library(src):
+ pass
+ else:
+ # Assume anything not specifically a source file is a header. This is because
+ # people generate files with weird suffixes (.inc, .fh) that they then include
+ # in their source files.
+ header_deps.append(RawFilename(src))
+ else:
+ for src in gensource.get_outfilelist():
+ if self.environment.is_object(src):
+ obj_list.append(os.path.join(self.get_target_private_dir(target), src))
+ elif not self.environment.is_header(src):
+ if is_unity:
+ if self.has_dir_part(src):
+ rel_src = src
+ else:
+ rel_src = os.path.join(self.get_target_private_dir(target), src)
+ unity_deps.append(rel_src)
+ abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
+ unity_src.append(abs_src)
+ else:
+ obj_list.append(self.generate_single_compile(target, outfile, src, True,
+ header_deps=header_deps))
+ src_list = []
+ for src in gen_src_deps:
+ src_list.append(src)
+ if is_unity:
+ unity_src.append(os.path.join(self.environment.get_build_dir(), src))
+ header_deps.append(src)
+ else:
+ # Generated targets are ordered deps because the must exist
+ # before the sources compiling them are used. After the first
+ # compile we get precise dependency info from dep files.
+ # This should work in all cases. If it does not, then just
+ # move them from orderdeps to proper deps.
+ obj_list.append(self.generate_single_compile(target, outfile, src, True, [], header_deps))
+ for src in target.get_sources():
+ if src.endswith('.vala'):
+ continue
+ if not self.environment.is_header(src):
+ src_list.append(src)
+ if is_unity:
+ abs_src = os.path.join(self.environment.get_build_dir(),
+ src.rel_to_builddir(self.build_to_src))
+ unity_src.append(abs_src)
+ else:
+ obj_list.append(self.generate_single_compile(target, outfile, src, False, [], header_deps))
+ obj_list += self.flatten_object_list(target)
+ if is_unity:
+ for src in self.generate_unity_files(target, unity_src):
+ obj_list.append(self.generate_single_compile(target, outfile, src, True, unity_deps + header_deps))
+ linker = self.determine_linker(target, src_list)
+ elem = self.generate_link(target, outfile, outname, obj_list, linker, pch_objects)
+ self.generate_shlib_aliases(target, self.get_target_dir(target))
+ elem.write(outfile)
+ self.processed_targets[name] = True
+
+ def process_target_dependencies(self, target, outfile):
+ for t in target.get_dependencies():
+ tname = t.get_basename() + t.type_suffix()
+ if not tname in self.processed_targets:
+ self.generate_target(t, outfile)
+
+ def generate_custom_target(self, target, outfile):
+ (srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
+ deps = []
+ for i in target.get_dependencies():
+ # FIXME, should not grab element at zero but rather expand all.
+ if isinstance(i, list):
+ i = i[0]
+ fname = i.get_filename()
+ if isinstance(fname, list):
+ fname = fname[0]
+ deps.append(os.path.join(self.get_target_dir(i), fname))
+ if target.build_always:
+ deps.append('PHONY')
+ elem = NinjaBuildElement(ofilenames, 'CUSTOM_COMMAND', srcs)
+ for i in target.depend_files:
+ if isinstance(i, mesonlib.File):
+ deps.append(i.rel_to_builddir(self.build_to_src))
+ else:
+ deps.append(os.path.join(self.build_to_src, i))
+ elem.add_dep(deps)
+ for d in target.extra_depends:
+ tmp = d.get_filename()
+ if not isinstance(tmp, list):
+ tmp = [tmp]
+ for fname in tmp:
+ elem.add_dep(os.path.join(self.get_target_dir(d), fname))
+
+ elem.add_item('COMMAND', cmd)
+ elem.add_item('description', 'Generating %s with a custom command.' % target.name)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ self.processed_targets[target.name + target.type_suffix()] = True
+
+ def generate_run_target(self, target, outfile):
+ runnerscript = os.path.join(self.environment.get_script_dir(), 'commandrunner.py')
+ deps = []
+ arg_strings = []
+ for i in target.args:
+ if isinstance(i, str):
+ arg_strings.append(i)
+ elif isinstance(i, (build.BuildTarget, build.CustomTarget)):
+ relfname = self.get_target_filename(i)
+ deps.append(relfname)
+ arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
+ else:
+ mlog.debug(str(i))
+ raise MesonException('Unreachable code in generate_run_target.')
+ elem = NinjaBuildElement(target.name, 'CUSTOM_COMMAND', deps)
+ cmd = [sys.executable, runnerscript, self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir]
+ texe = target.command
+ try:
+ texe = texe.held_object
+ except AttributeError:
+ pass
+ if isinstance(texe, build.Executable):
+ abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))
+ deps.append(self.get_target_filename(texe))
+ if self.environment.is_cross_build() \
+ and self.environment.cross_info.config['binaries'].get('exe_wrapper', None) is not None:
+ cmd += [self.environment.cross_info.config['binaries']['exe_wrapper']]
+ cmd.append(abs_exe)
+ else:
+ cmd.append(target.command)
+ cmd += arg_strings
+ elem.add_item('COMMAND', cmd)
+ elem.add_item('description', 'Running external command %s.' % target.name)
+ elem.add_item('pool', 'console')
+ elem.write(outfile)
+ self.check_outputs(elem)
+ self.processed_targets[target.name + target.type_suffix()] = True
+
+ def generate_po(self, outfile):
+ for p in self.build.pot:
+ (packagename, languages, subdir) = p
+ input_file = os.path.join(subdir, 'POTFILES')
+ elem = NinjaBuildElement('pot', 'GEN_POT', [])
+ elem.add_item('PACKAGENAME', packagename)
+ elem.add_item('OUTFILE', packagename + '.pot')
+ elem.add_item('FILELIST', os.path.join(self.environment.get_source_dir(), input_file))
+ elem.add_item('OUTDIR', os.path.join(self.environment.get_source_dir(), subdir))
+ elem.write(outfile)
+ self.check_outputs(elem)
+ for l in languages:
+ infile = os.path.join(self.environment.get_source_dir(), subdir, l + '.po')
+ outfilename = os.path.join(subdir, l + '.gmo')
+ lelem = NinjaBuildElement(outfilename, 'GEN_GMO', infile)
+ lelem.add_item('INFILE', infile)
+ lelem.add_item('OUTFILE', outfilename)
+ lelem.write(outfile)
+ self.check_outputs(lelem)
+
+ def generate_coverage_rules(self, outfile):
+ (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
+ added_rule = False
+ if gcovr_exe:
+ added_rule = True
+ elem = NinjaBuildElement('coverage-xml', 'CUSTOM_COMMAND', '')
+ elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_build_dir(),\
+ '-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')])
+ elem.add_item('DESC', 'Generating XML coverage report.')
+ elem.write(outfile)
+ elem = NinjaBuildElement('coverage-text', 'CUSTOM_COMMAND', '')
+ elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_build_dir(),\
+ '-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')])
+ elem.add_item('DESC', 'Generating text coverage report.')
+ elem.write(outfile)
+ self.check_outputs(elem)
+ if lcov_exe and genhtml_exe:
+ added_rule = True
+ phony_elem = NinjaBuildElement('coverage-html', 'phony', 'coveragereport/index.html')
+ phony_elem.write(outfile)
+
+ elem = NinjaBuildElement('coveragereport/index.html', 'CUSTOM_COMMAND', '')
+ command = [lcov_exe, '--directory', self.environment.get_build_dir(),\
+ '--capture', '--output-file', 'coverage.info', '--no-checksum',\
+ '&&', genhtml_exe, '--prefix', self.environment.get_build_dir(),\
+ '--output-directory', self.environment.get_log_dir(), '--title', 'Code coverage',\
+ '--legend', '--show-details', 'coverage.info']
+ elem.add_item('COMMAND', command)
+ elem.add_item('DESC', 'Generating HTML coverage report.')
+ self.check_outputs(elem)
+ elem.write(outfile)
+ if not added_rule:
+ mlog.log(mlog.red('Warning:'), 'coverage requested but neither gcovr nor lcov/genhtml found.')
+
+ def generate_install(self, outfile):
+ script_root = self.environment.get_script_dir()
+ install_script = os.path.join(script_root, 'meson_install.py')
+ install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat')
+ depfixer = [sys.executable, self.environment.get_build_command(), '--internal', 'depfixer']
+ d = InstallData(self.environment.get_source_dir(),
+ self.environment.get_build_dir(),
+ self.environment.get_prefix(), depfixer)
+ elem = NinjaBuildElement('install', 'CUSTOM_COMMAND', 'PHONY')
+ elem.add_dep('all')
+ elem.add_item('DESC', 'Installing files.')
+ elem.add_item('COMMAND', [sys.executable, self.environment.get_build_command(), '--internal', 'install', install_data_file])
+ elem.add_item('pool', 'console')
+ self.generate_depmf_install(d)
+ self.generate_target_install(d)
+ self.generate_header_install(d)
+ self.generate_man_install(d)
+ self.generate_data_install(d)
+ self.generate_po_install(d, elem)
+ self.generate_custom_install_script(d)
+ self.generate_subdir_install(d)
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ ofile = open(install_data_file, 'wb')
+ pickle.dump(d, ofile)
+
+ def generate_po_install(self, d, elem):
+ for p in self.build.pot:
+ (package_name, languages, subdir) = p
+ # FIXME: assumes only one po package per source
+ d.po_package_name = package_name
+ for lang in languages:
+ rel_src = os.path.join(subdir, lang + '.gmo')
+ src_file = os.path.join(self.environment.get_build_dir(), rel_src)
+ d.po.append((src_file, self.environment.coredata.get_builtin_option('localedir'), lang))
+ elem.add_dep(rel_src)
+
+ def generate_target_install(self, d):
+ libdir = self.environment.get_libdir()
+ bindir = self.environment.get_bindir()
+
+ should_strip = self.environment.coredata.get_builtin_option('strip')
+ for t in self.build.get_targets().values():
+ if t.should_install():
+ outdir = t.get_custom_install_dir()
+ if outdir is None:
+ if isinstance(t, build.Executable):
+ outdir = bindir
+ else:
+ outdir = libdir
+ i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\
+ should_strip, t.install_rpath]
+ d.targets.append(i)
+
+ def generate_custom_install_script(self, d):
+ d.install_scripts = self.build.install_scripts
+
+ def generate_header_install(self, d):
+ incroot = self.environment.get_includedir()
+ headers = self.build.get_headers()
+
+ for h in headers:
+ outdir = h.get_custom_install_dir()
+ if outdir is None:
+ outdir = os.path.join(incroot, h.get_install_subdir())
+ for f in h.get_sources():
+ abspath = os.path.join(self.environment.get_source_dir(), h.get_source_subdir(), f)
+ i = [abspath, outdir]
+ d.headers.append(i)
+
+ def generate_man_install(self, d):
+ manroot = self.environment.get_mandir()
+ man = self.build.get_man()
+ for m in man:
+ for f in m.get_sources():
+ num = f.split('.')[-1]
+ subdir = m.get_custom_install_dir()
+ if subdir is None:
+ subdir = os.path.join(manroot, 'man' + num)
+ srcabs = os.path.join(self.environment.get_source_dir(), m.get_source_subdir(), f)
+ dstabs = os.path.join(subdir, f + '.gz')
+ i = [srcabs, dstabs]
+ d.man.append(i)
+
+ def generate_data_install(self, d):
+ data = self.build.get_data()
+ for de in data:
+ assert(isinstance(de, build.Data))
+ subdir = de.install_dir
+ for f in de.sources:
+ if de.in_sourcetree:
+ srcprefix = self.environment.get_source_dir()
+ else:
+ srcprefix = self.environment.get_build_dir()
+ srcabs = os.path.join(srcprefix, de.source_subdir, f)
+ dstabs = os.path.join(subdir, f)
+ i = [srcabs, dstabs]
+ d.data.append(i)
+
+ def generate_subdir_install(self, d):
+ for sd in self.build.get_install_subdirs():
+ src_dir = os.path.join(self.environment.get_source_dir(), sd.source_subdir, sd.installable_subdir)
+ dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir)
+ d.install_subdirs.append([src_dir, dst_dir])
+
+ def write_test_suite_targets(self, cmd, outfile):
+ suites = {}
+ for t in self.build.get_tests():
+ for s in t.suite:
+ suites[s] = True
+ suites = list(suites.keys())
+ suites.sort()
+ for s in suites:
+ if s == '':
+ visible_name = 'for top level tests'
+ else:
+ visible_name = s
+ elem = NinjaBuildElement('test-' + s, 'CUSTOM_COMMAND', ['all', 'PHONY'])
+ elem.add_item('COMMAND', cmd + ['--suite=' + s])
+ elem.add_item('DESC', 'Running test suite %s.' % visible_name)
+ elem.add_item('pool', 'console')
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ def generate_tests(self, outfile):
+ self.serialise_tests()
+ valgrind = environment.find_valgrind()
+ script_root = self.environment.get_script_dir()
+ test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
+ cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'test', test_data]
+ elem = NinjaBuildElement('test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
+ elem.add_item('COMMAND', cmd)
+ elem.add_item('DESC', 'Running all tests.')
+ elem.add_item('pool', 'console')
+ elem.write(outfile)
+ self.check_outputs(elem)
+ self.write_test_suite_targets(cmd, outfile)
+
+ if valgrind:
+ velem = NinjaBuildElement('test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY'])
+ velem.add_item('COMMAND', cmd + ['--wrapper=' + valgrind])
+ velem.add_item('DESC', 'Running test suite under Valgrind.')
+ velem.add_item('pool', 'console')
+ velem.write(outfile)
+ self.check_outputs(velem)
+
+ # And then benchmarks.
+ benchmark_script = os.path.join(script_root, 'meson_benchmark.py')
+ benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat')
+ cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data]
+ elem = NinjaBuildElement('benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
+ elem.add_item('COMMAND', cmd)
+ elem.add_item('DESC', 'Running benchmark suite.')
+ elem.add_item('pool', 'console')
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ def generate_rules(self, outfile):
+ outfile.write('# Rules for compiling.\n\n')
+ self.generate_compile_rules(outfile)
+ outfile.write('# Rules for linking.\n\n')
+ if self.environment.is_cross_build():
+ self.generate_static_link_rules(True, outfile)
+ self.generate_static_link_rules(False, outfile)
+ self.generate_dynamic_link_rules(outfile)
+ outfile.write('# Other rules\n\n')
+ outfile.write('rule CUSTOM_COMMAND\n')
+ outfile.write(' command = $COMMAND\n')
+ outfile.write(' description = $DESC\n')
+ outfile.write(' restat = 1\n\n')
+ outfile.write('rule REGENERATE_BUILD\n')
+ c = (quote_char + ninja_quote(sys.executable) + quote_char,
+ quote_char + ninja_quote(self.environment.get_build_command()) + quote_char,
+ '--internal',
+ 'regenerate',
+ quote_char + ninja_quote(self.environment.get_source_dir()) + quote_char,
+ quote_char + ninja_quote(self.environment.get_build_dir()) + quote_char)
+ outfile.write(" command = %s %s %s %s %s %s --backend ninja\n" % c)
+ outfile.write(' description = Regenerating build files\n')
+ outfile.write(' generator = 1\n\n')
+ if len(self.build.pot) > 0:
+ self.generate_gettext_rules(outfile)
+ outfile.write('\n')
+
+ def generate_gettext_rules(self, outfile):
+ rule = 'rule GEN_POT\n'
+ command = " command = xgettext --package-name=$PACKAGENAME -p $OUTDIR -f $FILELIST -D '%s' -k_ -o $OUTFILE\n" % \
+ self.environment.get_source_dir()
+ desc = " description = Creating pot file for package $PACKAGENAME.\n"
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(desc)
+ outfile.write('\n')
+ rule = 'rule GEN_GMO\n'
+ command = ' command = msgfmt $INFILE -o $OUTFILE\n'
+ desc = ' description = Generating gmo file $OUTFILE\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(desc)
+ outfile.write('\n')
+
+ def generate_phony(self, outfile):
+ outfile.write('# Phony build target, always out of date\n')
+ outfile.write('build PHONY: phony\n')
+ outfile.write('\n')
+
+ def generate_jar_target(self, target, outfile):
+ fname = target.get_filename()
+ subdir = target.get_subdir()
+ outname_rel = os.path.join(self.get_target_dir(target), fname)
+ src_list = target.get_sources()
+ class_list = []
+ compiler = self.get_compiler_for_source(src_list[0])
+ assert(compiler.get_language() == 'java')
+ c = 'c'
+ m = ''
+ e = ''
+ f = 'f'
+ main_class = target.get_main_class()
+ if main_class != '':
+ e = 'e'
+ for src in src_list:
+ plain_class_path = self.generate_single_java_compile(src, target, compiler, outfile)
+ class_list.append(plain_class_path)
+ class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list]
+ jar_rule = 'java_LINKER'
+ commands = [c+m+e+f]
+ if e != '':
+ commands.append(main_class)
+ commands.append(self.get_target_filename(target))
+ for cls in class_list:
+ commands += ['-C', self.get_target_private_dir(target), cls]
+ elem = NinjaBuildElement(outname_rel, jar_rule, [])
+ elem.add_dep(class_dep_list)
+ elem.add_item('ARGS', commands)
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ def generate_cs_resource_tasks(self, target, outfile):
+ args = []
+ deps = []
+ for r in target.resources:
+ rel_sourcefile = os.path.join(self.build_to_src, target.subdir, r)
+ if r.endswith('.resources'):
+ a = '-resource:' + rel_sourcefile
+ elif r.endswith('.txt') or r.endswith('.resx'):
+ ofilebase = os.path.splitext(os.path.basename(r))[0] + '.resources'
+ ofilename = os.path.join(self.get_target_private_dir(target), ofilebase)
+ elem = NinjaBuildElement(ofilename, "CUSTOM_COMMAND", rel_sourcefile)
+ elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename])
+ elem.add_item('DESC', 'Compiling resource %s.' % rel_sourcefile)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ deps.append(ofilename)
+ a = '-resource:' + ofilename
+ else:
+ raise InvalidArguments('Unknown resource file %s.' % r)
+ args.append(a)
+ return (args, deps)
+
+ def generate_cs_target(self, target, outfile):
+ buildtype = self.environment.coredata.get_builtin_option('buildtype')
+ fname = target.get_filename()
+ outname_rel = os.path.join(self.get_target_dir(target), fname)
+ src_list = target.get_sources()
+ compiler = self.get_compiler_for_source(src_list[0])
+ assert(compiler.get_language() == 'cs')
+ rel_srcs = [s.rel_to_builddir(self.build_to_src) for s in src_list]
+ deps = []
+ commands = target.extra_args.get('cs', [])
+ commands += compiler.get_buildtype_args(buildtype)
+ if isinstance(target, build.Executable):
+ commands.append('-target:exe')
+ elif isinstance(target, build.SharedLibrary):
+ commands.append('-target:library')
+ else:
+ raise MesonException('Unknown C# target type.')
+ (resource_args, resource_deps) = self.generate_cs_resource_tasks(target, outfile)
+ commands += resource_args
+ deps += resource_deps
+ commands += compiler.get_output_args(outname_rel)
+ for l in target.link_targets:
+ lname = os.path.join(self.get_target_dir(l), l.get_filename())
+ commands += compiler.get_link_args(lname)
+ deps.append(lname)
+ if '-g' in commands:
+ outputs = [outname_rel, outname_rel + '.mdb']
+ else:
+ outputs = [outname_rel]
+ elem = NinjaBuildElement(outputs, 'cs_COMPILER', rel_srcs)
+ elem.add_dep(deps)
+ elem.add_item('ARGS', commands)
+ self.check_outputs(elem)
+ elem.write(outfile)
+
+ def generate_single_java_compile(self, src, target, compiler, outfile):
+ args = []
+ args += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ args += compiler.get_output_args(self.get_target_private_dir(target))
+ for i in target.include_dirs:
+ for idir in i.get_incdirs():
+ args += ['-sourcepath', os.path.join(self.build_to_src, i.curdir, idir)]
+ rel_src = src.rel_to_builddir(self.build_to_src)
+ plain_class_path = src.fname[:-4] + 'class'
+ rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path)
+ element = NinjaBuildElement(rel_obj, compiler.get_language() + '_COMPILER', rel_src)
+ element.add_item('ARGS', args)
+ element.write(outfile)
+ self.check_outputs(element)
+ return plain_class_path
+
+ def generate_java_link(self, outfile):
+ rule = 'rule java_LINKER\n'
+ command = ' command = jar $ARGS\n'
+ description = ' description = Creating jar $out.\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_fastvapi_compile(self, target, valac, outfile):
+ fastvapis = {}
+ for s in target.get_sources():
+ if not s.endswith('.vala'):
+ continue
+ vapibase = os.path.basename(s.fname)[:-4] + 'vapi'
+ rel_vapi = os.path.join(self.get_target_private_dir(target), vapibase)
+ args = ['--fast-vapi=' + rel_vapi]
+ rel_s = s.rel_to_builddir(self.build_to_src)
+ element = NinjaBuildElement(rel_vapi, valac.get_language() + '_COMPILER', rel_s)
+ element.add_item('ARGS', args)
+ element.write(outfile)
+ self.check_outputs(element)
+ fastvapis[s] = (vapibase, rel_vapi)
+ return fastvapis
+
+ def split_vala_sources(self, sources):
+ src = []
+ vapi_src = []
+ for s in sources:
+ if s.endswith('.vapi'):
+ vapi_src.append(s)
+ else:
+ src.append(s)
+ return (src, vapi_src)
+
+ def generate_vala_compile(self, target, outfile):
+ """Vala is compiled into C. Set up all necessary build steps here."""
+ valac = self.environment.coredata.compilers['vala']
+ fast_vapis = self.generate_fastvapi_compile(target, valac, outfile)
+ generated_c = []
+ (src, vapi_src) = self.split_vala_sources(target.get_sources())
+ vapi_src = [x.rel_to_builddir(self.build_to_src) for x in vapi_src]
+ extra_dep_files = []
+ for s in src:
+ if not s.endswith('.vala'):
+ continue
+ args = ['-d', self.get_target_private_dir(target)]
+ sc = os.path.basename(s.fname)[:-4] + 'c'
+ args += ['-C']
+ vapi_order_deps = []
+ for (sourcefile, vapi_info) in fast_vapis.items():
+ if sourcefile == s:
+ continue
+ (vapibase, rel_vapi) = vapi_info
+ args += ['--use-fast-vapi=' + rel_vapi]
+ vapi_order_deps.append(rel_vapi)
+ relsc = os.path.join(self.get_target_private_dir(target), sc)
+ rel_s = s.rel_to_builddir(self.build_to_src)
+ args += ['--deps', relsc + '.d']
+ if self.environment.coredata.get_builtin_option('werror'):
+ args += valac.get_werror_args()
+ for d in target.external_deps:
+ if isinstance(d, dependencies.PkgConfigDependency):
+ if d.name == 'glib-2.0' and d.version_requirement is not None \
+ and d.version_requirement.startswith(('>=', '==')):
+ args += ['--target-glib', d.version_requirement[2:]]
+ args += ['--pkg', d.name]
+ args += vapi_src
+ extra_args = []
+
+ for a in target.extra_args.get('vala', []):
+ if isinstance(a, File):
+ relname = a.rel_to_builddir(self.build_to_src)
+ extra_dep_files.append(relname)
+ extra_args.append(relname)
+ else:
+ extra_args.append(a)
+ args += extra_args
+ generated_c += [relsc]
+ element = NinjaBuildElement(relsc, valac.get_language() + '_COMPILER', rel_s)
+ element.add_item('ARGS', args)
+ element.add_orderdep(vapi_order_deps)
+ element.add_dep(extra_dep_files)
+ element.write(outfile)
+ self.check_outputs(element)
+ return generated_c
+
+ def generate_rust_target(self, target, outfile):
+ rustc = self.environment.coredata.compilers['rust']
+ relsrc = []
+ for i in target.get_sources():
+ if not rustc.can_compile(i):
+ raise InvalidArguments('Rust target %s contains a non-rust source file.' % target.get_basename())
+ relsrc.append(i.rel_to_builddir(self.build_to_src))
+ target_name = os.path.join(target.subdir, target.get_filename())
+ args = ['--crate-type']
+ if isinstance(target, build.Executable):
+ cratetype = 'bin'
+ elif isinstance(target, build.SharedLibrary):
+ cratetype = 'rlib'
+ elif isinstance(target, build.StaticLibrary):
+ cratetype = 'rlib'
+ else:
+ raise InvalidArguments('Unknown target type for rustc.')
+ args.append(cratetype)
+ args += rustc.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
+ depfile = target.name + '.d'
+ args += ['--out-dir', target.subdir]
+ args += ['--emit', 'dep-info', '--emit', 'link']
+ orderdeps = [os.path.join(t.subdir, t.get_filename()) for t in target.link_targets]
+ linkdirs = {}
+ for d in target.link_targets:
+ linkdirs[d.subdir] = True
+ for d in linkdirs.keys():
+ if d == '':
+ d = '.'
+ args += ['-L', d]
+ element = NinjaBuildElement(target_name, 'rust_COMPILER', relsrc)
+ if len(orderdeps) > 0:
+ element.add_orderdep(orderdeps)
+ element.add_item('ARGS', args)
+ element.add_item('targetdep', depfile)
+ element.add_item('cratetype', cratetype)
+ element.write(outfile)
+ self.check_outputs(element)
+
+ def swift_module_file_name(self, target):
+ return os.path.join(self.get_target_private_dir(target),
+ self.target_swift_modulename(target) + '.swiftmodule')
+
+ def target_swift_modulename(self, target):
+ return target.name
+
+ def is_swift_target(self, target):
+ for s in target.sources:
+ if s.endswith('swift'):
+ return True
+ return False
+
+ def determine_swift_dep_modules(self, target):
+ result = []
+ for l in target.link_targets:
+ if self.is_swift_target(l):
+ result.append(self.swift_module_file_name(l))
+ return result
+
+ def determine_swift_dep_dirs(self, target):
+ result = []
+ for l in target.link_targets:
+ result.append(self.get_target_private_dir_abs(l))
+ return result
+
+ def get_swift_link_deps(self, target):
+ result = []
+ for l in target.link_targets:
+ result.append(self.get_target_filename(l))
+ return result
+
+ def split_swift_generated_sources(self, target):
+ all_srcs = []
+ for genlist in target.get_generated_sources():
+ if isinstance(genlist, build.CustomTarget):
+ for ifile in genlist.get_filename():
+ rel = os.path.join(self.get_target_dir(genlist), ifile)
+ all_srcs.append(rel)
+ else:
+ for ifile in genlist.get_outfilelist():
+ rel = os.path.join(self.get_target_private_dir(target), ifile)
+ all_srcs.append(rel)
+ srcs = []
+ others = []
+ for i in all_srcs:
+ if i.endswith('.swift'):
+ srcs.append(i)
+ else:
+ others.append(i)
+ return (srcs, others)
+
+ def generate_swift_target(self, target, outfile):
+ module_name = self.target_swift_modulename(target)
+ swiftc = self.environment.coredata.compilers['swift']
+ abssrc = []
+ abs_headers = []
+ header_imports = []
+ for i in target.get_sources():
+ if swiftc.can_compile(i):
+ relsrc = i.rel_to_builddir(self.build_to_src)
+ abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), relsrc))
+ abssrc.append(abss)
+ elif self.environment.is_header(i):
+ relh = i.rel_to_builddir(self.build_to_src)
+ absh = os.path.normpath(os.path.join(self.environment.get_build_dir(), relh))
+ abs_headers.append(absh)
+ header_imports += swiftc.get_header_import_args(absh)
+ else:
+ raise InvalidArguments('Swift target %s contains a non-swift source file.' % target.get_basename())
+ os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
+ compile_args = swiftc.get_compile_only_args()
+ compile_args += swiftc.get_module_args(module_name)
+ link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)))
+ rundir = self.get_target_private_dir(target)
+ out_module_name = self.swift_module_file_name(target)
+ in_module_files = self.determine_swift_dep_modules(target)
+ abs_module_dirs = self.determine_swift_dep_dirs(target)
+ module_includes = []
+ for x in abs_module_dirs:
+ module_includes += swiftc.get_include_args(x)
+ link_deps = self.get_swift_link_deps(target)
+ abs_link_deps = [os.path.join(self.environment.get_build_dir(), x) for x in link_deps]
+ (rel_generated, _) = self.split_swift_generated_sources(target)
+ abs_generated = [os.path.join(self.environment.get_build_dir(), x) for x in rel_generated]
+ # We need absolute paths because swiftc needs to be invoked in a subdir
+ # and this is the easiest way about it.
+ objects = [] # Relative to swift invocation dir
+ rel_objects = [] # Relative to build.ninja
+ for i in abssrc + abs_generated:
+ base = os.path.split(i)[1]
+ oname = os.path.splitext(base)[0] + '.o'
+ objects.append(oname)
+ rel_objects.append(os.path.join(self.get_target_private_dir(target), oname))
+
+ # Swiftc does not seem to be able to emit objects and module files in one go.
+ elem = NinjaBuildElement(rel_objects,
+ 'swift_COMPILER',
+ abssrc)
+ elem.add_dep(in_module_files + rel_generated)
+ elem.add_dep(abs_headers)
+ elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes)
+ elem.add_item('RUNDIR', rundir)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ elem = NinjaBuildElement(out_module_name,
+ 'swift_COMPILER',
+ abssrc)
+ elem.add_dep(in_module_files + rel_generated)
+ elem.add_item('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args())
+ elem.add_item('RUNDIR', rundir)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ if isinstance(target, build.StaticLibrary):
+ elem = self.generate_link(target, outfile, self.get_target_filename(target),
+ rel_objects, self.build.static_linker)
+ elem.write(outfile)
+ elif isinstance(target, build.Executable):
+ elem = NinjaBuildElement(self.get_target_filename(target), 'swift_COMPILER', [])
+ elem.add_dep(rel_objects)
+ elem.add_dep(link_deps)
+ elem.add_item('ARGS', link_args + swiftc.get_std_exe_link_args() + objects + abs_link_deps)
+ elem.add_item('RUNDIR', rundir)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ else:
+ raise MesonException('Swift supports only executable and static library targets.')
+
+ def generate_static_link_rules(self, is_cross, outfile):
+ if self.build.has_language('java'):
+ if not is_cross:
+ self.generate_java_link(outfile)
+ if is_cross:
+ if self.environment.cross_info.need_cross_compiler():
+ static_linker = self.build.static_cross_linker
+ else:
+ static_linker = self.build.static_linker
+ crstr = '_CROSS'
+ else:
+ static_linker = self.build.static_linker
+ crstr = ''
+ if static_linker is None:
+ return
+ rule = 'rule STATIC%s_LINKER\n' % crstr
+ if mesonlib.is_windows():
+ command_templ = ''' command = %s @$out.rsp
+ rspfile = $out.rsp
+ rspfile_content = $LINK_ARGS %s $in
+'''
+ else:
+ command_templ = ' command = %s $LINK_ARGS %s $in\n'
+ command = command_templ %\
+ (' '.join(static_linker.get_exelist()),
+ ' '.join(static_linker.get_output_args('$out')))
+ description = ' description = Static linking library $out\n\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+
+ def generate_dynamic_link_rules(self, outfile):
+ ctypes = [(self.build.compilers, False)]
+ if self.environment.is_cross_build():
+ if self.environment.cross_info.need_cross_compiler():
+ ctypes.append((self.build.cross_compilers, True))
+ else:
+ # Native compiler masquerades as the cross compiler.
+ ctypes.append((self.build.compilers, True))
+ else:
+ ctypes.append((self.build.cross_compilers, True))
+ for (complist, is_cross) in ctypes:
+ for compiler in complist:
+ langname = compiler.get_language()
+ if langname == 'java' or langname == 'vala' or\
+ langname == 'rust' or langname == 'cs':
+ continue
+ crstr = ''
+ cross_args = []
+ if is_cross:
+ crstr = '_CROSS'
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_link_args']
+ except KeyError:
+ pass
+ rule = 'rule %s%s_LINKER\n' % (langname, crstr)
+ if mesonlib.is_windows():
+ command_template = ''' command = %s @$out.rsp
+ rspfile = $out.rsp
+ rspfile_content = %s $ARGS %s $in $LINK_ARGS $aliasing
+'''
+ else:
+ command_template = ' command = %s %s $ARGS %s $in $LINK_ARGS $aliasing\n'
+ command = command_template % \
+ (' '.join(compiler.get_linker_exelist()),\
+ ' '.join(cross_args),\
+ ' '.join(compiler.get_linker_output_args('$out')))
+ description = ' description = Linking target $out'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write('\n')
+ scriptdir = self.environment.get_script_dir()
+ outfile.write('\n')
+ symrule = 'rule SHSYM\n'
+ symcmd = ' command = "%s" "%s" %s %s %s %s $CROSS\n' % (ninja_quote(sys.executable),
+ self.environment.get_build_command(),
+ '--internal',
+ 'symbolextractor',
+ '$in',
+ '$out')
+ synstat = ' restat = 1\n'
+ syndesc = ' description = Generating symbol file $out.\n'
+ outfile.write(symrule)
+ outfile.write(symcmd)
+ outfile.write(synstat)
+ outfile.write(syndesc)
+ outfile.write('\n')
+
+ def generate_java_compile_rule(self, compiler, outfile):
+ rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
+ command = ' command = %s $ARGS $in\n' % invoc
+ description = ' description = Compiling Java object $in.\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_cs_compile_rule(self, compiler, outfile):
+ rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
+ command = ' command = %s $ARGS $in\n' % invoc
+ description = ' description = Compiling cs target $out.\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_vala_compile_rules(self, compiler, outfile):
+ rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
+ command = ' command = %s $ARGS $in\n' % invoc
+ description = ' description = Compiling Vala source $in.\n'
+ restat = ' restat = 1\n' # ValaC does this always to take advantage of it.
+ depfile = ' depfile = $out.d\n'
+ depstyle = ' deps = gcc\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write(restat)
+ outfile.write(depfile)
+ outfile.write(depstyle)
+ outfile.write('\n')
+
+ def generate_rust_compile_rules(self, compiler, outfile):
+ rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
+ command = ' command = %s $ARGS $in\n' % invoc
+ description = ' description = Compiling Rust source $in.\n'
+ depfile = ' depfile = $targetdep\n'
+
+ depstyle = ' deps = gcc\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write(depfile)
+ outfile.write(depstyle)
+ outfile.write('\n')
+
+ def generate_swift_compile_rules(self, compiler, outfile):
+ rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ full_exe = [sys.executable,
+ os.path.join(self.environment.get_script_dir(), 'dirchanger.py'),
+ '$RUNDIR'] + compiler.get_exelist()
+ invoc = ' '.join([ninja_quote(i) for i in full_exe])
+ command = ' command = %s $ARGS $in\n' % invoc
+ description = ' description = Compiling Swift source $in.\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_fortran_dep_hack(self, outfile):
+ if mesonlib.is_windows():
+ cmd = 'cmd /C ""'
+ else:
+ cmd = 'true'
+ template = '''# Workaround for these issues:
+# https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485
+rule FORTRAN_DEP_HACK
+ command = %s
+ description = Dep hack
+ restat = 1
+
+'''
+ outfile.write(template % cmd)
+
+ def generate_compile_rule_for(self, langname, compiler, qstr, is_cross, outfile):
+ if langname == 'java':
+ if not is_cross:
+ self.generate_java_compile_rule(compiler, outfile)
+ return
+ if langname == 'cs':
+ if not is_cross:
+ self.generate_cs_compile_rule(compiler, outfile)
+ return
+ if langname == 'vala':
+ if not is_cross:
+ self.generate_vala_compile_rules(compiler, outfile)
+ return
+ if langname == 'rust':
+ if not is_cross:
+ self.generate_rust_compile_rules(compiler, outfile)
+ return
+ if langname == 'swift':
+ if not is_cross:
+ self.generate_swift_compile_rules(compiler, outfile)
+ return
+ if langname == 'fortran':
+ self.generate_fortran_dep_hack(outfile)
+ if is_cross:
+ crstr = '_CROSS'
+ else:
+ crstr = ''
+ rule = 'rule %s%s_COMPILER\n' % (langname, crstr)
+ depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
+ quoted_depargs = []
+ for d in depargs:
+ if d != '$out' and d != '$in':
+ d = qstr % d
+ quoted_depargs.append(d)
+ cross_args = []
+ if is_cross:
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_args']
+ except KeyError:
+ pass
+ if mesonlib.is_windows():
+ command_template = ''' command = %s @$out.rsp
+ rspfile = $out.rsp
+ rspfile_content = %s $ARGS %s %s %s $in
+'''
+ else:
+ command_template = ' command = %s %s $ARGS %s %s %s $in\n'
+ command = command_template % \
+ (' '.join(compiler.get_exelist()),\
+ ' '.join(cross_args),
+ ' '.join(quoted_depargs),\
+ ' '.join(compiler.get_output_args('$out')),\
+ ' '.join(compiler.get_compile_only_args()))
+ description = ' description = Compiling %s object $out\n' % langname
+ if compiler.get_id() == 'msvc':
+ deps = ' deps = msvc\n'
+ else:
+ deps = ' deps = gcc\n'
+ deps += ' depfile = $DEPFILE\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(deps)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_pch_rule_for(self, langname, compiler, qstr, is_cross, outfile):
+ if langname != 'c' and langname != 'cpp':
+ return
+ if is_cross:
+ crstr = '_CROSS'
+ else:
+ crstr = ''
+ rule = 'rule %s%s_PCH\n' % (langname, crstr)
+ depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
+ cross_args = []
+ if is_cross:
+ try:
+ cross_args = self.environment.cross_info.config['properties'][langname + '_args']
+ except KeyError:
+ pass
+
+ quoted_depargs = []
+ for d in depargs:
+ if d != '$out' and d != '$in':
+ d = qstr % d
+ quoted_depargs.append(d)
+ if compiler.get_id() == 'msvc':
+ output = ''
+ else:
+ output = ' '.join(compiler.get_output_args('$out'))
+ command = " command = %s %s $ARGS %s %s %s $in\n" % \
+ (' '.join(compiler.get_exelist()),\
+ ' '.join(cross_args),\
+ ' '.join(quoted_depargs),\
+ output,\
+ ' '.join(compiler.get_compile_only_args()))
+ description = ' description = Precompiling header %s\n' % '$in'
+ if compiler.get_id() == 'msvc':
+ deps = ' deps = msvc\n'
+ else:
+ deps = ' deps = gcc\n'
+ deps += ' depfile = $DEPFILE\n'
+ outfile.write(rule)
+ outfile.write(command)
+ outfile.write(deps)
+ outfile.write(description)
+ outfile.write('\n')
+
+ def generate_compile_rules(self, outfile):
+ qstr = quote_char + "%s" + quote_char
+ for compiler in self.build.compilers:
+ langname = compiler.get_language()
+ self.generate_compile_rule_for(langname, compiler, qstr, False, outfile)
+ self.generate_pch_rule_for(langname, compiler, qstr, False, outfile)
+ if self.environment.is_cross_build():
+ # In case we are going a target-only build, make the native compilers
+ # masquerade as cross compilers.
+ if self.environment.cross_info.need_cross_compiler():
+ cclist = self.build.cross_compilers
+ else:
+ cclist = self.build.compilers
+ for compiler in cclist:
+ langname = compiler.get_language()
+ self.generate_compile_rule_for(langname, compiler, qstr, True, outfile)
+ self.generate_pch_rule_for(langname, compiler, qstr, True, outfile)
+ outfile.write('\n')
+
+ def replace_outputs(self, args, private_dir, output_list):
+ newargs = []
+ regex = re.compile('@OUTPUT(\d+)@')
+ for arg in args:
+ m = regex.search(arg)
+ while m is not None:
+ index = int(m.group(1))
+ src = '@OUTPUT%d@' % index
+ arg = arg.replace(src, os.path.join(private_dir, output_list[index]))
+ m = regex.search(arg)
+ newargs.append(arg)
+ return newargs
+
+ def generate_custom_generator_rules(self, target, outfile):
+ for genlist in target.get_generated_sources():
+ if isinstance(genlist, build.CustomTarget):
+ continue # Customtarget has already written its output rules
+ generator = genlist.get_generator()
+ exe = generator.get_exe()
+ exe_arr = self.exe_object_to_cmd_array(exe)
+ infilelist = genlist.get_infilelist()
+ outfilelist = genlist.get_outfilelist()
+ base_args = generator.get_arglist()
+ extra_dependencies = [os.path.join(self.build_to_src, i) for i in genlist.extra_depends]
+ for i in range(len(infilelist)):
+ if len(generator.outputs) == 1:
+ sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
+ else:
+ sole_output = ''
+ curfile = infilelist[i]
+ infilename = os.path.join(self.build_to_src, curfile)
+ outfiles = genlist.get_outputs_for(curfile)
+ outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles]
+ args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\
+ for x in base_args]
+ args = self.replace_outputs(args, self.get_target_private_dir(target), outfilelist)
+ # We have consumed output files, so drop them from the list of remaining outputs.
+ if sole_output == '':
+ outfilelist = outfilelist[len(generator.outputs):]
+ relout = self.get_target_private_dir(target)
+ args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout)
+ for x in args]
+ final_args = []
+ for a in args:
+ if a == '@EXTRA_ARGS@':
+ final_args += genlist.get_extra_args()
+ else:
+ final_args.append(a)
+ cmdlist = exe_arr + final_args
+ elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename)
+ if len(extra_dependencies) > 0:
+ elem.add_dep(extra_dependencies)
+ elem.add_item('DESC', 'Generating $out')
+ if isinstance(exe, build.BuildTarget):
+ elem.add_dep(self.get_target_filename(exe))
+ elem.add_item('COMMAND', cmdlist)
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ def scan_fortran_module_outputs(self, target):
+ compiler = None
+ for c in self.build.compilers:
+ if c.get_language() == 'fortran':
+ compiler = c
+ break
+ if compiler is None:
+ self.fortran_deps[target.get_basename()] = {}
+ return
+ modre = re.compile(r"\s*module\s+(\w+)", re.IGNORECASE)
+ module_files = {}
+ for s in target.get_sources():
+ # FIXME, does not work for generated Fortran sources,
+ # but those are really rare. I hope.
+ if not compiler.can_compile(s):
+ continue
+ for line in open(os.path.join(self.environment.get_source_dir(), s.subdir, s.fname)):
+ modmatch = modre.match(line)
+ if modmatch is not None:
+ modname = modmatch.group(1)
+ if modname.lower() == 'procedure': # MODULE PROCEDURE construct
+ continue
+ if modname in module_files:
+ raise InvalidArguments('Namespace collision: module %s defined in two files %s and %s.' %
+ (modname, module_files[modname], s))
+ module_files[modname] = s
+ self.fortran_deps[target.get_basename()] = module_files
+
+ def get_fortran_deps(self, compiler, src, target):
+ mod_files = []
+ usere = re.compile(r"\s*use\s+(\w+)", re.IGNORECASE)
+ dirname = self.get_target_private_dir(target)
+ tdeps= self.fortran_deps[target.get_basename()]
+ for line in open(src):
+ usematch = usere.match(line)
+ if usematch is not None:
+ usename = usematch.group(1)
+ if usename not in tdeps:
+ # The module is not provided by any source file. This is due to
+ # a) missing file/typo/etc
+ # b) using a module provided by the compiler, such as OpenMP
+ # There's no easy way to tell which is which (that I know of)
+ # so just ignore this and go on. Ideally we would print a
+ # warning message to the user but this is a common occurrance,
+ # which would lead to lots of distracting noise.
+ continue
+ mod_source_file = tdeps[usename]
+ # Check if a source uses a module it exports itself.
+ # Potential bug if multiple targets have a file with
+ # the same name.
+ if mod_source_file.fname == os.path.split(src)[1]:
+ continue
+ mod_name = compiler.module_name_to_filename(usematch.group(1))
+ mod_files.append(os.path.join(dirname, mod_name))
+ return mod_files
+
+ def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]):
+ if(isinstance(src, str) and src.endswith('.h')):
+ raise RuntimeError('Fug')
+ if isinstance(src, RawFilename) and src.fname.endswith('.h'):
+ raise RuntimeError('Fug')
+ extra_orderdeps = []
+ compiler = self.get_compiler_for_source(src)
+ commands = self.generate_basic_compiler_args(target, compiler)
+ commands += compiler.get_include_args(self.get_target_private_dir(target), False)
+ curdir = target.get_subdir()
+ tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir))
+ commands += compiler.get_include_args(tmppath, False)
+ if curdir == '':
+ curdir = '.'
+ commands += compiler.get_include_args(curdir, False)
+ for d in target.external_deps:
+ if d.need_threads():
+ commands += compiler.thread_flags()
+ break
+ if isinstance(src, RawFilename):
+ rel_src = src.fname
+ elif is_generated:
+ if self.has_dir_part(src):
+ rel_src = src
+ else:
+ rel_src = os.path.join(self.get_target_private_dir(target), src)
+ abs_src = os.path.join(self.environment.get_source_dir(), rel_src)
+ else:
+ if isinstance(src, File):
+ rel_src = src.rel_to_builddir(self.build_to_src)
+ else:
+ raise build.InvalidArguments('Invalid source type.')
+ abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
+ if isinstance(src, RawFilename):
+ src_filename = src.fname
+ elif isinstance(src, File):
+ src_filename = src.fname
+ elif os.path.isabs(src):
+ src_filename = os.path.basename(src)
+ else:
+ src_filename = src
+ obj_basename = src_filename.replace('/', '_').replace('\\', '_')
+ rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename)
+ rel_obj += '.' + self.environment.get_object_suffix()
+ dep_file = compiler.depfile_for_object(rel_obj)
+ if self.environment.coredata.get_builtin_option('use_pch'):
+ pchlist = target.get_pch(compiler.language)
+ else:
+ pchlist = []
+ if len(pchlist) == 0:
+ pch_dep = []
+ else:
+ arr = []
+ i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0]))
+ arr.append(i)
+ pch_dep = arr
+ for i in target.get_include_dirs():
+ basedir = i.get_curdir()
+ for d in i.get_incdirs():
+ expdir = os.path.join(basedir, d)
+ srctreedir = os.path.join(self.build_to_src, expdir)
+ bargs = compiler.get_include_args(expdir, i.is_system)
+ sargs = compiler.get_include_args(srctreedir, i.is_system)
+ commands += bargs
+ commands += sargs
+ for d in i.get_extra_build_dirs():
+ commands += compiler.get_include_args(d, i.is_system)
+ custom_target_include_dirs = []
+ for i in target.generated:
+ if isinstance(i, build.CustomTarget):
+ idir = self.get_target_dir(i)
+ if idir not in custom_target_include_dirs:
+ custom_target_include_dirs.append(idir)
+ for i in custom_target_include_dirs:
+ commands+= compiler.get_include_args(i, False)
+ if self.environment.coredata.get_builtin_option('use_pch'):
+ commands += self.get_pch_include_args(compiler, target)
+ crstr = ''
+ if target.is_cross:
+ crstr = '_CROSS'
+ compiler_name = '%s%s_COMPILER' % (compiler.get_language(), crstr)
+ extra_deps = []
+ if compiler.get_language() == 'fortran':
+ extra_deps += self.get_fortran_deps(compiler, abs_src, target)
+ # Dependency hack. Remove once multiple outputs in Ninja is fixed:
+ # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
+ for modname, srcfile in self.fortran_deps[target.get_basename()].items():
+ modfile = os.path.join(self.get_target_private_dir(target),
+ compiler.module_name_to_filename(modname))
+ if srcfile == src:
+ depelem = NinjaBuildElement(modfile, 'FORTRAN_DEP_HACK', rel_obj)
+ depelem.write(outfile)
+ self.check_outputs(depelem)
+ commands += compiler.get_module_outdir_args(self.get_target_private_dir(target))
+
+ element = NinjaBuildElement(rel_obj, compiler_name, rel_src)
+ for d in header_deps:
+ if isinstance(d, RawFilename):
+ d = d.fname
+ elif not self.has_dir_part(d):
+ d = os.path.join(self.get_target_private_dir(target), d)
+ element.add_dep(d)
+ for d in extra_deps:
+ element.add_dep(d)
+ for d in order_deps:
+ if isinstance(d, RawFilename):
+ d = d.fname
+ elif not self.has_dir_part(d):
+ d = os.path.join(self.get_target_private_dir(target), d)
+ element.add_orderdep(d)
+ element.add_orderdep(pch_dep)
+ element.add_orderdep(extra_orderdeps)
+ for i in self.get_fortran_orderdeps(target, compiler):
+ element.add_orderdep(i)
+ element.add_item('DEPFILE', dep_file)
+ element.add_item('ARGS', commands)
+ element.write(outfile)
+ self.check_outputs(element)
+ return rel_obj
+
+ def has_dir_part(self, fname):
+ return '/' in fname or '\\' in fname
+
+ # Fortran is a bit weird (again). When you link against a library, just compiling a source file
+ # requires the mod files that are output when single files are built. To do this right we would need to
+ # scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so
+ # instead just have an ordered dependendy on the library. This ensures all required mod files are created.
+ # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
+ # produce incorrect dep files but such is life.
+ def get_fortran_orderdeps(self, target, compiler):
+ if compiler.language != 'fortran':
+ return []
+ return [os.path.join(self.get_target_dir(lt), lt.get_filename()) for lt in target.link_targets]
+
+ def generate_msvc_pch_command(self, target, compiler, pch):
+ if len(pch) != 2:
+ raise RuntimeError('MSVC requires one header and one source to produce precompiled headers.')
+ header = pch[0]
+ source = pch[1]
+ pchname = compiler.get_pch_name(header)
+ dst = os.path.join(self.get_target_private_dir(target), pchname)
+
+ commands = []
+ commands += self.generate_basic_compiler_args(target, compiler)
+ just_name = os.path.split(header)[1]
+ (objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
+ commands += pch_args
+ dep = dst + '.' + compiler.get_depfile_suffix()
+ return (commands, dep, dst, [objname])
+
+ def generate_gcc_pch_command(self, target, compiler, pch):
+ commands = []
+ commands += self.generate_basic_compiler_args(target, compiler)
+ dst = os.path.join(self.get_target_private_dir(target),
+ os.path.split(pch)[-1] + '.' + compiler.get_pch_suffix())
+ dep = dst + '.' + compiler.get_depfile_suffix()
+ return (commands, dep, dst, []) # Gcc does not create an object file during pch generation.
+
+ def generate_pch(self, target, outfile):
+ cstr = ''
+ pch_objects = []
+ if target.is_cross:
+ cstr = '_CROSS'
+ for lang in ['c', 'cpp']:
+ pch = target.get_pch(lang)
+ if len(pch) == 0:
+ continue
+ if '/' not in pch[0] or '/' not in pch[-1]:
+ raise build.InvalidArguments('Precompiled header of "%s" must not be in the same directory as source, please put it in a subdirectory.' % target.get_basename())
+ compiler = self.get_compiler_for_lang(lang)
+ if compiler.id == 'msvc':
+ src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1])
+ (commands, dep, dst, objs) = self.generate_msvc_pch_command(target, compiler, pch)
+ extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
+ else:
+ src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
+ (commands, dep, dst, objs) = self.generate_gcc_pch_command(target, compiler, pch[0])
+ extradep = None
+ pch_objects += objs
+ rulename = compiler.get_language() + cstr + '_PCH'
+ elem = NinjaBuildElement(dst, rulename, src)
+ if extradep is not None:
+ elem.add_dep(extradep)
+ elem.add_item('ARGS', commands)
+ elem.add_item('DEPFILE', dep)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ return pch_objects
+
+ def generate_shsym(self, outfile, target):
+ target_name = self.get_target_filename(target)
+ targetdir = self.get_target_private_dir(target)
+ symname = os.path.join(targetdir, target_name + '.symbols')
+ elem = NinjaBuildElement(symname, 'SHSYM', target_name)
+ if self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler():
+ elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system'])
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]):
+ if isinstance(target, build.StaticLibrary):
+ linker_base = 'STATIC'
+ else:
+ linker_base = linker.get_language() # Fixme.
+ if isinstance(target, build.SharedLibrary):
+ self.generate_shsym(outfile, target)
+ crstr = ''
+ if target.is_cross:
+ crstr = '_CROSS'
+ linker_rule = linker_base + crstr + '_LINKER'
+ abspath = os.path.join(self.environment.get_build_dir(), target.subdir)
+ commands = []
+ commands += linker.get_linker_always_args()
+ commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype'))
+ commands += linker.get_option_link_args(self.environment.coredata.compiler_options)
+ if not(isinstance(target, build.StaticLibrary)):
+ commands += self.environment.coredata.external_link_args[linker.get_language()]
+ if isinstance(target, build.Executable):
+ commands += linker.get_std_exe_link_args()
+ elif isinstance(target, build.SharedLibrary):
+ commands += linker.get_std_shared_lib_link_args()
+ commands += linker.get_pic_args()
+ if hasattr(target, 'soversion'):
+ soversion = target.soversion
+ else:
+ soversion = None
+ commands += linker.get_soname_args(target.name, abspath, soversion)
+ elif isinstance(target, build.StaticLibrary):
+ commands += linker.get_std_link_args()
+ else:
+ raise RuntimeError('Unknown build target type.')
+ # Link arguments of static libraries are not put in the command line of
+ # the library. They are instead appended to the command line where
+ # the static library is used.
+ if linker_base == 'STATIC':
+ dependencies = []
+ else:
+ dependencies = target.get_dependencies()
+ commands += self.build_target_link_arguments(linker, dependencies)
+ for d in target.external_deps:
+ if d.need_threads():
+ commands += linker.thread_link_flags()
+ if not isinstance(target, build.StaticLibrary):
+ commands += target.link_args
+ # External deps must be last because target link libraries may depend on them.
+ if not(isinstance(target, build.StaticLibrary)):
+ for dep in target.get_external_deps():
+ commands += dep.get_link_args()
+ for d in target.get_dependencies():
+ if isinstance(d, build.StaticLibrary):
+ for dep in d.get_external_deps():
+ commands += dep.get_link_args()
+ commands += linker.build_rpath_args(self.environment.get_build_dir(),\
+ self.determine_rpath_dirs(target), target.install_rpath)
+ if self.environment.coredata.get_builtin_option('coverage'):
+ commands += linker.get_coverage_link_args()
+ custom_target_libraries = self.get_custom_target_provided_libraries(target)
+ commands += extra_args
+ commands += custom_target_libraries
+ commands = linker.unixtype_flags_to_native(commands)
+ dep_targets = [self.get_dependency_filename(t) for t in dependencies]
+ dep_targets += [os.path.join(self.environment.source_dir,
+ target.subdir, t) for t in target.link_depends]
+ elem = NinjaBuildElement(outname, linker_rule, obj_list)
+ elem.add_dep(dep_targets + custom_target_libraries)
+ elem.add_item('LINK_ARGS', commands)
+ self.check_outputs(elem)
+ return elem
+
+ def get_custom_target_provided_libraries(self, target):
+ libs = []
+ for t in target.get_generated_sources():
+ if not isinstance(t, build.CustomTarget):
+ continue
+ for f in t.output:
+ if self.environment.is_library(f):
+ libs.append(os.path.join(self.get_target_dir(t), f))
+ return libs
+
+ def determine_rpath_dirs(self, target):
+ link_deps = target.get_all_link_deps()
+ result = []
+ for ld in link_deps:
+ prospective = self.get_target_dir(ld)
+ if not prospective in result:
+ result.append(prospective)
+ return result
+
+ def get_dependency_filename(self, t):
+ if isinstance(t, build.SharedLibrary):
+ return os.path.join(self.get_target_private_dir(t), self.get_target_filename(t) + '.symbols')
+ return self.get_target_filename(t)
+
+ def generate_shlib_aliases(self, target, outdir):
+ basename = target.get_filename()
+ aliases = target.get_aliaslist()
+ if not mesonlib.is_windows():
+ for alias in aliases:
+ aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
+ try:
+ os.remove(aliasfile)
+ except Exception:
+ pass
+ os.symlink(basename, aliasfile)
+ else:
+ mlog.debug("Library versioning disabled because host does not support symlinks.")
+
+ def generate_gcov_clean(self, outfile):
+ gcno_elem = NinjaBuildElement('clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
+ script_root = self.environment.get_script_dir()
+ clean_script = os.path.join(script_root, 'delwithsuffix.py')
+ gcno_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcno'])
+ gcno_elem.add_item('description', 'Deleting gcno files')
+ gcno_elem.write(outfile)
+ self.check_outputs(gcno_elem)
+
+ gcda_elem = NinjaBuildElement('clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
+ script_root = self.environment.get_script_dir()
+ clean_script = os.path.join(script_root, 'delwithsuffix.py')
+ gcda_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcda'])
+ gcda_elem.add_item('description', 'Deleting gcda files')
+ gcda_elem.write(outfile)
+ self.check_outputs(gcda_elem)
+
+ def is_compilable_file(self, filename):
+ if filename.endswith('.cpp') or\
+ filename.endswith('.c') or\
+ filename.endswith('.cxx') or\
+ filename.endswith('.cc') or\
+ filename.endswith('.C'):
+ return True
+ return False
+
+ def process_dep_gens(self, outfile, target):
+ src_deps = []
+ other_deps = []
+ for rule in self.dep_rules.values():
+ srcs = target.get_original_kwargs().get(rule.src_keyword, [])
+ if isinstance(srcs, str):
+ srcs = [srcs]
+ for src in srcs:
+ plainname = os.path.split(src)[1]
+ basename = plainname.split('.')[0]
+ outname = rule.name_templ.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname)
+ outfilename = os.path.join(self.get_target_private_dir(target), outname)
+ infilename = os.path.join(self.build_to_src, target.get_source_subdir(), src)
+ elem = NinjaBuildElement(outfilename, rule.name, infilename)
+ elem.write(outfile)
+ self.check_outputs(elem)
+ if self.is_compilable_file(outfilename):
+ src_deps.append(outfilename)
+ else:
+ other_deps.append(outfilename)
+ return (src_deps, other_deps)
+
+ def generate_ending(self, outfile):
+ targetlist = [self.get_target_filename(t) for t in self.build.get_targets().values()\
+ if not isinstance(t, build.RunTarget)]
+
+ elem = NinjaBuildElement('all', 'phony', targetlist)
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ default = 'default all\n\n'
+ outfile.write(default)
+
+ ninja_command = environment.detect_ninja()
+ if ninja_command is None:
+ raise MesonException('Could not detect ninja command')
+ elem = NinjaBuildElement('clean', 'CUSTOM_COMMAND', 'PHONY')
+ elem.add_item('COMMAND', [ninja_command, '-t', 'clean'])
+ elem.add_item('description', 'Cleaning')
+ if self.environment.coredata.get_builtin_option('coverage'):
+ self.generate_gcov_clean(outfile)
+ elem.add_dep('clean-gcda')
+ elem.add_dep('clean-gcno')
+ elem.write(outfile)
+ self.check_outputs(elem)
+
+ deps = self.get_regen_filelist()
+ elem = NinjaBuildElement('build.ninja', 'REGENERATE_BUILD', deps)
+ elem.add_item('pool', 'console')
+ elem.write(outfile)
+
+ elem = NinjaBuildElement(deps, 'phony', '')
+ elem.write(outfile)
+ self.check_outputs(elem)
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
new file mode 100644
index 0000000..f0c93ae
--- /dev/null
+++ b/mesonbuild/optinterpreter.py
@@ -0,0 +1,148 @@
+# 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.
+
+from . import mparser
+from . import coredata, mesonlib
+import os, re
+
+forbidden_option_names = coredata.builtin_options
+forbidden_prefixes = {'c_': True,
+ 'cpp_': True,
+ 'rust_': True,
+ 'fortran_': True,
+ 'objc_': True,
+ 'objcpp_': True,
+ 'vala_': True,
+ 'csharp_': True
+ }
+
+def is_invalid_name(name):
+ if name in forbidden_option_names:
+ return True
+ if name in forbidden_prefixes:
+ return True
+ return False
+
+class OptionException(coredata.MesonException):
+ pass
+
+optname_regex = re.compile('[^a-zA-Z0-9_-]')
+
+def StringParser(name, description, kwargs):
+ return coredata.UserStringOption(name, description,
+ kwargs.get('value', ''), kwargs.get('choices', []))
+
+def BooleanParser(name, description, kwargs):
+ return coredata.UserBooleanOption(name, description, kwargs.get('value', True))
+
+def ComboParser(name, description, kwargs):
+ if 'choices' not in kwargs:
+ raise OptionException('Combo option missing "choices" keyword.')
+ choices = kwargs['choices']
+ if not isinstance(choices, list):
+ raise OptionException('Combo choices must be an array.')
+ for i in choices:
+ if not isinstance(i, str):
+ raise OptionException('Combo choice elements must be strings.')
+ return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0]))
+
+option_types = {'string' : StringParser,
+ 'boolean' : BooleanParser,
+ 'combo' : ComboParser,
+ }
+
+class OptionInterpreter:
+ def __init__(self, subproject, command_line_options):
+ self.options = {}
+ self.subproject = subproject
+ self.cmd_line_options = {}
+ for o in command_line_options:
+ (key, value) = o.split('=', 1)
+ self.cmd_line_options[key] = value
+
+ def process(self, option_file):
+ try:
+ ast = mparser.Parser(open(option_file, 'r').read()).parse()
+ except coredata.MesonException as me:
+ me.file = option_file
+ raise me
+ if not isinstance(ast, mparser.CodeBlockNode):
+ e = OptionException('Option file is malformed.')
+ e.lineno = ast.lineno()
+ raise e
+ for cur in ast.lines:
+ try:
+ self.evaluate_statement(cur)
+ except Exception as e:
+ e.lineno = cur.lineno
+ e.colno = cur.colno
+ e.file = os.path.join('meson_options.txt')
+ raise e
+
+ def reduce_single(self, arg):
+ if isinstance(arg, str):
+ return arg
+ elif isinstance(arg, mparser.StringNode):
+ return arg.value
+ elif isinstance(arg, mparser.BooleanNode):
+ return arg.value
+ elif isinstance(arg, mparser.ArrayNode):
+ return [self.reduce_single(curarg) for curarg in arg.args.arguments]
+ elif isinstance(arg, mparser.NumberNode):
+ return arg.value
+ else:
+ raise OptionException('Arguments may only be string, int, bool, or array of those.')
+
+ def reduce_arguments(self, args):
+ assert(isinstance(args, mparser.ArgumentNode))
+ if args.incorrect_order():
+ raise OptionException('All keyword arguments must be after positional arguments.')
+ reduced_pos = [self.reduce_single(arg) for arg in args.arguments]
+ reduced_kw = {}
+ for key in args.kwargs.keys():
+ if not isinstance(key, str):
+ raise OptionException('Keyword argument name is not a string.')
+ a = args.kwargs[key]
+ reduced_kw[key] = self.reduce_single(a)
+ return (reduced_pos, reduced_kw)
+
+ def evaluate_statement(self, node):
+ if not isinstance(node, mparser.FunctionNode):
+ raise OptionException('Option file may only contain option definitions')
+ func_name = node.func_name
+ if func_name != 'option':
+ raise OptionException('Only calls to option() are allowed in option files.')
+ (posargs, kwargs) = self.reduce_arguments(node.args)
+ if 'type' not in kwargs:
+ raise OptionException('Option call missing mandatory "type" keyword argument')
+ opt_type = kwargs['type']
+ if not opt_type in option_types:
+ raise OptionException('Unknown type %s.' % opt_type)
+ if len(posargs) != 1:
+ raise OptionException('Option() must have one (and only one) positional argument')
+ opt_name = posargs[0]
+ if not isinstance(opt_name, str):
+ raise OptionException('Positional argument must be a string.')
+ if optname_regex.search(opt_name) is not None:
+ raise OptionException('Option names can only contain letters, numbers or dashes.')
+ if is_invalid_name(opt_name):
+ raise OptionException('Option name %s is reserved.' % opt_name)
+ if self.subproject != '':
+ opt_name = self.subproject + ':' + opt_name
+ opt = option_types[opt_type](opt_name, kwargs.get('description', ''), kwargs)
+ if opt.description == '':
+ opt.description = opt_name
+ if opt_name in self.cmd_line_options:
+ opt.set_value(opt.parse_string(self.cmd_line_options[opt_name]))
+ self.options[opt_name] = opt
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:]))
diff --git a/mesonbuild/vs2010backend.py b/mesonbuild/vs2010backend.py
new file mode 100644
index 0000000..33e9646
--- /dev/null
+++ b/mesonbuild/vs2010backend.py
@@ -0,0 +1,640 @@
+# Copyright 2014-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 os, sys
+import pickle
+from . import backends, build
+from . import dependencies
+from . import mlog
+import xml.etree.ElementTree as ET
+import xml.dom.minidom
+from .coredata import MesonException
+
+class RegenInfo():
+ def __init__(self, source_dir, build_dir, depfiles, solutionfile):
+ self.source_dir = source_dir
+ self.build_dir = build_dir
+ self.depfiles = depfiles
+ self.solutionfile = solutionfile
+
+class Vs2010Backend(backends.Backend):
+ def __init__(self, build):
+ super().__init__(build)
+ self.project_file_version = '10.0.30319.1'
+ # foo.c compiles to foo.obj, not foo.c.obj
+ self.source_suffix_in_obj = False
+
+ def generate_custom_generator_commands(self, target, parent_node):
+ idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup')
+ all_output_files = []
+ for genlist in target.get_generated_sources():
+ if isinstance(genlist, build.CustomTarget):
+ all_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output]
+ else:
+ generator = genlist.get_generator()
+ exe = generator.get_exe()
+ infilelist = genlist.get_infilelist()
+ outfilelist = genlist.get_outfilelist()
+ if isinstance(exe, build.BuildTarget):
+ exe_file = os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe))
+ else:
+ exe_file = exe.get_command()
+ base_args = generator.get_arglist()
+ for i in range(len(infilelist)):
+ if len(infilelist) == len(outfilelist):
+ sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
+ else:
+ sole_output = ''
+ curfile = infilelist[i]
+ infilename = os.path.join(self.environment.get_source_dir(), curfile)
+ outfiles = genlist.get_outputs_for(curfile)
+ outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles]
+ all_output_files += outfiles
+ args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\
+ for x in base_args]
+ args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir(target))
+ for x in args]
+ fullcmd = [exe_file] + args
+ cbs = ET.SubElement(idgroup, 'CustomBuildStep')
+ ET.SubElement(cbs, 'Command').text = ' '.join(self.special_quote(fullcmd))
+ ET.SubElement(cbs, 'Inputs').text = infilename
+ ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles)
+ ET.SubElement(cbs, 'Message').text = 'Generating sources from %s.' % infilename
+ pg = ET.SubElement(parent_node, 'PropertyGroup')
+ ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile'
+ return all_output_files
+
+ def generate(self, interp):
+ self.interpreter = interp
+ self.platform = 'Win32'
+ self.buildtype = self.environment.coredata.get_builtin_option('buildtype')
+ sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln')
+ projlist = self.generate_projects()
+ self.gen_testproj('RUN_TESTS', os.path.join(self.environment.get_build_dir(), 'RUN_TESTS.vcxproj'))
+ self.gen_regenproj('REGEN', os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj'))
+ self.generate_solution(sln_filename, projlist)
+ self.generate_regen_info(sln_filename)
+ open(os.path.join(self.environment.get_scratch_dir(), 'regen.stamp'), 'wb')
+ rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule')
+ if not os.path.exists(rulefile):
+ open(rulefile, 'w').write("# For some reason this needs to be here.")
+
+ def generate_regen_info(self, sln_filename):
+ deps = self.get_regen_filelist()
+ regeninfo = RegenInfo(self.environment.get_source_dir(),
+ self.environment.get_build_dir(),
+ deps,
+ sln_filename)
+ pickle.dump(regeninfo, open(os.path.join(self.environment.get_scratch_dir(), 'regeninfo.dump'), 'wb'))
+
+ def get_obj_target_deps(self, obj_list):
+ result = {}
+ for o in obj_list:
+ if isinstance(o, build.ExtractedObjects):
+ result[o.target.get_basename()] = True
+ return result.keys()
+
+ def determine_deps(self, p):
+ all_deps = {}
+ target = self.build.targets[p[0]]
+ if isinstance(target, build.CustomTarget):
+ for d in target.dependencies:
+ all_deps[d.get_id()] = True
+ return all_deps
+ if isinstance(target, build.RunTarget):
+ for d in [target.command] + target.args:
+ if isinstance(d, build.BuildTarget):
+ all_deps[d.get_id()] = True
+ return all_deps
+ for ldep in target.link_targets:
+ all_deps[ldep.get_id()] = True
+ for objdep in self.get_obj_target_deps(target.objects):
+ all_deps[objdep] = True
+ for gendep in target.generated:
+ if isinstance(gendep, build.CustomTarget):
+ all_deps[gendep.get_id()] = True
+ else:
+ gen_exe = gendep.generator.get_exe()
+ if isinstance(gen_exe, build.Executable):
+ all_deps[gen_exe.get_id()] = True
+ return all_deps
+
+ def generate_solution(self, sln_filename, projlist):
+ ofile = open(sln_filename, 'w')
+ ofile.write('Microsoft Visual Studio Solution File, Format Version 11.00\n')
+ ofile.write('# Visual Studio 2010\n')
+ prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n'
+ for p in projlist:
+ prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2])
+ ofile.write(prj_line)
+ all_deps = self.determine_deps(p)
+ ofile.write('\tProjectSection(ProjectDependencies) = postProject\n')
+ regen_guid = self.environment.coredata.regen_guid
+ ofile.write('\t\t{%s} = {%s}\n' % (regen_guid, regen_guid))
+ for dep in all_deps.keys():
+ guid = self.environment.coredata.target_guids[dep]
+ ofile.write('\t\t{%s} = {%s}\n' % (guid, guid))
+ ofile.write('EndProjectSection\n')
+ ofile.write('EndProject\n')
+ test_line = prj_templ % (self.environment.coredata.guid,
+ 'RUN_TESTS', 'RUN_TESTS.vcxproj', self.environment.coredata.test_guid)
+ ofile.write(test_line)
+ ofile.write('EndProject\n')
+ regen_line = prj_templ % (self.environment.coredata.guid,
+ 'REGEN', 'REGEN.vcxproj', self.environment.coredata.regen_guid)
+ ofile.write(regen_line)
+ ofile.write('EndProject\n')
+ ofile.write('Global\n')
+ ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
+ ofile.write('\t\t%s|%s = %s|%s\n' % (self.buildtype, self.platform, self.buildtype, self.platform))
+ ofile.write('\tEndGlobalSection\n')
+ ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
+ ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' %
+ (self.environment.coredata.regen_guid, self.buildtype, self.platform,
+ self.buildtype, self.platform))
+ ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' %
+ (self.environment.coredata.regen_guid, self.buildtype, self.platform,
+ self.buildtype, self.platform))
+ for p in projlist:
+ ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' %
+ (p[2], self.buildtype, self.platform,
+ self.buildtype, self.platform))
+ if not isinstance(self.build.targets[p[0]], build.RunTarget):
+ ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' %
+ (p[2], self.buildtype, self.platform,
+ self.buildtype, self.platform))
+ ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' %
+ (self.environment.coredata.test_guid, self.buildtype, self.platform,
+ self.buildtype, self.platform))
+ ofile.write('\tEndGlobalSection\n')
+ ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n')
+ ofile.write('\t\tHideSolutionNode = FALSE\n')
+ ofile.write('\tEndGlobalSection\n')
+ ofile.write('EndGlobal\n')
+
+ def generate_projects(self):
+ projlist = []
+ comp = None
+ for l, c in self.environment.coredata.compilers.items():
+ if l == 'c' or l == 'cpp':
+ comp = c
+ break
+ if comp is None:
+ raise RuntimeError('C and C++ compilers missing.')
+ for name, target in self.build.targets.items():
+ outdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
+ fname = name + '.vcxproj'
+ relname = os.path.join(target.subdir, fname)
+ projfile = os.path.join(outdir, fname)
+ uuid = self.environment.coredata.target_guids[name]
+ self.gen_vcxproj(target, projfile, uuid, comp)
+ projlist.append((name, relname, uuid))
+ return projlist
+
+ def split_sources(self, srclist):
+ sources = []
+ headers = []
+ for i in srclist:
+ if self.environment.is_header(i):
+ headers.append(i)
+ else:
+ sources.append(i)
+ return (sources, headers)
+
+ def target_to_build_root(self, target):
+ if target.subdir == '':
+ return ''
+
+ directories = os.path.split(target.subdir)
+ directories = list(filter(bool,directories)) #Filter out empty strings
+
+ return '/'.join(['..']*len(directories))
+
+ def special_quote(self, arr):
+ return ['&quot;%s&quot;' % i for i in arr]
+
+ def create_basic_crap(self, target):
+ project_name = target.name
+ root = ET.Element('Project', {'DefaultTargets' : "Build",
+ 'ToolsVersion' : '4.0',
+ 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'})
+ confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'})
+ prjconf = ET.SubElement(confitems, 'ProjectConfiguration',
+ {'Include' : self.buildtype + '|' + self.platform})
+ p = ET.SubElement(prjconf, 'Configuration')
+ p.text= self.buildtype
+ pl = ET.SubElement(prjconf, 'Platform')
+ pl.text = self.platform
+ globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals')
+ guidelem = ET.SubElement(globalgroup, 'ProjectGuid')
+ guidelem.text = self.environment.coredata.test_guid
+ kw = ET.SubElement(globalgroup, 'Keyword')
+ kw.text = self.platform + 'Proj'
+ p = ET.SubElement(globalgroup, 'Platform')
+ p.text= self.platform
+ pname= ET.SubElement(globalgroup, 'ProjectName')
+ pname.text = project_name
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
+ type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
+ ET.SubElement(type_config, 'ConfigurationType')
+ ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte'
+ ET.SubElement(type_config, 'UseOfMfc').text = 'false'
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
+ direlem = ET.SubElement(root, 'PropertyGroup')
+ fver = ET.SubElement(direlem, '_ProjectFileVersion')
+ fver.text = self.project_file_version
+ outdir = ET.SubElement(direlem, 'OutDir')
+ outdir.text = '.\\'
+ intdir = ET.SubElement(direlem, 'IntDir')
+ intdir.text = 'test-temp\\'
+ tname = ET.SubElement(direlem, 'TargetName')
+ tname.text = target.name
+ return root
+
+ def gen_run_target_vcxproj(self, target, ofname, guid):
+ root = self.create_basic_crap(target)
+ action = ET.SubElement(root, 'ItemDefinitionGroup')
+ customstep = ET.SubElement(action, 'PostBuildEvent')
+ cmd_raw = [target.command] + target.args
+ cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'),
+ self.environment.get_build_dir(), self.environment.get_source_dir(),
+ self.get_target_dir(target)]
+ for i in cmd_raw:
+ if isinstance(i, build.BuildTarget):
+ cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
+ elif isinstance(i, dependencies.ExternalProgram):
+ cmd += i.fullpath
+ else:
+ cmd.append(i)
+ cmd_templ = '''"%s" '''*len(cmd)
+ ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd)
+ ET.SubElement(customstep, 'Message').text = 'Running custom command.'
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
+ tree = ET.ElementTree(root)
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+
+ def gen_custom_target_vcxproj(self, target, ofname, guid):
+ root = self.create_basic_crap(target)
+ action = ET.SubElement(root, 'ItemDefinitionGroup')
+ customstep = ET.SubElement(action, 'CustomBuildStep')
+ (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True)
+ cmd_templ = '''"%s" '''*len(cmd)
+ ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd)
+ ET.SubElement(customstep, 'Outputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i)\
+ for i in ofilenames])
+ ET.SubElement(customstep, 'Inputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i) \
+ for i in srcs])
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
+ tree = ET.ElementTree(root)
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+
+ def gen_vcxproj(self, target, ofname, guid, compiler):
+ mlog.debug('Generating vcxproj %s.' % target.name)
+ entrypoint = 'WinMainCRTStartup'
+ subsystem = 'Windows'
+ if isinstance(target, build.Executable):
+ conftype = 'Application'
+ if not target.gui_app:
+ subsystem = 'Console'
+ entrypoint = 'mainCRTStartup'
+ elif isinstance(target, build.StaticLibrary):
+ conftype = 'StaticLibrary'
+ elif isinstance(target, build.SharedLibrary):
+ conftype = 'DynamicLibrary'
+ entrypoint = '_DllMainCrtStartup'
+ elif isinstance(target, build.CustomTarget):
+ return self.gen_custom_target_vcxproj(target, ofname, guid)
+ elif isinstance(target, build.RunTarget):
+ return self.gen_run_target_vcxproj(target, ofname, guid)
+ else:
+ raise MesonException('Unknown target type for %s' % target.get_basename())
+ down = self.target_to_build_root(target)
+ proj_to_src_root = os.path.join(down, self.build_to_src)
+ proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir)
+ (sources, headers) = self.split_sources(target.sources)
+ buildtype = self.buildtype
+ project_name = target.name
+ target_name = target.name
+ root = ET.Element('Project', {'DefaultTargets' : "Build",
+ 'ToolsVersion' : '4.0',
+ 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'})
+ confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'})
+ prjconf = ET.SubElement(confitems, 'ProjectConfiguration',
+ {'Include' : self.buildtype + '|' + self.platform})
+ p = ET.SubElement(prjconf, 'Configuration')
+ p.text= buildtype
+ pl = ET.SubElement(prjconf, 'Platform')
+ pl.text = self.platform
+ globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals')
+ guidelem = ET.SubElement(globalgroup, 'ProjectGuid')
+ guidelem.text = guid
+ kw = ET.SubElement(globalgroup, 'Keyword')
+ kw.text = self.platform + 'Proj'
+ ns = ET.SubElement(globalgroup, 'RootNamespace')
+ ns.text = target_name
+ p = ET.SubElement(globalgroup, 'Platform')
+ p.text= self.platform
+ pname= ET.SubElement(globalgroup, 'ProjectName')
+ pname.text = project_name
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
+ type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
+ ET.SubElement(type_config, 'ConfigurationType').text = conftype
+ ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte'
+ ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false'
+ ET.SubElement(type_config, 'UseDebugLibraries').text = 'true'
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
+ generated_files = self.generate_custom_generator_commands(target, root)
+ (gen_src, gen_hdrs) = self.split_sources(generated_files)
+ direlem = ET.SubElement(root, 'PropertyGroup')
+ fver = ET.SubElement(direlem, '_ProjectFileVersion')
+ fver.text = self.project_file_version
+ outdir = ET.SubElement(direlem, 'OutDir')
+ outdir.text = '.\\'
+ intdir = ET.SubElement(direlem, 'IntDir')
+ intdir.text = os.path.join(self.get_target_dir(target), target.get_basename() + '.dir') + '\\'
+ tname = ET.SubElement(direlem, 'TargetName')
+ tname.text = target_name
+ inclinc = ET.SubElement(direlem, 'LinkIncremental')
+ inclinc.text = 'true'
+
+ compiles = ET.SubElement(root, 'ItemDefinitionGroup')
+ clconf = ET.SubElement(compiles, 'ClCompile')
+ opt = ET.SubElement(clconf, 'Optimization')
+ opt.text = 'disabled'
+ inc_dirs = [proj_to_src_dir, self.get_target_private_dir(target)]
+ cur_dir = target.subdir
+ if cur_dir == '':
+ cur_dir= '.'
+ inc_dirs.append(cur_dir)
+ extra_args = []
+ # SUCKS, VS can not handle per-language type flags, so just use
+ # them all.
+ extra_args += compiler.get_buildtype_args(self.buildtype)
+ for l in self.environment.coredata.external_args.values():
+ for a in l:
+ extra_args.append(a)
+ for l in self.build.global_args.values():
+ for a in l:
+ extra_args.append(a)
+ for l in target.extra_args.values():
+ for a in l:
+ extra_args.append(a)
+ # FIXME all the internal flags of VS (optimization etc) are represented
+ # by their own XML elements. In theory we should split all flags to those
+ # that have an XML element and those that don't and serialise them
+ # properly. This is a crapton of work for no real gain, so just dump them
+ # here.
+ extra_args = compiler.get_option_compile_args(self.environment.coredata.compiler_options)
+ if len(extra_args) > 0:
+ extra_args.append('%(AdditionalOptions)')
+ ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(extra_args)
+ for d in target.include_dirs:
+ for i in d.incdirs:
+ curdir = os.path.join(d.curdir, i)
+ inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir
+ inc_dirs.append(os.path.join(proj_to_src_root, curdir)) # src dir
+ inc_dirs.append('%(AdditionalIncludeDirectories)')
+ ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs)
+ preproc = ET.SubElement(clconf, 'PreprocessorDefinitions')
+ rebuild = ET.SubElement(clconf, 'MinimalRebuild')
+ rebuild.text = 'true'
+ rtlib = ET.SubElement(clconf, 'RuntimeLibrary')
+ rtlib.text = 'MultiThreadedDebugDLL'
+ funclink = ET.SubElement(clconf, 'FunctionLevelLinking')
+ funclink.text = 'true'
+ pch = ET.SubElement(clconf, 'PrecompiledHeader')
+ warnings = ET.SubElement(clconf, 'WarningLevel')
+ warnings.text = 'Level3'
+ debinfo = ET.SubElement(clconf, 'DebugInformationFormat')
+ debinfo.text = 'EditAndContinue'
+ resourcecompile = ET.SubElement(compiles, 'ResourceCompile')
+ ET.SubElement(resourcecompile, 'PreprocessorDefinitions')
+ link = ET.SubElement(compiles, 'Link')
+ # Put all language args here, too.
+ extra_link_args = compiler.get_option_link_args(self.environment.coredata.compiler_options)
+ extra_link_args += compiler.get_buildtype_linker_args(self.buildtype)
+ for l in self.environment.coredata.external_link_args.values():
+ for a in l:
+ extra_link_args.append(a)
+ for l in target.link_args:
+ for a in l:
+ extra_link_args.append(a)
+ if len(extra_args) > 0:
+ extra_args.append('%(AdditionalOptions)')
+ ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_args)
+
+ additional_links = []
+ for t in target.link_targets:
+ lobj = self.build.targets[t.get_id()]
+ rel_path = self.relpath(lobj.subdir, target.subdir)
+ linkname = os.path.join(rel_path, lobj.get_import_filename())
+ additional_links.append(linkname)
+ for o in self.flatten_object_list(target, down):
+ assert(isinstance(o, str))
+ additional_links.append(o)
+ if len(additional_links) > 0:
+ additional_links.append('%(AdditionalDependencies)')
+ ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
+ ofile = ET.SubElement(link, 'OutputFile')
+ ofile.text = '$(OutDir)%s' % target.get_filename()
+ addlibdir = ET.SubElement(link, 'AdditionalLibraryDirectories')
+ addlibdir.text = '%(AdditionalLibraryDirectories)'
+ subsys = ET.SubElement(link, 'SubSystem')
+ subsys.text = subsystem
+ gendeb = ET.SubElement(link, 'GenerateDebugInformation')
+ gendeb.text = 'true'
+ if isinstance(target, build.SharedLibrary):
+ ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename()
+ pdb = ET.SubElement(link, 'ProgramDataBaseFileName')
+ pdb.text = '$(OutDir}%s.pdb' % target_name
+ if isinstance(target, build.Executable):
+ ET.SubElement(link, 'EntryPointSymbol').text = entrypoint
+ targetmachine = ET.SubElement(link, 'TargetMachine')
+ targetmachine.text = 'MachineX86'
+
+ if len(headers) + len(gen_hdrs) > 0:
+ inc_hdrs = ET.SubElement(root, 'ItemGroup')
+ for h in headers:
+ relpath = h.rel_to_builddir(proj_to_src_root)
+ ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath)
+ for h in gen_hdrs:
+ if isinstance(h, str):
+ relpath = h
+ else:
+ relpath = h.rel_to_builddir(proj_to_src_root)
+ ET.SubElement(inc_hdrs, 'CLInclude', Include = relpath)
+ if len(sources) + len(gen_src) > 0:
+ inc_src = ET.SubElement(root, 'ItemGroup')
+ for s in sources:
+ relpath = s.rel_to_builddir(proj_to_src_root)
+ ET.SubElement(inc_src, 'CLCompile', Include=relpath)
+ for s in gen_src:
+ relpath = self.relpath(s, target.subdir)
+ ET.SubElement(inc_src, 'CLCompile', Include=relpath)
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
+ # Reference the regen target.
+ ig = ET.SubElement(root, 'ItemGroup')
+ pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj'))
+ ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid
+ tree = ET.ElementTree(root)
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ # ElementTree can not do prettyprinting so do it manually
+ doc = xml.dom.minidom.parse(ofname)
+ open(ofname, 'w').write(doc.toprettyxml())
+ # World of horror! Python insists on not quoting quotes and
+ # fixing the escaped &quot; into &amp;quot; whereas MSVS
+ # requires quoted but not fixed elements. Enter horrible hack.
+ txt = open(ofname, 'r').read()
+ open(ofname, 'w').write(txt.replace('&amp;quot;', '&quot;'))
+
+ def gen_regenproj(self, project_name, ofname):
+ root = ET.Element('Project', {'DefaultTargets': 'Build',
+ 'ToolsVersion' : '4.0',
+ 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'})
+ confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'})
+ prjconf = ET.SubElement(confitems, 'ProjectConfiguration',
+ {'Include' : self.buildtype + '|' + self.platform})
+ p = ET.SubElement(prjconf, 'Configuration')
+ p.text= self.buildtype
+ pl = ET.SubElement(prjconf, 'Platform')
+ pl.text = self.platform
+ globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals')
+ guidelem = ET.SubElement(globalgroup, 'ProjectGuid')
+ guidelem.text = self.environment.coredata.test_guid
+ kw = ET.SubElement(globalgroup, 'Keyword')
+ kw.text = self.platform + 'Proj'
+ p = ET.SubElement(globalgroup, 'Platform')
+ p.text = self.platform
+ pname= ET.SubElement(globalgroup, 'ProjectName')
+ pname.text = project_name
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
+ type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
+ ET.SubElement(type_config, 'ConfigurationType').text = "Utility"
+ ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte'
+ ET.SubElement(type_config, 'UseOfMfc').text = 'false'
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
+ direlem = ET.SubElement(root, 'PropertyGroup')
+ fver = ET.SubElement(direlem, '_ProjectFileVersion')
+ fver.text = self.project_file_version
+ outdir = ET.SubElement(direlem, 'OutDir')
+ outdir.text = '.\\'
+ intdir = ET.SubElement(direlem, 'IntDir')
+ intdir.text = 'test-temp\\'
+ tname = ET.SubElement(direlem, 'TargetName')
+ tname.text = project_name
+
+ action = ET.SubElement(root, 'ItemDefinitionGroup')
+ midl = ET.SubElement(action, 'Midl')
+ ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)'
+ ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)'
+ ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h'
+ ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb'
+ ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c'
+ ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c'
+ script_root = self.environment.get_script_dir()
+ regen_script = os.path.join(script_root, 'regen_checker.py')
+ private_dir = self.environment.get_scratch_dir()
+ cmd_templ = '''setlocal
+"%s" "%s" "%s"
+if %%errorlevel%% neq 0 goto :cmEnd
+:cmEnd
+endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone
+:cmErrorLevel
+exit /b %%1
+:cmDone
+if %%errorlevel%% neq 0 goto :VCEnd'''
+ igroup = ET.SubElement(root, 'ItemGroup')
+ custombuild = ET.SubElement(igroup, 'CustomBuild', Include='meson-private/regen.rule')
+ message = ET.SubElement(custombuild, 'Message')
+ message.text = 'Checking whether solution needs to be regenerated.'
+ ET.SubElement(custombuild, 'Command').text = cmd_templ % \
+ (sys.executable, regen_script, private_dir)
+ ET.SubElement(custombuild, 'Outputs').text = os.path.join(self.environment.get_scratch_dir(), 'regen.stamp')
+ deps = self.get_regen_filelist()
+ depstr = ';'.join([os.path.join(self.environment.get_source_dir(), d) for d in deps])
+ ET.SubElement(custombuild, 'AdditionalInputs').text = depstr
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
+ ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets')
+ tree = ET.ElementTree(root)
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+
+ def gen_testproj(self, target_name, ofname):
+ project_name = target_name
+ root = ET.Element('Project', {'DefaultTargets' : "Build",
+ 'ToolsVersion' : '4.0',
+ 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'})
+ confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'})
+ prjconf = ET.SubElement(confitems, 'ProjectConfiguration',
+ {'Include' : self.buildtype + '|' + self.platform})
+ p = ET.SubElement(prjconf, 'Configuration')
+ p.text= self.buildtype
+ pl = ET.SubElement(prjconf, 'Platform')
+ pl.text = self.platform
+ globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals')
+ guidelem = ET.SubElement(globalgroup, 'ProjectGuid')
+ guidelem.text = self.environment.coredata.test_guid
+ kw = ET.SubElement(globalgroup, 'Keyword')
+ kw.text = self.platform + 'Proj'
+ p = ET.SubElement(globalgroup, 'Platform')
+ p.text= self.platform
+ pname= ET.SubElement(globalgroup, 'ProjectName')
+ pname.text = project_name
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
+ type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
+ ET.SubElement(type_config, 'ConfigurationType')
+ ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte'
+ ET.SubElement(type_config, 'UseOfMfc').text = 'false'
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
+ direlem = ET.SubElement(root, 'PropertyGroup')
+ fver = ET.SubElement(direlem, '_ProjectFileVersion')
+ fver.text = self.project_file_version
+ outdir = ET.SubElement(direlem, 'OutDir')
+ outdir.text = '.\\'
+ intdir = ET.SubElement(direlem, 'IntDir')
+ intdir.text = 'test-temp\\'
+ tname = ET.SubElement(direlem, 'TargetName')
+ tname.text = target_name
+
+ action = ET.SubElement(root, 'ItemDefinitionGroup')
+ midl = ET.SubElement(action, 'Midl')
+ ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)'
+ ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)'
+ ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h'
+ ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb'
+ ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c'
+ ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c'
+ postbuild = ET.SubElement(action, 'PostBuildEvent')
+ ET.SubElement(postbuild, 'Message')
+ script_root = self.environment.get_script_dir()
+ test_script = os.path.join(script_root, 'meson_test.py')
+ test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
+ cmd_templ = '''setlocal
+"%s" "%s" "%s"
+if %%errorlevel%% neq 0 goto :cmEnd
+:cmEnd
+endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone
+:cmErrorLevel
+exit /b %%1
+:cmDone
+if %%errorlevel%% neq 0 goto :VCEnd'''
+ ET.SubElement(postbuild, 'Command').text = cmd_templ % (sys.executable, test_script, test_data)
+ ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
+ tree = ET.ElementTree(root)
+ tree.write(ofname, encoding='utf-8', xml_declaration=True)
+ datafile = open(test_data, 'wb')
+ self.serialise_tests()
+ datafile.close()
+ # ElementTree can not do prettyprinting so do it manually
+ #doc = xml.dom.minidom.parse(ofname)
+ #open(ofname, 'w').write(doc.toprettyxml())
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
new file mode 100644
index 0000000..2818fa0
--- /dev/null
+++ b/mesonbuild/wrap/wrap.py
@@ -0,0 +1,212 @@
+# 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.
+
+from .. import mlog
+import urllib.request, os, hashlib, shutil
+import subprocess
+import sys
+
+try:
+ import ssl
+ has_ssl = True
+ API_ROOT = 'https://wrapdb.mesonbuild.com/v1/'
+except ImportError:
+ has_ssl = False
+ API_ROOT = 'http://wrapdb.mesonbuild.com/v1/'
+
+def build_ssl_context():
+ ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ ctx.options |= ssl.OP_NO_SSLv2
+ ctx.options |= ssl.OP_NO_SSLv3
+ ctx.verify_mode = ssl.CERT_REQUIRED
+ ctx.load_default_certs()
+ return ctx
+
+def open_wrapdburl(urlstring):
+ global ssl_warning_printed
+ if has_ssl:
+ try:
+ return urllib.request.urlopen(urlstring)#, context=build_ssl_context())
+ except urllib.error.URLError:
+ if not ssl_warning_printed:
+ print('SSL connection failed. Falling back to unencrypted connections.')
+ ssl_warning_printed = True
+ if not ssl_warning_printed:
+ print('Warning: SSL not available, traffic not authenticated.',
+ file=sys.stderr)
+ ssl_warning_printed = True
+ # Trying to open SSL connection to wrapdb fails because the
+ # certificate is not known.
+ if urlstring.startswith('https'):
+ urlstring = 'http' + urlstring[5:]
+ return urllib.request.urlopen(urlstring)
+
+
+class PackageDefinition:
+ def __init__(self, fname):
+ self.values = {}
+ ifile = open(fname)
+ first = ifile.readline().strip()
+
+ if first == '[wrap-file]':
+ self.type = 'file'
+ elif first == '[wrap-git]':
+ self.type = 'git'
+ else:
+ raise RuntimeError('Invalid format of package file')
+ for line in ifile:
+ line = line.strip()
+ if line == '':
+ continue
+ (k, v) = line.split('=', 1)
+ k = k.strip()
+ v = v.strip()
+ self.values[k] = v
+
+ def get(self, key):
+ return self.values[key]
+
+ def has_patch(self):
+ return 'patch_url' in self.values
+
+class Resolver:
+ def __init__(self, subdir_root):
+ self.subdir_root = subdir_root
+ self.cachedir = os.path.join(self.subdir_root, 'packagecache')
+
+ def resolve(self, packagename):
+ fname = os.path.join(self.subdir_root, packagename + '.wrap')
+ dirname = os.path.join(self.subdir_root, packagename)
+ if not os.path.isfile(fname):
+ if os.path.isdir(dirname):
+ # No wrap file but dir exists -> user put it there manually.
+ return packagename
+ return None
+ p = PackageDefinition(fname)
+ if p.type == 'file':
+ if not os.path.isdir(self.cachedir):
+ os.mkdir(self.cachedir)
+ self.download(p, packagename)
+ self.extract_package(p)
+ elif p.type == 'git':
+ self.get_git(p)
+ else:
+ raise RuntimeError('Unreachable code.')
+ return p.get('directory')
+
+ def get_git(self, p):
+ checkoutdir = os.path.join(self.subdir_root, p.get('directory'))
+ revno = p.get('revision')
+ is_there = os.path.isdir(checkoutdir)
+ if is_there:
+ if revno.lower() == 'head':
+ subprocess.check_call(['git', 'pull'], cwd=checkoutdir)
+ else:
+ if subprocess.call(['git', 'checkout', revno], cwd=checkoutdir) != 0:
+ subprocess.check_call(['git', 'fetch'], cwd=checkoutdir)
+ subprocess.check_call(['git', 'checkout', revno],
+ cwd=checkoutdir)
+ else:
+ subprocess.check_call(['git', 'clone', p.get('url'),
+ p.get('directory')], cwd=self.subdir_root)
+ if revno.lower() != 'head':
+ subprocess.check_call(['git', 'checkout', revno],
+ cwd=checkoutdir)
+
+
+ def get_data(self, url):
+ blocksize = 10*1024
+ if url.startswith('https://wrapdb.mesonbuild.com'):
+ resp = open_wrapdburl(url)
+ else:
+ resp = urllib.request.urlopen(url)
+ dlsize = int(resp.info()['Content-Length'])
+ print('Download size:', dlsize)
+ print('Downloading: ', end='')
+ sys.stdout.flush()
+ printed_dots = 0
+ blocks = []
+ downloaded = 0
+ while True:
+ block = resp.read(blocksize)
+ if block == b'':
+ break
+ downloaded += len(block)
+ blocks.append(block)
+ ratio = int(downloaded/dlsize * 10)
+ while printed_dots < ratio:
+ print('.', end='')
+ sys.stdout.flush()
+ printed_dots += 1
+ print('')
+ resp.close()
+ return b''.join(blocks)
+
+ def get_hash(self, data):
+ h = hashlib.sha256()
+ h.update(data)
+ hashvalue = h.hexdigest()
+ return hashvalue
+
+ def download(self, p, packagename):
+ ofname = os.path.join(self.cachedir, p.get('source_filename'))
+ if os.path.exists(ofname):
+ mlog.log('Using', mlog.bold(packagename), 'from cache.')
+ return
+ srcurl = p.get('source_url')
+ mlog.log('Dowloading', mlog.bold(packagename), 'from', mlog.bold(srcurl))
+ srcdata = self.get_data(srcurl)
+ dhash = self.get_hash(srcdata)
+ expected = p.get('source_hash')
+ if dhash != expected:
+ raise RuntimeError('Incorrect hash for source %s:\n %s expected\n %s actual.' % (packagename, expected, dhash))
+ open(ofname, 'wb').write(srcdata)
+ if p.has_patch():
+ purl = p.get('patch_url')
+ mlog.log('Downloading patch from', mlog.bold(purl))
+ pdata = self.get_data(purl)
+ phash = self.get_hash(pdata)
+ expected = p.get('patch_hash')
+ if phash != expected:
+ raise RuntimeError('Incorrect hash for patch %s:\n %s expected\n %s actual' % (packagename, expected, phash))
+ open(os.path.join(self.cachedir, p.get('patch_filename')), 'wb').write(pdata)
+ else:
+ mlog.log('Package does not require patch.')
+
+ def extract_package(self, package):
+ if sys.version_info < (3, 5):
+ try:
+ import lzma
+ del lzma
+ try:
+ shutil.register_unpack_format('xztar', ['.tar.xz', '.txz'], shutil._unpack_tarfile, [], "xz'ed tar-file")
+ except shutil.RegistryError:
+ pass
+ except ImportError:
+ pass
+ target_dir = os.path.join(self.subdir_root, package.get('directory'))
+ if os.path.isdir(target_dir):
+ return
+ extract_dir = self.subdir_root
+ # Some upstreams ship packages that do not have a leading directory.
+ # Create one for them.
+ try:
+ package.get('lead_directory_missing')
+ os.mkdir(target_dir)
+ extract_dir = target_dir
+ except KeyError:
+ pass
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('source_filename')), extract_dir)
+ if package.has_patch():
+ shutil.unpack_archive(os.path.join(self.cachedir, package.get('patch_filename')), self.subdir_root)
diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py
new file mode 100755
index 0000000..d2f0a28
--- /dev/null
+++ b/mesonbuild/wrap/wraptool.py
@@ -0,0 +1,200 @@
+#!/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 json
+import sys, os
+import configparser
+import shutil
+
+from glob import glob
+
+from .wrap import API_ROOT, open_wrapdburl
+
+help_templ = '''This program allows you to manage your Wrap dependencies
+using the online wrap database http://wrapdb.mesonbuild.com.
+
+Run this command in your top level source directory.
+
+Usage:
+
+%s <command> [options]
+
+Commands:
+
+ list - show all available projects
+ search - search the db by name
+ install - install the specified project
+ update - update the project to its newest available release
+ info - show available versions of a project
+ status - show installed and available versions of your projects
+
+'''
+
+
+def print_help():
+ print(help_templ % sys.argv[0])
+
+def get_result(urlstring):
+ u = open_wrapdburl(urlstring)
+ data = u.read().decode('utf-8')
+ jd = json.loads(data)
+ if jd['output'] != 'ok':
+ print('Got bad output from server.')
+ print(data)
+ sys.exit(1)
+ return jd
+
+def get_projectlist():
+ jd = get_result(API_ROOT + 'projects')
+ projects = jd['projects']
+ return projects
+
+def list_projects():
+ projects = get_projectlist()
+ for p in projects:
+ print(p)
+
+def search(name):
+ jd = get_result(API_ROOT + 'query/byname/' + name)
+ for p in jd['projects']:
+ print(p)
+
+def get_latest_version(name):
+ jd = get_result(API_ROOT + 'query/get_latest/' + name)
+ branch = jd['branch']
+ revision = jd['revision']
+ return (branch, revision)
+
+def install(name):
+ if not os.path.isdir('subprojects'):
+ print('Subprojects dir not found. Run this script in your source root directory.')
+ sys.exit(1)
+ if os.path.isdir(os.path.join('subprojects', name)):
+ print('Subproject directory for this project already exists.')
+ sys.exit(1)
+ wrapfile = os.path.join('subprojects', name + '.wrap')
+ if os.path.exists(wrapfile):
+ print('Wrap file already exists.')
+ sys.exit(1)
+ (branch, revision) = get_latest_version(name)
+ u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%s/get_wrap' % (name, branch, revision))
+ data = u.read()
+ open(wrapfile, 'wb').write(data)
+ print('Installed', name, 'branch', branch, 'revision', revision)
+
+def get_current_version(wrapfile):
+ cp = configparser.ConfigParser()
+ cp.read(wrapfile)
+ cp = cp['wrap-file']
+ patch_url = cp['patch_url']
+ arr = patch_url.split('/')
+ branch = arr[-3]
+ revision = int(arr[-2])
+ return (branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename'])
+
+def update(name):
+ if not os.path.isdir('subprojects'):
+ print('Subprojects dir not found. Run this command in your source root directory.')
+ sys.exit(1)
+ wrapfile = os.path.join('subprojects', name + '.wrap')
+ if not os.path.exists(wrapfile):
+ print('Project', name, 'is not in use.')
+ sys.exit(1)
+ (branch, revision, subdir, src_file, patch_file) = get_current_version(wrapfile)
+ (new_branch, new_revision) = get_latest_version(name)
+ if new_branch == branch and new_revision == revision:
+ print('Project', name, 'is already up to date.')
+ sys.exit(0)
+ u = open_wrapdburl(API_ROOT + 'projects/%s/%s/%d/get_wrap' % (name, new_branch, new_revision))
+ data = u.read()
+ shutil.rmtree(os.path.join('subprojects', subdir), ignore_errors=True)
+ try:
+ os.unlink(os.path.join('subprojects/packagecache', src_file))
+ except FileNotFoundError:
+ pass
+ try:
+ os.unlink(os.path.join('subprojects/packagecache', patch_file))
+ except FileNotFoundError:
+ pass
+ open(wrapfile, 'wb').write(data)
+ print('Updated', name, 'to branch', new_branch, 'revision', new_revision)
+
+def info(name):
+ jd = get_result(API_ROOT + 'projects/' + name)
+ versions = jd['versions']
+ if len(versions) == 0:
+ print('No available versions of', name)
+ sys.exit(0)
+ print('Available versions of %s:' % name)
+ for v in versions:
+ print(' ', v['branch'], v['revision'])
+
+def status():
+ print('Subproject status')
+ for w in glob('subprojects/*.wrap'):
+ name = os.path.split(w)[1][:-5]
+ try:
+ (latest_branch, latest_revision) = get_latest_version(name)
+ except Exception:
+ print('', name, 'not available in wrapdb.')
+ continue
+ try:
+ (current_branch, current_revision, _, _, _) = get_current_version(w)
+ except Exception:
+ print('Wrap file not from wrapdb.')
+ continue
+ if current_branch == latest_branch and current_revision == latest_revision:
+ print('', name, 'up to date. Branch %s, revision %d.' % (current_branch, current_revision))
+ else:
+ print('', name, 'not up to date. Have %s %d, but %s %d is available.' % (current_branch, current_revision, latest_branch, latest_revision))
+
+def run(args):
+ if len(sys.argv) < 1 or sys.argv[0] == '-h' or sys.argv[1] == '--help':
+ print_help()
+ return 0
+ command = args[0]
+ args = args[1:]
+ if command == 'list':
+ list_projects()
+ elif command == 'search':
+ if len(args) != 1:
+ print('Search requires exactly one argument.')
+ return 1
+ search(args[0])
+ elif command == 'install':
+ if len(args) != 1:
+ print('Install requires exactly one argument.')
+ return 1
+ install(args[0])
+ elif command == 'update':
+ if len(args) != 1:
+ print('update requires exactly one argument.')
+ return 1
+ update(args[0])
+ elif command == 'info':
+ if len(args) != 1:
+ print('info requires exactly one argument.')
+ return 1
+ info(args[0])
+ elif command == 'status':
+ status()
+ else:
+ print('Unknown command', command)
+ return 1
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(run(sys.argv[1:]))
diff --git a/mesonbuild/xcodebackend.py b/mesonbuild/xcodebackend.py
new file mode 100644
index 0000000..8ac3f67
--- /dev/null
+++ b/mesonbuild/xcodebackend.py
@@ -0,0 +1,775 @@
+# 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.
+
+from . import backends, build
+from . import mesonlib
+import uuid, os, sys
+
+from .coredata import MesonException
+
+class XCodeBackend(backends.Backend):
+ def __init__(self, build):
+ super().__init__(build)
+ self.project_uid = self.environment.coredata.guid.replace('-', '')[:24]
+ self.project_conflist = self.gen_id()
+ self.indent = ' '
+ self.indent_level = 0
+ self.xcodetypemap = {'c' : 'sourcecode.c.c',
+ 'a' : 'archive.ar',
+ 'cc': 'sourcecode.cpp.cpp',
+ 'cxx' : 'sourcecode.cpp.cpp',
+ 'cpp' : 'sourcecode.cpp.cpp',
+ 'c++' : 'sourcecode.cpp.cpp',
+ 'm' : 'sourcecode.c.objc',
+ 'mm' : 'sourcecode.cpp.objcpp',
+ 'h' : 'sourcecode.c.h',
+ 'hpp' : 'sourcecode.cpp.h',
+ 'hxx' : 'sourcecode.cpp.h',
+ 'hh' : 'sourcecode.cpp.hh',
+ 'inc' : 'sourcecode.c.h',
+ 'dylib' : 'compiled.mach-o.dylib',
+ 'o' : 'compiled.mach-o.objfile',}
+ self.maingroup_id = self.gen_id()
+ self.all_id = self.gen_id()
+ self.all_buildconf_id = self.gen_id()
+ self.buildtypes = ['debug']
+ self.test_id = self.gen_id()
+ self.test_command_id = self.gen_id()
+ self.test_buildconf_id = self.gen_id()
+
+ def gen_id(self):
+ return str(uuid.uuid4()).upper().replace('-', '')[:24]
+
+ def get_target_dir(self, target):
+ dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_builtin_option('buildtype'))
+ os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
+ return dirname
+
+ def write_line(self, text):
+ self.ofile.write(self.indent*self.indent_level + text)
+ if not text.endswith('\n'):
+ self.ofile.write('\n')
+
+ def generate(self, interp):
+ self.interpreter = interp
+ self.serialise_tests()
+ self.generate_filemap()
+ self.generate_buildmap()
+ self.generate_buildstylemap()
+ self.generate_build_phase_map()
+ self.generate_build_configuration_map()
+ self.generate_build_configurationlist_map()
+ self.generate_project_configurations_map()
+ self.generate_buildall_configurations_map()
+ self.generate_test_configurations_map()
+ self.generate_native_target_map()
+ self.generate_source_phase_map()
+ self.generate_target_dependency_map()
+ self.generate_pbxdep_map()
+ self.generate_containerproxy_map()
+ self.proj_dir = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.xcodeproj')
+ os.makedirs(self.proj_dir, exist_ok=True)
+ self.proj_file = os.path.join(self.proj_dir, 'project.pbxproj')
+ self.ofile = open(self.proj_file, 'w')
+ self.generate_prefix()
+ self.generate_pbx_aggregate_target()
+ self.generate_pbx_build_file()
+ self.generate_pbx_build_style()
+ self.generate_pbx_container_item_proxy()
+ self.generate_pbx_file_reference()
+ self.generate_pbx_group()
+ self.generate_pbx_native_target()
+ self.generate_pbx_project()
+ self.generate_pbx_shell_build_phase()
+ self.generate_pbx_sources_build_phase()
+ self.generate_pbx_target_dependency()
+ self.generate_xc_build_configuration()
+ self.generate_xc_configurationList()
+ self.generate_suffix()
+
+ def get_xcodetype(self, fname):
+ return self.xcodetypemap[fname.split('.')[-1]]
+
+ def generate_filemap(self):
+ self.filemap = {} # Key is source file relative to src root.
+ self.target_filemap = {}
+ for name, t in self.build.targets.items():
+ for s in t.sources:
+ if isinstance(s, mesonlib.File):
+ s = os.path.join(s.subdir, s.fname)
+ self.filemap[s] = self.gen_id()
+ for o in t.objects:
+ if isinstance(o, str):
+ o = os.path.join(t.subdir, o)
+ self.filemap[o] = self.gen_id()
+ self.target_filemap[name] = self.gen_id()
+
+ def generate_buildmap(self):
+ self.buildmap = {}
+ for t in self.build.targets.values():
+ for s in t.sources:
+ s = os.path.join(s.subdir, s.fname)
+ self.buildmap[s] = self.gen_id()
+ for o in t.objects:
+ o = os.path.join(t.subdir, o)
+ if isinstance(o, str):
+ self.buildmap[o] = self.gen_id()
+
+ def generate_buildstylemap(self):
+ self.buildstylemap = {'debug' : self.gen_id()}
+
+ def generate_build_phase_map(self):
+ self.buildphasemap = {}
+ for t in self.build.targets:
+ self.buildphasemap[t] = self.gen_id()
+
+ def generate_build_configuration_map(self):
+ self.buildconfmap = {}
+ for t in self.build.targets:
+ bconfs = {'debug' : self.gen_id()}
+ self.buildconfmap[t] = bconfs
+
+ def generate_project_configurations_map(self):
+ self.project_configurations = {'debug' : self.gen_id()}
+
+ def generate_buildall_configurations_map(self):
+ self.buildall_configurations = {'debug' : self.gen_id()}
+
+ def generate_test_configurations_map(self):
+ self.test_configurations = {'debug' : self.gen_id()}
+
+ def generate_build_configurationlist_map(self):
+ self.buildconflistmap = {}
+ for t in self.build.targets:
+ self.buildconflistmap[t] = self.gen_id()
+
+ def generate_native_target_map(self):
+ self.native_targets = {}
+ for t in self.build.targets:
+ self.native_targets[t] = self.gen_id()
+
+ def generate_target_dependency_map(self):
+ self.target_dependency_map = {}
+ for tname, t in self.build.targets.items():
+ for target in t.link_targets:
+ self.target_dependency_map[(tname, target.get_basename())] = self.gen_id()
+
+ def generate_pbxdep_map(self):
+ self.pbx_dep_map = {}
+ for t in self.build.targets:
+ self.pbx_dep_map[t] = self.gen_id()
+
+ def generate_containerproxy_map(self):
+ self.containerproxy_map = {}
+ for t in self.build.targets:
+ self.containerproxy_map[t] = self.gen_id()
+
+ def generate_source_phase_map(self):
+ self.source_phase = {}
+ for t in self.build.targets:
+ self.source_phase[t] = self.gen_id()
+
+ def generate_pbx_aggregate_target(self):
+ self.ofile.write('\n/* Begin PBXAggregateTarget section */\n')
+ self.write_line('%s /* ALL_BUILD */ = {' % self.all_id)
+ self.indent_level+=1
+ self.write_line('isa = PBXAggregateTarget;')
+ self.write_line('buildConfigurationList = %s;' % self.all_buildconf_id)
+ self.write_line('buildPhases = (')
+ self.write_line(');')
+ self.write_line('dependencies = (')
+ self.indent_level+=1
+ for t in self.build.targets:
+ self.write_line('%s /* PBXTargetDependency */,' % self.pbx_dep_map[t])
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('name = ALL_BUILD;')
+ self.write_line('productName = ALL_BUILD;')
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('%s /* RUN_TESTS */ = {' % self.test_id)
+ self.indent_level +=1
+ self.write_line('isa = PBXAggregateTarget;')
+ self.write_line('buildConfigurationList = %s;' % self.test_buildconf_id)
+ self.write_line('buildPhases = (')
+ self.indent_level+=1
+ self.write_line('%s /* test run command */,' % self.test_command_id)
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('dependencies = (')
+ self.write_line(');')
+ self.write_line('name = RUN_TESTS;')
+ self.write_line('productName = RUN_TESTS;')
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXAggregateTarget section */\n')
+
+ def generate_pbx_build_file(self):
+ self.ofile.write('\n/* Begin PBXBuildFile section */\n')
+ templ = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */; settings = { COMPILER_FLAGS = "%s"; }; };\n'
+ otempl = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */;};\n'
+ for t in self.build.targets.values():
+ for s in t.sources:
+ if isinstance(s, str):
+ s = os.path.join(t.subdir, s)
+ idval = self.buildmap[s]
+ fullpath = os.path.join(self.environment.get_source_dir(), s)
+ fileref = self.filemap[s]
+ fullpath2 = fullpath
+ compiler_args = ''
+ self.ofile.write(templ % (idval, fullpath, fileref, fullpath2, compiler_args))
+ for o in t.objects:
+ o = os.path.join(t.subdir, o)
+ idval = self.buildmap[o]
+ fileref = self.filemap[o]
+ fullpath = os.path.join(self.environment.get_source_dir(), o)
+ fullpath2 = fullpath
+ self.ofile.write(otempl % (idval, fullpath, fileref, fullpath2))
+ self.ofile.write('/* End PBXBuildFile section */\n')
+
+ def generate_pbx_build_style(self):
+ self.ofile.write('\n/* Begin PBXBuildStyle section */\n')
+ for name, idval in self.buildstylemap.items():
+ self.write_line('%s /* %s */ = {\n' % (idval, name))
+ self.indent_level += 1
+ self.write_line('isa = PBXBuildStyle;\n')
+ self.write_line('buildSettings = {\n')
+ self.indent_level += 1
+ self.write_line('COPY_PHASE_STRIP = NO;\n')
+ self.indent_level -= 1
+ self.write_line('};\n')
+ self.write_line('name = %s;\n' % name)
+ self.indent_level -= 1
+ self.write_line('};\n')
+ self.ofile.write('/* End PBXBuildStyle section */\n')
+
+ def generate_pbx_container_item_proxy(self):
+ self.ofile.write('\n/* Begin PBXContainerItemProxy section */\n')
+ for t in self.build.targets:
+ self.write_line('%s /* PBXContainerItemProxy */ = {' % self.containerproxy_map[t])
+ self.indent_level += 1
+ self.write_line('isa = PBXContainerItemProxy;')
+ self.write_line('containerPortal = %s /* Project object */;' % self.project_uid)
+ self.write_line('proxyType = 1;')
+ self.write_line('remoteGlobalIDString = %s;' % self.native_targets[t])
+ self.write_line('remoteInfo = %s;' % t)
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXContainerItemProxy section */\n')
+
+ def generate_pbx_file_reference(self):
+ self.ofile.write('\n/* Begin PBXFileReference section */\n')
+ src_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; fileEncoding = 4; name = "%s"; path = "%s"; sourceTree = SOURCE_ROOT; };\n'
+ for fname, idval in self.filemap.items():
+ fullpath = os.path.join(self.environment.get_source_dir(), fname)
+ xcodetype = self.get_xcodetype(fname)
+ name = os.path.split(fname)[-1]
+ path = fname
+ self.ofile.write(src_templ % (idval, fullpath, xcodetype, name, path))
+ target_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; path = %s; refType = %d; sourceTree = BUILT_PRODUCTS_DIR; };\n'
+ for tname, idval in self.target_filemap.items():
+ t = self.build.targets[tname]
+ fname = t.get_filename()
+ reftype = 0
+ if isinstance(t, build.Executable):
+ typestr = 'compiled.mach-o.executable'
+ path = t.get_filename()
+ elif isinstance(t, build.SharedLibrary):
+ # OSX has a completely different shared library
+ # naming scheme so do this manually.
+ typestr = self.get_xcodetype('dummy.dylib')
+ path = t.get_osx_filename()
+ else:
+ typestr = self.get_xcodetype(fname)
+ path = '"%s"' % t.get_filename()
+ self.ofile.write(target_templ % (idval, tname, typestr, path, reftype))
+ self.ofile.write('/* End PBXFileReference section */\n')
+
+ def generate_pbx_group(self):
+ groupmap = {}
+ target_src_map = {}
+ for t in self.build.targets:
+ groupmap[t] = self.gen_id()
+ target_src_map[t] = self.gen_id()
+ self.ofile.write('\n/* Begin PBXGroup section */\n')
+ sources_id = self.gen_id()
+ resources_id = self.gen_id()
+ products_id = self.gen_id()
+ self.write_line('%s = {' % self.maingroup_id)
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.indent_level+=1
+ self.write_line('%s /* Sources */,' % sources_id)
+ self.write_line('%s /* Resources */,' % resources_id)
+ self.write_line('%s /* Products */,' % products_id)
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('sourceTree = "<group>";')
+ self.indent_level -= 1
+ self.write_line('};')
+
+ # Sources
+ self.write_line('%s /* Sources */ = {' % sources_id)
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.indent_level+=1
+ for t in self.build.targets:
+ self.write_line('%s /* %s */,' % (groupmap[t], t))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('name = Sources;')
+ self.write_line('sourcetree = "<group>";')
+ self.indent_level-=1
+ self.write_line('};')
+
+ self.write_line('%s /* Resources */ = {' % resources_id)
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.write_line(');')
+ self.write_line('name = Resources;')
+ self.write_line('sourceTree = "<group>";')
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Targets
+ for t in self.build.targets:
+ self.write_line('%s /* %s */ = {' % (groupmap[t], t))
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.indent_level+=1
+ self.write_line('%s /* Source files */,' % target_src_map[t])
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('name = %s;' % t)
+ self.write_line('sourceTree = "<group>";')
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('%s /* Source files */ = {' % target_src_map[t])
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.indent_level+=1
+ for s in self.build.targets[t].sources:
+ s = os.path.join(s.subdir, s.fname)
+ if isinstance(s, str):
+ self.write_line('%s /* %s */,' % (self.filemap[s], s))
+ for o in self.build.targets[t].objects:
+ o = os.path.join(self.build.targets[t].subdir, o)
+ self.write_line('%s /* %s */,' % (self.filemap[o], o))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('name = "Source files";')
+ self.write_line('sourceTree = "<group>";')
+ self.indent_level-=1
+ self.write_line('};')
+
+ # And finally products
+ self.write_line('%s /* Products */ = {' % products_id)
+ self.indent_level+=1
+ self.write_line('isa = PBXGroup;')
+ self.write_line('children = (')
+ self.indent_level+=1
+ for t in self.build.targets:
+ self.write_line('%s /* %s */,' % (self.target_filemap[t], t))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('name = Products;')
+ self.write_line('sourceTree = "<group>";')
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXGroup section */\n')
+
+ def generate_pbx_native_target(self):
+ self.ofile.write('\n/* Begin PBXNativeTarget section */\n')
+ for tname, idval in self.native_targets.items():
+ t = self.build.targets[tname]
+ self.write_line('%s /* %s */ = {' % (idval, tname))
+ self.indent_level+=1
+ self.write_line('isa = PBXNativeTarget;')
+ self.write_line('buildConfigurationList = %s /* Build configuration list for PBXNativeTarget "%s" */;'\
+ % (self.buildconflistmap[tname], tname))
+ self.write_line('buildPhases = (')
+ self.indent_level+=1
+ self.write_line('%s /* Sources */,' % self.buildphasemap[tname])
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('buildRules = (')
+ self.write_line(');')
+ self.write_line('dependencies = (')
+ self.indent_level+=1
+ for lt in self.build.targets[tname].link_targets:
+ # NOT DOCUMENTED, may need to make different links
+ # to same target have different targetdependency item.
+ idval = self.pbx_dep_map[lt.get_basename()]
+ self.write_line('%s /* PBXTargetDependency */,' % idval)
+ self.indent_level -=1
+ self.write_line(");")
+ self.write_line('name = %s;' % tname)
+ self.write_line('productName = %s;' % tname)
+ self.write_line('productReference = %s /* %s */;' % (self.target_filemap[tname], tname))
+ if isinstance(t, build.Executable):
+ typestr = 'com.apple.product-type.tool'
+ elif isinstance(t, build.StaticLibrary):
+ typestr = 'com.apple.product-type.library.static'
+ elif isinstance(t, build.SharedLibrary):
+ typestr = 'com.apple.product-type.library.dynamic'
+ else:
+ raise MesonException('Unknown target type for %s' % tname)
+ self.write_line('productType = "%s";' % typestr)
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXNativeTarget section */\n')
+
+ def generate_pbx_project(self):
+ self.ofile.write('\n/* Begin PBXProject section */\n')
+ self.write_line('%s /* Project object */ = {' % self.project_uid)
+ self.indent_level += 1
+ self.write_line('isa = PBXProject;')
+ self.write_line('attributes = {')
+ self.indent_level += 1
+ self.write_line('BuildIndependentTargetsInParallel = YES;')
+ self.indent_level -= 1
+ self.write_line('};')
+ conftempl = 'buildConfigurationList = %s /* build configuration list for PBXProject "%s"*/;'
+ self.write_line(conftempl % (self.project_conflist, self.build.project_name))
+ self.write_line('buildSettings = {')
+ self.write_line('};')
+ self.write_line('buildStyles = (')
+ self.indent_level += 1
+ for name, idval in self.buildstylemap.items():
+ self.write_line('%s /* %s */,' % (idval, name))
+ self.indent_level -= 1
+ self.write_line(');')
+ self.write_line('compatibilityVersion = "Xcode 3.2";')
+ self.write_line('hasScannedForEncodings = 0;')
+ self.write_line('mainGroup = %s;' % self.maingroup_id)
+ self.write_line('projectDirPath = "%s";' % self.build_to_src)
+ self.write_line('projectRoot = "";')
+ self.write_line('targets = (')
+ self.indent_level += 1
+ self.write_line('%s /* ALL_BUILD */,' % self.all_id)
+ self.write_line('%s /* RUN_TESTS */,' % self.test_id)
+ for t in self.build.targets:
+ self.write_line('%s /* %s */,' % (self.native_targets[t], t))
+ self.indent_level -= 1
+ self.write_line(');')
+ self.indent_level -= 1
+ self.write_line('};')
+ self.ofile.write('/* End PBXProject section */\n')
+
+ def generate_pbx_shell_build_phase(self):
+ self.ofile.write('\n/* Begin PBXShellScriptBuildPhase section */\n')
+ self.write_line('%s = {' % self.test_command_id)
+ self.indent_level += 1
+ self.write_line('isa = PBXShellScriptBuildPhase;')
+ self.write_line('buildActionMask = 2147483647;')
+ self.write_line('files = (')
+ self.write_line(');')
+ self.write_line('inputPaths = (')
+ self.write_line(');')
+ self.write_line('outputPaths = (')
+ self.write_line(');')
+ self.write_line('runOnlyForDeploymentPostprocessing = 0;')
+ self.write_line('shellPath = /bin/sh;')
+ script_root = self.environment.get_script_dir()
+ test_script = os.path.join(script_root, 'meson_test.py')
+ test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
+ cmd = [sys.executable, test_script, test_data, '--wd', self.environment.get_build_dir()]
+ cmdstr = ' '.join(["'%s'" % i for i in cmd])
+ self.write_line('shellScript = "%s";' % cmdstr)
+ self.write_line('showEnvVarsInLog = 0;')
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXShellScriptBuildPhase section */\n')
+
+ def generate_pbx_sources_build_phase(self):
+ self.ofile.write('\n/* Begin PBXSourcesBuildPhase section */\n')
+ for name, phase_id in self.source_phase.items():
+ self.write_line('%s /* Sources */ = {' % self.buildphasemap[name])
+ self.indent_level+=1
+ self.write_line('isa = PBXSourcesBuildPhase;')
+ self.write_line('buildActionMask = 2147483647;')
+ self.write_line('files = (')
+ self.indent_level+=1
+ for s in self.build.targets[name].sources:
+ s = os.path.join(s.subdir, s.fname)
+ if not self.environment.is_header(s):
+ self.write_line('%s /* %s */,' % (self.buildmap[s], os.path.join(self.environment.get_source_dir(), s)))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('runOnlyForDeploymentPostprocessing = 0;')
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXSourcesBuildPhase section */\n')
+
+ def generate_pbx_target_dependency(self):
+ self.ofile.write('\n/* Begin PBXTargetDependency section */\n')
+ for t in self.build.targets:
+ idval = self.pbx_dep_map[t] # VERIFY: is this correct?
+ self.write_line('%s /* PBXTargetDependency */ = {' % idval)
+ self.indent_level += 1
+ self.write_line('isa = PBXTargetDependency;')
+ self.write_line('target = %s /* %s */;' % (self.native_targets[t], t))
+ self.write_line('targetProxy = %s /* PBXContainerItemProxy */;' % self.containerproxy_map[t])
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End PBXTargetDependency section */\n')
+
+ def generate_xc_build_configuration(self):
+ self.ofile.write('\n/* Begin XCBuildConfiguration section */\n')
+ # First the setup for the toplevel project.
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */ = {' % (self.project_configurations[buildtype], buildtype))
+ self.indent_level+=1
+ self.write_line('isa = XCBuildConfiguration;')
+ self.write_line('buildSettings = {')
+ self.indent_level+=1
+ self.write_line('ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";')
+ self.write_line('ONLY_ACTIVE_ARCH = YES;')
+ self.write_line('SDKROOT = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk";')
+ self.write_line('SYMROOT = "%s/build";' % self.environment.get_build_dir())
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('name = %s;' % buildtype)
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Then the all target.
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */ = {' % (self.buildall_configurations[buildtype], buildtype))
+ self.indent_level+=1
+ self.write_line('isa = XCBuildConfiguration;')
+ self.write_line('buildSettings = {')
+ self.indent_level += 1
+ self.write_line('COMBINE_HIDPI_IMAGES = YES;')
+ self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;')
+ self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
+ self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
+ self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");')
+ self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
+ self.write_line('INSTALL_PATH = "";')
+ self.write_line('OTHER_CFLAGS = " ";')
+ self.write_line('OTHER_LDFLAGS = " ";')
+ self.write_line('OTHER_REZFLAGS = "";')
+ self.write_line('PRODUCT_NAME = ALL_BUILD;')
+ self.write_line('SECTORDER_FLAGS = "";')
+ self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir())
+ self.write_line('USE_HEADERMAP = NO;')
+ self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );')
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('name = %s;' % buildtype)
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Then the test target.
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */ = {' % (self.test_configurations[buildtype], buildtype))
+ self.indent_level+=1
+ self.write_line('isa = XCBuildConfiguration;')
+ self.write_line('buildSettings = {')
+ self.indent_level += 1
+ self.write_line('COMBINE_HIDPI_IMAGES = YES;')
+ self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;')
+ self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
+ self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
+ self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");')
+ self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
+ self.write_line('INSTALL_PATH = "";')
+ self.write_line('OTHER_CFLAGS = " ";')
+ self.write_line('OTHER_LDFLAGS = " ";')
+ self.write_line('OTHER_REZFLAGS = "";')
+ self.write_line('PRODUCT_NAME = RUN_TESTS;')
+ self.write_line('SECTORDER_FLAGS = "";')
+ self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir())
+ self.write_line('USE_HEADERMAP = NO;')
+ self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );')
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('name = %s;' % buildtype)
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Now finally targets.
+ langnamemap = {'c' : 'C', 'cpp' : 'CPLUSPLUS', 'objc' : 'OBJC', 'objcpp' : 'OBJCPLUSPLUS'}
+ for target_name, target in self.build.targets.items():
+ for buildtype in self.buildtypes:
+ dep_libs = []
+ links_dylib = False
+ headerdirs = []
+ for d in target.include_dirs:
+ for sd in d.incdirs:
+ cd = os.path.join(d.curdir, sd)
+ headerdirs.append(os.path.join(self.environment.get_source_dir(), cd))
+ headerdirs.append(os.path.join(self.environment.get_build_dir(), cd))
+ for l in target.link_targets:
+ abs_path = os.path.join(self.environment.get_build_dir(),
+ l.subdir, buildtype, l.get_osx_filename())
+ dep_libs.append("'%s'" % abs_path)
+ if isinstance(l, build.SharedLibrary):
+ links_dylib = True
+ if links_dylib:
+ dep_libs = ['-Wl,-search_paths_first', '-Wl,-headerpad_max_install_names'] + dep_libs
+ dylib_version = None
+ if isinstance(target, build.SharedLibrary):
+ ldargs = ['-dynamiclib', '-Wl,-headerpad_max_install_names'] + dep_libs
+ install_path = os.path.join(self.environment.get_build_dir(), target.subdir, buildtype)
+ dylib_version = target.version
+ else:
+ ldargs = dep_libs
+ install_path = ''
+ if dylib_version is not None:
+ product_name = target_name + '.' + dylib_version
+ else:
+ product_name = target_name
+ ldargs += target.link_args
+ ldstr = ' '.join(ldargs)
+ valid = self.buildconfmap[target_name][buildtype]
+ langargs = {}
+ for lang in self.environment.coredata.compilers:
+ if lang not in langnamemap:
+ continue
+ gargs = self.build.global_args.get(lang, [])
+ targs = target.get_extra_args(lang)
+ args = gargs + targs
+ if len(args) > 0:
+ langargs[langnamemap[lang]] = args
+ symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
+ self.write_line('%s /* %s */ = {' % (valid, buildtype))
+ self.indent_level+=1
+ self.write_line('isa = XCBuildConfiguration;')
+ self.write_line('buildSettings = {')
+ self.indent_level += 1
+ self.write_line('COMBINE_HIDPI_IMAGES = YES;')
+ if dylib_version is not None:
+ self.write_line('DYLIB_CURRENT_VERSION = "%s";' % dylib_version)
+ self.write_line('EXECUTABLE_PREFIX = "%s";' % target.prefix)
+ if target.suffix == '':
+ suffix = ''
+ else:
+ suffix = '.' + target.suffix
+ self.write_line('EXECUTABLE_SUFFIX = "%s";' % suffix)
+ self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = YES;')
+ self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
+ self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
+ self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");')
+ self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
+ if len(headerdirs) > 0:
+ quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs])
+ self.write_line('HEADER_SEARCH_PATHS=(%s);' % quotedh)
+ self.write_line('INSTALL_PATH = "%s";' % install_path)
+ self.write_line('LIBRARY_SEARCH_PATHS = "";')
+ if isinstance(target, build.SharedLibrary):
+ self.write_line('LIBRARY_STYLE = DYNAMIC;')
+ for langname, args in langargs.items():
+ argstr = ' '.join(args)
+ self.write_line('OTHER_%sFLAGS = "%s";' % (langname, argstr))
+ self.write_line('OTHER_LDFLAGS = "%s";' % ldstr)
+ self.write_line('OTHER_REZFLAGS = "";')
+ self.write_line('PRODUCT_NAME = %s;' % product_name)
+ self.write_line('SECTORDER_FLAGS = "";')
+ self.write_line('SYMROOT = "%s";' % symroot)
+ self.write_line('USE_HEADERMAP = NO;')
+ self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );')
+ self.indent_level-=1
+ self.write_line('};')
+ self.write_line('name = %s;' % buildtype)
+ self.indent_level-=1
+ self.write_line('};')
+ self.ofile.write('/* End XCBuildConfiguration section */\n')
+
+ def generate_xc_configurationList(self):
+ self.ofile.write('\n/* Begin XCConfigurationList section */\n')
+ self.write_line('%s /* Build configuration list for PBXProject "%s" */ = {' % (self.project_conflist, self.build.project_name))
+ self.indent_level+=1
+ self.write_line('isa = XCConfigurationList;')
+ self.write_line('buildConfigurations = (')
+ self.indent_level+=1
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */,' % (self.project_configurations[buildtype], buildtype))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('defaultConfigurationIsVisible = 0;')
+ self.write_line('defaultConfigurationName = debug;')
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Now the all target
+ self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.all_buildconf_id)
+ self.indent_level+=1
+ self.write_line('isa = XCConfigurationList;')
+ self.write_line('buildConfigurations = (')
+ self.indent_level+=1
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */,' % (self.buildall_configurations[buildtype], buildtype))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('defaultConfigurationIsVisible = 0;')
+ self.write_line('defaultConfigurationName = debug;')
+ self.indent_level-=1
+ self.write_line('};')
+
+ # Test target
+ self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.test_buildconf_id)
+ self.indent_level+=1
+ self.write_line('isa = XCConfigurationList;')
+ self.write_line('buildConfigurations = (')
+ self.indent_level+=1
+ for buildtype in self.buildtypes:
+ self.write_line('%s /* %s */,' % (self.test_configurations[buildtype], buildtype))
+ self.indent_level-=1
+ self.write_line(');')
+ self.write_line('defaultConfigurationIsVisible = 0;')
+ self.write_line('defaultConfigurationName = debug;')
+ self.indent_level-=1
+ self.write_line('};')
+
+ for target_name in self.build.targets:
+ listid = self.buildconflistmap[target_name]
+ self.write_line('%s /* Build configuration list for PBXNativeTarget "%s" */ = {' % (listid, target_name))
+ self.indent_level += 1
+ self.write_line('isa = XCConfigurationList;')
+ self.write_line('buildConfigurations = (')
+ self.indent_level += 1
+ typestr = 'debug'
+ idval = self.buildconfmap[target_name][typestr]
+ self.write_line('%s /* %s */,' % (idval, typestr))
+ self.indent_level -= 1
+ self.write_line(');')
+ self.write_line('defaultConfigurationIsVisible = 0;')
+ self.write_line('defaultConfigurationName = %s;' % typestr)
+ self.indent_level -= 1
+ self.write_line('};')
+ self.ofile.write('/* End XCConfigurationList section */\n')
+
+ def generate_prefix(self):
+ self.ofile.write('// !$*UTF8*$!\n{\n')
+ self.indent_level += 1
+ self.write_line('archiveVersion = 1;\n')
+ self.write_line('classes = {\n')
+ self.write_line('};\n')
+ self.write_line('objectVersion = 46;\n')
+ self.write_line('objects = {\n')
+ self.indent_level += 1
+
+ def generate_suffix(self):
+ self.indent_level -= 1
+ self.write_line('};\n')
+ self.write_line('rootObject = ' + self.project_uid + ';')
+ self.indent_level -= 1
+ self.write_line('}\n')