From 8b1039fa30a405e2d07ac70eb0284ee4654c619a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 15 Jan 2016 21:12:23 +0200 Subject: Organise files into a module structure. --- backends.py | 408 -------- build.py | 969 ------------------- commandrunner.py | 55 -- compilers.py | 1837 ------------------------------------ coredata.py | 222 ----- delwithsuffix.py | 32 - dependencies.py | 1120 ---------------------- depfixer.py | 299 ------ dirchanger.py | 26 - environment.py | 673 ------------- gtkdochelper.py | 118 --- interpreter.py | 2250 ------------------------------------------- meson.py | 225 +---- meson/__init__.py | 0 meson/backends.py | 408 ++++++++ meson/build.py | 969 +++++++++++++++++++ meson/commandrunner.py | 55 ++ meson/compilers.py | 1837 ++++++++++++++++++++++++++++++++++++ meson/coredata.py | 222 +++++ meson/delwithsuffix.py | 32 + meson/dependencies.py | 1120 ++++++++++++++++++++++ meson/depfixer.py | 299 ++++++ meson/dirchanger.py | 26 + meson/environment.py | 673 +++++++++++++ meson/gtkdochelper.py | 118 +++ meson/interpreter.py | 2251 ++++++++++++++++++++++++++++++++++++++++++++ meson/meson_benchmark.py | 97 ++ meson/meson_install.py | 227 +++++ meson/meson_test.py | 232 +++++ meson/mesonconf.py | 205 ++++ meson/mesongui.py | 561 +++++++++++ meson/mesonintrospect.py | 208 ++++ meson/mesonlib.py | 284 ++++++ meson/mesonmain.py | 225 +++++ meson/mlog.py | 81 ++ meson/modules/__init__.py | 0 meson/modules/gnome.py | 330 +++++++ meson/modules/modtest.py | 21 + meson/modules/pkgconfig.py | 82 ++ meson/modules/qt4.py | 155 +++ meson/modules/qt5.py | 162 ++++ meson/modules/rpm.py | 163 ++++ meson/modules/windows.py | 47 + meson/mparser.py | 565 +++++++++++ meson/ninjabackend.py | 1815 +++++++++++++++++++++++++++++++++++ meson/optinterpreter.py | 148 +++ meson/regen_checker.py | 42 + meson/symbolextractor.py | 102 ++ meson/vcstagger.py | 33 + meson/vs2010backend.py | 640 +++++++++++++ meson/wrap.py | 178 ++++ meson/wraptool.py | 233 +++++ meson/xcodebackend.py | 775 +++++++++++++++ meson_benchmark.py | 97 -- meson_install.py | 227 ----- meson_test.py | 228 ----- mesonconf.py | 205 ---- mesongui.py | 561 ----------- mesonintrospect.py | 208 ---- mesonlib.py | 284 ------ mlog.py | 81 -- modules/gnome.py | 330 ------- modules/modtest.py | 21 - modules/pkgconfig.py | 82 -- modules/qt4.py | 155 --- modules/qt5.py | 162 ---- modules/rpm.py | 163 ---- modules/windows.py | 47 - mparser.py | 565 ----------- ninjabackend.py | 1815 ----------------------------------- optinterpreter.py | 148 --- regen_checker.py | 42 - run_tests.py | 10 +- symbolextractor.py | 102 -- vcstagger.py | 33 - vs2010backend.py | 640 ------------- wrap.py | 178 ---- wraptool.py | 233 ----- xcodebackend.py | 775 --------------- 79 files changed, 15629 insertions(+), 15618 deletions(-) delete mode 100644 backends.py delete mode 100644 build.py delete mode 100755 commandrunner.py delete mode 100644 compilers.py delete mode 100644 coredata.py delete mode 100755 delwithsuffix.py delete mode 100644 dependencies.py delete mode 100755 depfixer.py delete mode 100755 dirchanger.py delete mode 100644 environment.py delete mode 100755 gtkdochelper.py delete mode 100644 interpreter.py create mode 100644 meson/__init__.py create mode 100644 meson/backends.py create mode 100644 meson/build.py create mode 100755 meson/commandrunner.py create mode 100644 meson/compilers.py create mode 100644 meson/coredata.py create mode 100755 meson/delwithsuffix.py create mode 100644 meson/dependencies.py create mode 100755 meson/depfixer.py create mode 100755 meson/dirchanger.py create mode 100644 meson/environment.py create mode 100755 meson/gtkdochelper.py create mode 100644 meson/interpreter.py create mode 100755 meson/meson_benchmark.py create mode 100755 meson/meson_install.py create mode 100755 meson/meson_test.py create mode 100755 meson/mesonconf.py create mode 100755 meson/mesongui.py create mode 100755 meson/mesonintrospect.py create mode 100644 meson/mesonlib.py create mode 100755 meson/mesonmain.py create mode 100644 meson/mlog.py create mode 100644 meson/modules/__init__.py create mode 100644 meson/modules/gnome.py create mode 100644 meson/modules/modtest.py create mode 100644 meson/modules/pkgconfig.py create mode 100644 meson/modules/qt4.py create mode 100644 meson/modules/qt5.py create mode 100644 meson/modules/rpm.py create mode 100644 meson/modules/windows.py create mode 100644 meson/mparser.py create mode 100644 meson/ninjabackend.py create mode 100644 meson/optinterpreter.py create mode 100755 meson/regen_checker.py create mode 100755 meson/symbolextractor.py create mode 100755 meson/vcstagger.py create mode 100644 meson/vs2010backend.py create mode 100644 meson/wrap.py create mode 100755 meson/wraptool.py create mode 100644 meson/xcodebackend.py delete mode 100755 meson_benchmark.py delete mode 100755 meson_install.py delete mode 100755 meson_test.py delete mode 100755 mesonconf.py delete mode 100755 mesongui.py delete mode 100755 mesonintrospect.py delete mode 100644 mesonlib.py delete mode 100644 mlog.py delete mode 100644 modules/gnome.py delete mode 100644 modules/modtest.py delete mode 100644 modules/pkgconfig.py delete mode 100644 modules/qt4.py delete mode 100644 modules/qt5.py delete mode 100644 modules/rpm.py delete mode 100644 modules/windows.py delete mode 100644 mparser.py delete mode 100644 ninjabackend.py delete mode 100644 optinterpreter.py delete mode 100755 regen_checker.py delete mode 100755 symbolextractor.py delete mode 100755 vcstagger.py delete mode 100644 vs2010backend.py delete mode 100644 wrap.py delete mode 100755 wraptool.py delete mode 100644 xcodebackend.py diff --git a/backends.py b/backends.py deleted file mode 100644 index 748a742..0000000 --- a/backends.py +++ /dev/null @@ -1,408 +0,0 @@ -# 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 -import build -import dependencies -import mesonlib -import json -from coredata import MesonException - -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/build.py b/build.py deleted file mode 100644 index 7dededa..0000000 --- a/build.py +++ /dev/null @@ -1,969 +0,0 @@ -# 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 coredata -import environment -import dependencies -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/commandrunner.py b/commandrunner.py deleted file mode 100755 index 0dad585..0000000 --- a/commandrunner.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/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) - -if __name__ == '__main__': - if len(sys.argv) < 5: - print(sys.argv[0], ' [arguments]') - 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() - sys.exit(pc.returncode) diff --git a/compilers.py b/compilers.py deleted file mode 100644 index 55b41dc..0000000 --- a/compilers.py +++ /dev/null @@ -1,1837 +0,0 @@ -# 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 -import mesonlib -import mlog -from coredata import MesonException -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 -%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 -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 -#include - -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\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\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/coredata.py b/coredata.py deleted file mode 100644 index 5b1102c..0000000 --- a/coredata.py +++ /dev/null @@ -1,222 +0,0 @@ -# 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/delwithsuffix.py b/delwithsuffix.py deleted file mode 100755 index 4b8a60d..0000000 --- a/delwithsuffix.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/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 - -if len(sys.argv) != 3: - print('%s ' % sys.argv[0]) - sys.exit(1) - -topdir = sys.argv[1] -suffix = sys.argv[2] -if suffix[0] != '.': - suffix = '.' + suffix - -for (root, dirs, files) in os.walk(topdir): - for f in files: - if f.endswith(suffix): - fullname = os.path.join(root, f) - os.unlink(fullname) diff --git a/dependencies.py b/dependencies.py deleted file mode 100644 index cc9a941..0000000 --- a/dependencies.py +++ /dev/null @@ -1,1120 +0,0 @@ -# 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 -import mlog -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/depfixer.py b/depfixer.py deleted file mode 100755 index 4f7ce3d..0000000 --- a/depfixer.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/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 - -if __name__ == '__main__': - if len(sys.argv) < 2 or len(sys.argv) > 3: - print('This application resets target rpath.') - print('Don\'t run this unless you know what you are doing.') - print('%s: ' % sys.argv[0]) - exit(1) - e = Elf(sys.argv[1]) - if len(sys.argv) == 2: - e.print_rpath() - else: - new_rpath = sys.argv[2] - e.fix_rpath(new_rpath.encode('utf8')) - #e.fix_deps(prefix.encode()) diff --git a/dirchanger.py b/dirchanger.py deleted file mode 100755 index fd3dc23..0000000 --- a/dirchanger.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 - -dirname = sys.argv[1] -command = sys.argv[2:] - -os.chdir(dirname) -sys.exit(subprocess.call(command)) diff --git a/environment.py b/environment.py deleted file mode 100644 index e905b2f..0000000 --- a/environment.py +++ /dev/null @@ -1,673 +0,0 @@ -# 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, re, subprocess -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.dirname(self.meson_script_file) - - 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/gtkdochelper.py b/gtkdochelper.py deleted file mode 100755 index 7e476b8..0000000 --- a/gtkdochelper.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - options = parser.parse_args(sys.argv[1:]) - 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) diff --git a/interpreter.py b/interpreter.py deleted file mode 100644 index 21cc6cf..0000000 --- a/interpreter.py +++ /dev/null @@ -1,2250 +0,0 @@ -# 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 mparser -import environment -import coredata -import dependencies -import mlog -import build -import optinterpreter -import wrap -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('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 - scriptfile = os.path.join(self.environment.get_script_dir(), 'vcstagger.py') - # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command... - kwargs['command'] = [sys.executable, scriptfile, '@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/meson.py b/meson.py index 0ba3447..94d6e22 100755 --- a/meson.py +++ b/meson.py @@ -1,225 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2012-2015 The Meson development team +from meson import mesonmain +import sys -# 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 -import environment, interpreter, mesonlib -import build -import platform -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': - import ninjabackend - g = ninjabackend.NinjaBackend(b) - elif self.options.backend == 'vs2010': - import vs2010backend - g = vs2010backend.Vs2010Backend(b) - elif self.options.backend == 'xcode': - 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(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[-1] == 'secret-handshake': - args = args[:-1] - handshake = True - else: - handshake = False - args = mesonlib.expand_arguments(args) - if not args: - return 1 - options = parser.parse_args(args[1:]) - if options.print_version: - print(coredata.version) - return 0 - args = options.directories - if len(args) == 0 or len(args) > 2: - print('%s ' % 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 = '.' - this_file = os.path.abspath(__file__) - while os.path.islink(this_file): - resolved = os.readlink(this_file) - if resolved[0] != '/': - this_file = os.path.join(os.path.dirname(this_file), resolved) - else: - this_file = resolved - - try: - app = MesonApp(dir1, dir2, this_file, 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 - -if __name__ == '__main__': - sys.exit(run(sys.argv[:])) +sys.exit(mesonmain.run(sys.argv[:])) diff --git a/meson/__init__.py b/meson/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meson/backends.py b/meson/backends.py new file mode 100644 index 0000000..2706368 --- /dev/null +++ b/meson/backends.py @@ -0,0 +1,408 @@ +# 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 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/meson/build.py b/meson/build.py new file mode 100644 index 0000000..c0ba895 --- /dev/null +++ b/meson/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/meson/commandrunner.py b/meson/commandrunner.py new file mode 100755 index 0000000..0dad585 --- /dev/null +++ b/meson/commandrunner.py @@ -0,0 +1,55 @@ +#!/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) + +if __name__ == '__main__': + if len(sys.argv) < 5: + print(sys.argv[0], ' [arguments]') + 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() + sys.exit(pc.returncode) diff --git a/meson/compilers.py b/meson/compilers.py new file mode 100644 index 0000000..ec0181e --- /dev/null +++ b/meson/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 +%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 +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 +#include + +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\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\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/meson/coredata.py b/meson/coredata.py new file mode 100644 index 0000000..5b1102c --- /dev/null +++ b/meson/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/meson/delwithsuffix.py b/meson/delwithsuffix.py new file mode 100755 index 0000000..4b8a60d --- /dev/null +++ b/meson/delwithsuffix.py @@ -0,0 +1,32 @@ +#!/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 + +if len(sys.argv) != 3: + print('%s ' % sys.argv[0]) + sys.exit(1) + +topdir = sys.argv[1] +suffix = sys.argv[2] +if suffix[0] != '.': + suffix = '.' + suffix + +for (root, dirs, files) in os.walk(topdir): + for f in files: + if f.endswith(suffix): + fullname = os.path.join(root, f) + os.unlink(fullname) diff --git a/meson/dependencies.py b/meson/dependencies.py new file mode 100644 index 0000000..974559f --- /dev/null +++ b/meson/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/meson/depfixer.py b/meson/depfixer.py new file mode 100755 index 0000000..4f7ce3d --- /dev/null +++ b/meson/depfixer.py @@ -0,0 +1,299 @@ +#!/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 + +if __name__ == '__main__': + if len(sys.argv) < 2 or len(sys.argv) > 3: + print('This application resets target rpath.') + print('Don\'t run this unless you know what you are doing.') + print('%s: ' % sys.argv[0]) + exit(1) + e = Elf(sys.argv[1]) + if len(sys.argv) == 2: + e.print_rpath() + else: + new_rpath = sys.argv[2] + e.fix_rpath(new_rpath.encode('utf8')) + #e.fix_deps(prefix.encode()) diff --git a/meson/dirchanger.py b/meson/dirchanger.py new file mode 100755 index 0000000..fd3dc23 --- /dev/null +++ b/meson/dirchanger.py @@ -0,0 +1,26 @@ +#!/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 + +dirname = sys.argv[1] +command = sys.argv[2:] + +os.chdir(dirname) +sys.exit(subprocess.call(command)) diff --git a/meson/environment.py b/meson/environment.py new file mode 100644 index 0000000..3e10bd4 --- /dev/null +++ b/meson/environment.py @@ -0,0 +1,673 @@ +# 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, 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.dirname(self.meson_script_file) + + 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/meson/gtkdochelper.py b/meson/gtkdochelper.py new file mode 100755 index 0000000..7e476b8 --- /dev/null +++ b/meson/gtkdochelper.py @@ -0,0 +1,118 @@ +#!/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 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) + +if __name__ == '__main__': + options = parser.parse_args(sys.argv[1:]) + 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) diff --git a/meson/interpreter.py b/meson/interpreter.py new file mode 100644 index 0000000..ec1bda0 --- /dev/null +++ b/meson/interpreter.py @@ -0,0 +1,2251 @@ +# 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 . 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('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 + scriptfile = os.path.join(self.environment.get_script_dir(), 'vcstagger.py') + # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command... + kwargs['command'] = [sys.executable, scriptfile, '@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/meson/meson_benchmark.py b/meson/meson_benchmark.py new file mode 100755 index 0000000..26f1f95 --- /dev/null +++ b/meson/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/meson/meson_install.py b/meson/meson_install.py new file mode 100755 index 0000000..c5c17ba --- /dev/null +++ b/meson/meson_install.py @@ -0,0 +1,227 @@ +#!/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 + +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 = [] + +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) + +if __name__ == '__main__': + if len(sys.argv) != 2: + print('Installer script for Meson. Do not run on your own, mmm\'kay?') + print('%s [install info file]' % sys.argv[0]) + datafilename = sys.argv[1] + do_install(datafilename) + diff --git a/meson/meson_test.py b/meson/meson_test.py new file mode 100755 index 0000000..d9b4993 --- /dev/null +++ b/meson/meson_test.py @@ -0,0 +1,232 @@ +#!/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, 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/meson/mesonconf.py b/meson/mesonconf.py new file mode 100755 index 0000000..e53875f --- /dev/null +++ b/meson/mesonconf.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +# 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 sys, os +import pickle +import argparse +import coredata, mesonlib +from coredata import build_types, layouts, 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) + +if __name__ == '__main__': + args = mesonlib.expand_arguments(sys.argv[:]) + if not args: + sys.exit(1) + options = parser.parse_args(args[1:]) + if len(options.directory) > 1: + print('%s ' % sys.argv[0]) + print('If you omit the build directory, the current directory is substituted.') + sys.exit(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) + diff --git a/meson/mesongui.py b/meson/mesongui.py new file mode 100755 index 0000000..bdd44bb --- /dev/null +++ b/meson/mesongui.py @@ -0,0 +1,561 @@ +#!/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 +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.buildtype) + combo.currentTextChanged.connect(self.build_type_changed) + self.form.addRow('Build type', combo) + strip = QCheckBox("") + strip.setChecked(self.coredata.strip) + strip.stateChanged.connect(self.strip_changed) + self.form.addRow('Strip on install', strip) + coverage = QCheckBox("") + coverage.setChecked(self.coredata.coverage) + coverage.stateChanged.connect(self.coverage_changed) + self.form.addRow('Enable coverage', coverage) + pch = QCheckBox("") + pch.setChecked(self.coredata.use_pch) + pch.stateChanged.connect(self.pch_changed) + self.form.addRow('Enable pch', pch) + unity = QCheckBox("") + unity.setChecked(self.coredata.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 + +if __name__ == '__main__': + app = QApplication(sys.argv) + if len(sys.argv) == 1: + arg = "" + elif len(sys.argv) == 2: + arg = sys.argv[1] + else: + print(sys.argv[0], "") + sys.exit(1) + if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): + guirespawner = MesonGuiRespawner(arg) + else: + runner = Starter(arg) + sys.exit(app.exec_()) diff --git a/meson/mesonintrospect.py b/meson/mesonintrospect.py new file mode 100755 index 0000000..9fcd4db --- /dev/null +++ b/meson/mesonintrospect.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 + +# 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. + +"""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 +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)) + +if __name__ == '__main__': + options = parser.parse_args() + if len(options.args) > 1: + print('Too many arguments') + sys.exit(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') + sys.exit(1) diff --git a/meson/mesonlib.py b/meson/mesonlib.py new file mode 100644 index 0000000..2ab5ce4 --- /dev/null +++ b/meson/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/meson/mesonmain.py b/meson/mesonmain.py new file mode 100755 index 0000000..c4a9ada --- /dev/null +++ b/meson/mesonmain.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 + +# 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 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(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[-1] == 'secret-handshake': + args = args[:-1] + handshake = True + else: + handshake = False + args = mesonlib.expand_arguments(args) + if not args: + return 1 + options = parser.parse_args(args[1:]) + if options.print_version: + print(coredata.version) + return 0 + args = options.directories + if len(args) == 0 or len(args) > 2: + print('%s ' % 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 = '.' + this_file = os.path.abspath(__file__) + while os.path.islink(this_file): + resolved = os.readlink(this_file) + if resolved[0] != '/': + this_file = os.path.join(os.path.dirname(this_file), resolved) + else: + this_file = resolved + + try: + app = MesonApp(dir1, dir2, this_file, 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 + +if __name__ == '__main__': + sys.exit(run(sys.argv[:])) diff --git a/meson/mlog.py b/meson/mlog.py new file mode 100644 index 0000000..2807c2b --- /dev/null +++ b/meson/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/meson/modules/__init__.py b/meson/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meson/modules/gnome.py b/meson/modules/gnome.py new file mode 100644 index 0000000..60c994e --- /dev/null +++ b/meson/modules/gnome.py @@ -0,0 +1,330 @@ +# 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 Gnome/GLib related +functionality such as gobject-introspection and gresources.''' + +import build +import os, sys +import subprocess +from coredata import MesonException +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/meson/modules/modtest.py b/meson/modules/modtest.py new file mode 100644 index 0000000..c9247e6 --- /dev/null +++ b/meson/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/meson/modules/pkgconfig.py b/meson/modules/pkgconfig.py new file mode 100644 index 0000000..5fa4bd0 --- /dev/null +++ b/meson/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. + +import coredata, build +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/meson/modules/qt4.py b/meson/modules/qt4.py new file mode 100644 index 0000000..bcf42a3 --- /dev/null +++ b/meson/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. + +import dependencies, mlog +import os, subprocess +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/meson/modules/qt5.py b/meson/modules/qt5.py new file mode 100644 index 0000000..0e732a1 --- /dev/null +++ b/meson/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. + +import dependencies, mlog +import os, subprocess +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/meson/modules/rpm.py b/meson/modules/rpm.py new file mode 100644 index 0000000..eebecdd --- /dev/null +++ b/meson/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.''' + +import build +import compilers +import datetime +import mlog +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 - \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/meson/modules/windows.py b/meson/modules/windows.py new file mode 100644 index 0000000..e01b454 --- /dev/null +++ b/meson/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. + +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/meson/mparser.py b/meson/mparser.py new file mode 100644 index 0000000..1d569d5 --- /dev/null +++ b/meson/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/meson/ninjabackend.py b/meson/ninjabackend.py new file mode 100644 index 0000000..53b59a1 --- /dev/null +++ b/meson/ninjabackend.py @@ -0,0 +1,1815 @@ +# 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 backends +from . import environment, mesonlib +from . import build +from . import mlog +from . import dependencies +from .mesonlib import File +from .meson_install 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 +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 = os.path.join(script_root, 'depfixer.py') + 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, install_script, 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_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] + 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, benchmark_script, 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, + 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 --backend ninja secret-handshake\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 $CROSS\n' % (ninja_quote(sys.executable), + ninja_quote(os.path.join(scriptdir, 'symbolextractor.py')), + '$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/meson/optinterpreter.py b/meson/optinterpreter.py new file mode 100644 index 0000000..f0c93ae --- /dev/null +++ b/meson/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/meson/regen_checker.py b/meson/regen_checker.py new file mode 100755 index 0000000..a0fe028 --- /dev/null +++ b/meson/regen_checker.py @@ -0,0 +1,42 @@ +#!/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 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) + +if __name__ == '__main__': + regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) + if need_regen(regeninfo): + regen(regeninfo) + sys.exit(0) diff --git a/meson/symbolextractor.py b/meson/symbolextractor.py new file mode 100755 index 0000000..f2c709d --- /dev/null +++ b/meson/symbolextractor.py @@ -0,0 +1,102 @@ +#!/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. + +# 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 +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) + +if __name__ == '__main__': + options = parser.parse_args() + if len(options.args) != 2: + print(sys.argv[0], ' ') + sys.exit(1) + libfile = options.args[0] + outfile = options.args[1] + gen_symbols(libfile, outfile, options.cross_host) diff --git a/meson/vcstagger.py b/meson/vcstagger.py new file mode 100755 index 0000000..ccc584e --- /dev/null +++ b/meson/vcstagger.py @@ -0,0 +1,33 @@ +#!/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 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) + +if __name__ == '__main__': + infile, outfile, fallback, source_dir, replace_string, regex_selector = sys.argv[1:7] + command = sys.argv[7:] + config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/meson/vs2010backend.py b/meson/vs2010backend.py new file mode 100644 index 0000000..76e024c --- /dev/null +++ b/meson/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 +import backends, build +import dependencies +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 ['"%s"' % 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 " into &quot; whereas MSVS + # requires quoted but not fixed elements. Enter horrible hack. + txt = open(ofname, 'r').read() + open(ofname, 'w').write(txt.replace('&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/meson/wrap.py b/meson/wrap.py new file mode 100644 index 0000000..e55d3db --- /dev/null +++ b/meson/wrap.py @@ -0,0 +1,178 @@ +# 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 + +from . import wraptool + +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 = wraptool.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/meson/wraptool.py b/meson/wraptool.py new file mode 100755 index 0000000..2366b78 --- /dev/null +++ b/meson/wraptool.py @@ -0,0 +1,233 @@ +#!/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 urllib.request, json +import sys, os +import configparser +import shutil +import platform +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/' + + +ssl_warning_printed = False + +from glob import glob + +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 [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 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) + +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)) + +if __name__ == '__main__': + if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': + print_help() + sys.exit(0) + command = sys.argv[1] + args = sys.argv[2:] + if command == 'list': + list_projects() + elif command == 'search': + if len(args) != 1: + print('Search requires exactly one argument.') + sys.exit(1) + search(args[0]) + elif command == 'install': + if len(args) != 1: + print('Install requires exactly one argument.') + sys.exit(1) + install(args[0]) + elif command == 'update': + if len(args) != 1: + print('update requires exactly one argument.') + sys.exit(1) + update(args[0]) + elif command == 'info': + if len(args) != 1: + print('info requires exactly one argument.') + sys.exit(1) + info(args[0]) + elif command == 'status': + status() + else: + print('Unknown command', command) + sys.exit(1) + diff --git a/meson/xcodebackend.py b/meson/xcodebackend.py new file mode 100644 index 0000000..d72cbdf --- /dev/null +++ b/meson/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. + +import backends, build +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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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') diff --git a/meson_benchmark.py b/meson_benchmark.py deleted file mode 100755 index 4888d9f..0000000 --- a/meson_benchmark.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/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 -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/meson_install.py b/meson_install.py deleted file mode 100755 index c5c17ba..0000000 --- a/meson_install.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/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 - -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 = [] - -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) - -if __name__ == '__main__': - if len(sys.argv) != 2: - print('Installer script for Meson. Do not run on your own, mmm\'kay?') - print('%s [install info file]' % sys.argv[0]) - datafilename = sys.argv[1] - do_install(datafilename) - diff --git a/meson_test.py b/meson_test.py deleted file mode 100755 index 7758e5a..0000000 --- a/meson_test.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/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, subprocess, time, datetime, pickle, multiprocessing, json -import concurrent.futures as conc -import argparse -import mesonlib - -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 mesonlib.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/mesonconf.py b/mesonconf.py deleted file mode 100755 index e53875f..0000000 --- a/mesonconf.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 - -# 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 sys, os -import pickle -import argparse -import coredata, mesonlib -from coredata import build_types, layouts, 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) - -if __name__ == '__main__': - args = mesonlib.expand_arguments(sys.argv[:]) - if not args: - sys.exit(1) - options = parser.parse_args(args[1:]) - if len(options.directory) > 1: - print('%s ' % sys.argv[0]) - print('If you omit the build directory, the current directory is substituted.') - sys.exit(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) - diff --git a/mesongui.py b/mesongui.py deleted file mode 100755 index bdd44bb..0000000 --- a/mesongui.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/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 -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.buildtype) - combo.currentTextChanged.connect(self.build_type_changed) - self.form.addRow('Build type', combo) - strip = QCheckBox("") - strip.setChecked(self.coredata.strip) - strip.stateChanged.connect(self.strip_changed) - self.form.addRow('Strip on install', strip) - coverage = QCheckBox("") - coverage.setChecked(self.coredata.coverage) - coverage.stateChanged.connect(self.coverage_changed) - self.form.addRow('Enable coverage', coverage) - pch = QCheckBox("") - pch.setChecked(self.coredata.use_pch) - pch.stateChanged.connect(self.pch_changed) - self.form.addRow('Enable pch', pch) - unity = QCheckBox("") - unity.setChecked(self.coredata.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 - -if __name__ == '__main__': - app = QApplication(sys.argv) - if len(sys.argv) == 1: - arg = "" - elif len(sys.argv) == 2: - arg = sys.argv[1] - else: - print(sys.argv[0], "") - sys.exit(1) - if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): - guirespawner = MesonGuiRespawner(arg) - else: - runner = Starter(arg) - sys.exit(app.exec_()) diff --git a/mesonintrospect.py b/mesonintrospect.py deleted file mode 100755 index 9fcd4db..0000000 --- a/mesonintrospect.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -"""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 -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)) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) > 1: - print('Too many arguments') - sys.exit(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') - sys.exit(1) diff --git a/mesonlib.py b/mesonlib.py deleted file mode 100644 index 99d6154..0000000 --- a/mesonlib.py +++ /dev/null @@ -1,284 +0,0 @@ -# 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/mlog.py b/mlog.py deleted file mode 100644 index 2807c2b..0000000 --- a/mlog.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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/modules/gnome.py b/modules/gnome.py deleted file mode 100644 index 60c994e..0000000 --- a/modules/gnome.py +++ /dev/null @@ -1,330 +0,0 @@ -# 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 Gnome/GLib related -functionality such as gobject-introspection and gresources.''' - -import build -import os, sys -import subprocess -from coredata import MesonException -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/modules/modtest.py b/modules/modtest.py deleted file mode 100644 index c9247e6..0000000 --- a/modules/modtest.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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/modules/pkgconfig.py b/modules/pkgconfig.py deleted file mode 100644 index 5fa4bd0..0000000 --- a/modules/pkgconfig.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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 coredata, build -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/modules/qt4.py b/modules/qt4.py deleted file mode 100644 index bcf42a3..0000000 --- a/modules/qt4.py +++ /dev/null @@ -1,155 +0,0 @@ -# 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 dependencies, mlog -import os, subprocess -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/modules/qt5.py b/modules/qt5.py deleted file mode 100644 index 0e732a1..0000000 --- a/modules/qt5.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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 dependencies, mlog -import os, subprocess -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/modules/rpm.py b/modules/rpm.py deleted file mode 100644 index eebecdd..0000000 --- a/modules/rpm.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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.''' - -import build -import compilers -import datetime -import mlog -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 - \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/modules/windows.py b/modules/windows.py deleted file mode 100644 index e01b454..0000000 --- a/modules/windows.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 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/mparser.py b/mparser.py deleted file mode 100644 index 455182a..0000000 --- a/mparser.py +++ /dev/null @@ -1,565 +0,0 @@ -# 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/ninjabackend.py b/ninjabackend.py deleted file mode 100644 index c818f1d..0000000 --- a/ninjabackend.py +++ /dev/null @@ -1,1815 +0,0 @@ -# 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 backends -import environment, mesonlib -import build -import mlog -import dependencies -from mesonlib import File -from meson_install 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 -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 = os.path.join(script_root, 'depfixer.py') - 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, install_script, 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_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] - 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, benchmark_script, 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, - 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 --backend ninja secret-handshake\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 $CROSS\n' % (ninja_quote(sys.executable), - ninja_quote(os.path.join(scriptdir, 'symbolextractor.py')), - '$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/optinterpreter.py b/optinterpreter.py deleted file mode 100644 index 672df51..0000000 --- a/optinterpreter.py +++ /dev/null @@ -1,148 +0,0 @@ -# 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 mparser -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/regen_checker.py b/regen_checker.py deleted file mode 100755 index a0fe028..0000000 --- a/regen_checker.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) - if need_regen(regeninfo): - regen(regeninfo) - sys.exit(0) diff --git a/run_tests.py b/run_tests.py index f1ed1c7..f1ea2a9 100755 --- a/run_tests.py +++ b/run_tests.py @@ -18,15 +18,15 @@ from glob import glob import os, subprocess, shutil, sys, signal from io import StringIO import sys -import environment -import mesonlib -import mlog -import meson, meson_test, meson_benchmark +from meson import environment +from meson import mesonlib +from meson import mlog +from meson import meson, meson_test, meson_benchmark import argparse import xml.etree.ElementTree as ET import time -from meson import backendlist +from meson.meson import backendlist class TestResult: def __init__(self, msg, stdo, stde, conftime=0, buildtime=0, testtime=0): diff --git a/symbolextractor.py b/symbolextractor.py deleted file mode 100755 index f2c709d..0000000 --- a/symbolextractor.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/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. - -# 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 -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) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) != 2: - print(sys.argv[0], ' ') - sys.exit(1) - libfile = options.args[0] - outfile = options.args[1] - gen_symbols(libfile, outfile, options.cross_host) diff --git a/vcstagger.py b/vcstagger.py deleted file mode 100755 index ccc584e..0000000 --- a/vcstagger.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - infile, outfile, fallback, source_dir, replace_string, regex_selector = sys.argv[1:7] - command = sys.argv[7:] - config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/vs2010backend.py b/vs2010backend.py deleted file mode 100644 index 76e024c..0000000 --- a/vs2010backend.py +++ /dev/null @@ -1,640 +0,0 @@ -# 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 -import backends, build -import dependencies -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 ['"%s"' % 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 " into &quot; whereas MSVS - # requires quoted but not fixed elements. Enter horrible hack. - txt = open(ofname, 'r').read() - open(ofname, 'w').write(txt.replace('&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/wrap.py b/wrap.py deleted file mode 100644 index ac1505f..0000000 --- a/wrap.py +++ /dev/null @@ -1,178 +0,0 @@ -# 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 mlog -import urllib.request, os, hashlib, shutil -import subprocess -import sys - -import wraptool - -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 = wraptool.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/wraptool.py b/wraptool.py deleted file mode 100755 index 2366b78..0000000 --- a/wraptool.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/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 urllib.request, json -import sys, os -import configparser -import shutil -import platform -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/' - - -ssl_warning_printed = False - -from glob import glob - -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 [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 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) - -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)) - -if __name__ == '__main__': - if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': - print_help() - sys.exit(0) - command = sys.argv[1] - args = sys.argv[2:] - if command == 'list': - list_projects() - elif command == 'search': - if len(args) != 1: - print('Search requires exactly one argument.') - sys.exit(1) - search(args[0]) - elif command == 'install': - if len(args) != 1: - print('Install requires exactly one argument.') - sys.exit(1) - install(args[0]) - elif command == 'update': - if len(args) != 1: - print('update requires exactly one argument.') - sys.exit(1) - update(args[0]) - elif command == 'info': - if len(args) != 1: - print('info requires exactly one argument.') - sys.exit(1) - info(args[0]) - elif command == 'status': - status() - else: - print('Unknown command', command) - sys.exit(1) - diff --git a/xcodebackend.py b/xcodebackend.py deleted file mode 100644 index d72cbdf..0000000 --- a/xcodebackend.py +++ /dev/null @@ -1,775 +0,0 @@ -# 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. - -import backends, build -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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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') -- cgit v1.1 From a5508d3fd362ea33633d9706a6257ef0f0c2bbc0 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 15 Jan 2016 21:43:15 +0200 Subject: Can run most of test suite (with hacks). --- meson/backends.py | 15 ++ meson/commandrunner.py | 55 ----- meson/delwithsuffix.py | 32 --- meson/depfixer.py | 299 ------------------------ meson/dirchanger.py | 26 --- meson/environment.py | 2 +- meson/gtkdochelper.py | 118 ---------- meson/meson_benchmark.py | 97 -------- meson/meson_install.py | 227 ------------------ meson/meson_test.py | 232 ------------------- meson/mesonconf.py | 205 ----------------- meson/mesongui.py | 561 --------------------------------------------- meson/mesonintrospect.py | 208 ----------------- meson/mesonmain.py | 3 - meson/ninjabackend.py | 2 +- meson/regen_checker.py | 42 ---- meson/symbolextractor.py | 102 --------- meson/vcstagger.py | 33 --- meson/wrap.py | 30 ++- meson/wraptool.py | 233 ------------------- run_tests.py | 7 +- scripts/commandrunner.py | 55 +++++ scripts/delwithsuffix.py | 32 +++ scripts/depfixer.py | 299 ++++++++++++++++++++++++ scripts/dirchanger.py | 26 +++ scripts/gtkdochelper.py | 118 ++++++++++ scripts/meson_benchmark.py | 97 ++++++++ scripts/meson_install.py | 212 +++++++++++++++++ scripts/meson_test.py | 233 +++++++++++++++++++ scripts/mesonconf.py | 205 +++++++++++++++++ scripts/mesongui.py | 561 +++++++++++++++++++++++++++++++++++++++++++++ scripts/mesonintrospect.py | 208 +++++++++++++++++ scripts/regen_checker.py | 42 ++++ scripts/symbolextractor.py | 102 +++++++++ scripts/vcstagger.py | 33 +++ scripts/wraptool.py | 206 +++++++++++++++++ 36 files changed, 2478 insertions(+), 2480 deletions(-) delete mode 100755 meson/commandrunner.py delete mode 100755 meson/delwithsuffix.py delete mode 100755 meson/depfixer.py delete mode 100755 meson/dirchanger.py delete mode 100755 meson/gtkdochelper.py delete mode 100755 meson/meson_benchmark.py delete mode 100755 meson/meson_install.py delete mode 100755 meson/meson_test.py delete mode 100755 meson/mesonconf.py delete mode 100755 meson/mesongui.py delete mode 100755 meson/mesonintrospect.py mode change 100755 => 100644 meson/mesonmain.py delete mode 100755 meson/regen_checker.py delete mode 100755 meson/symbolextractor.py delete mode 100755 meson/vcstagger.py delete mode 100755 meson/wraptool.py create mode 100755 scripts/commandrunner.py create mode 100755 scripts/delwithsuffix.py create mode 100755 scripts/depfixer.py create mode 100755 scripts/dirchanger.py create mode 100755 scripts/gtkdochelper.py create mode 100755 scripts/meson_benchmark.py create mode 100755 scripts/meson_install.py create mode 100755 scripts/meson_test.py create mode 100755 scripts/mesonconf.py create mode 100755 scripts/mesongui.py create mode 100755 scripts/mesonintrospect.py create mode 100755 scripts/regen_checker.py create mode 100755 scripts/symbolextractor.py create mode 100755 scripts/vcstagger.py create mode 100755 scripts/wraptool.py diff --git a/meson/backends.py b/meson/backends.py index 2706368..c583a7b 100644 --- a/meson/backends.py +++ b/meson/backends.py @@ -19,6 +19,21 @@ 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): diff --git a/meson/commandrunner.py b/meson/commandrunner.py deleted file mode 100755 index 0dad585..0000000 --- a/meson/commandrunner.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/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) - -if __name__ == '__main__': - if len(sys.argv) < 5: - print(sys.argv[0], ' [arguments]') - 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() - sys.exit(pc.returncode) diff --git a/meson/delwithsuffix.py b/meson/delwithsuffix.py deleted file mode 100755 index 4b8a60d..0000000 --- a/meson/delwithsuffix.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/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 - -if len(sys.argv) != 3: - print('%s ' % sys.argv[0]) - sys.exit(1) - -topdir = sys.argv[1] -suffix = sys.argv[2] -if suffix[0] != '.': - suffix = '.' + suffix - -for (root, dirs, files) in os.walk(topdir): - for f in files: - if f.endswith(suffix): - fullname = os.path.join(root, f) - os.unlink(fullname) diff --git a/meson/depfixer.py b/meson/depfixer.py deleted file mode 100755 index 4f7ce3d..0000000 --- a/meson/depfixer.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/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 - -if __name__ == '__main__': - if len(sys.argv) < 2 or len(sys.argv) > 3: - print('This application resets target rpath.') - print('Don\'t run this unless you know what you are doing.') - print('%s: ' % sys.argv[0]) - exit(1) - e = Elf(sys.argv[1]) - if len(sys.argv) == 2: - e.print_rpath() - else: - new_rpath = sys.argv[2] - e.fix_rpath(new_rpath.encode('utf8')) - #e.fix_deps(prefix.encode()) diff --git a/meson/dirchanger.py b/meson/dirchanger.py deleted file mode 100755 index fd3dc23..0000000 --- a/meson/dirchanger.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 - -dirname = sys.argv[1] -command = sys.argv[2:] - -os.chdir(dirname) -sys.exit(subprocess.call(command)) diff --git a/meson/environment.py b/meson/environment.py index 3e10bd4..a99cb30 100644 --- a/meson/environment.py +++ b/meson/environment.py @@ -125,7 +125,7 @@ class Environment(): coredata.save(self.coredata, cdf) def get_script_dir(self): - return os.path.dirname(self.meson_script_file) + return os.path.join(os.path.dirname(self.meson_script_file), '../scripts') def get_log_dir(self): return self.log_dir diff --git a/meson/gtkdochelper.py b/meson/gtkdochelper.py deleted file mode 100755 index 7e476b8..0000000 --- a/meson/gtkdochelper.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - options = parser.parse_args(sys.argv[1:]) - 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) diff --git a/meson/meson_benchmark.py b/meson/meson_benchmark.py deleted file mode 100755 index 26f1f95..0000000 --- a/meson/meson_benchmark.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/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/meson/meson_install.py b/meson/meson_install.py deleted file mode 100755 index c5c17ba..0000000 --- a/meson/meson_install.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/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 - -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 = [] - -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) - -if __name__ == '__main__': - if len(sys.argv) != 2: - print('Installer script for Meson. Do not run on your own, mmm\'kay?') - print('%s [install info file]' % sys.argv[0]) - datafilename = sys.argv[1] - do_install(datafilename) - diff --git a/meson/meson_test.py b/meson/meson_test.py deleted file mode 100755 index d9b4993..0000000 --- a/meson/meson_test.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/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, 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/meson/mesonconf.py b/meson/mesonconf.py deleted file mode 100755 index e53875f..0000000 --- a/meson/mesonconf.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 - -# 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 sys, os -import pickle -import argparse -import coredata, mesonlib -from coredata import build_types, layouts, 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) - -if __name__ == '__main__': - args = mesonlib.expand_arguments(sys.argv[:]) - if not args: - sys.exit(1) - options = parser.parse_args(args[1:]) - if len(options.directory) > 1: - print('%s ' % sys.argv[0]) - print('If you omit the build directory, the current directory is substituted.') - sys.exit(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) - diff --git a/meson/mesongui.py b/meson/mesongui.py deleted file mode 100755 index bdd44bb..0000000 --- a/meson/mesongui.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/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 -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.buildtype) - combo.currentTextChanged.connect(self.build_type_changed) - self.form.addRow('Build type', combo) - strip = QCheckBox("") - strip.setChecked(self.coredata.strip) - strip.stateChanged.connect(self.strip_changed) - self.form.addRow('Strip on install', strip) - coverage = QCheckBox("") - coverage.setChecked(self.coredata.coverage) - coverage.stateChanged.connect(self.coverage_changed) - self.form.addRow('Enable coverage', coverage) - pch = QCheckBox("") - pch.setChecked(self.coredata.use_pch) - pch.stateChanged.connect(self.pch_changed) - self.form.addRow('Enable pch', pch) - unity = QCheckBox("") - unity.setChecked(self.coredata.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 - -if __name__ == '__main__': - app = QApplication(sys.argv) - if len(sys.argv) == 1: - arg = "" - elif len(sys.argv) == 2: - arg = sys.argv[1] - else: - print(sys.argv[0], "") - sys.exit(1) - if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): - guirespawner = MesonGuiRespawner(arg) - else: - runner = Starter(arg) - sys.exit(app.exec_()) diff --git a/meson/mesonintrospect.py b/meson/mesonintrospect.py deleted file mode 100755 index 9fcd4db..0000000 --- a/meson/mesonintrospect.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -"""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 -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)) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) > 1: - print('Too many arguments') - sys.exit(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') - sys.exit(1) diff --git a/meson/mesonmain.py b/meson/mesonmain.py old mode 100755 new mode 100644 index c4a9ada..0bf123c --- a/meson/mesonmain.py +++ b/meson/mesonmain.py @@ -220,6 +220,3 @@ def run(args): traceback.print_exc() return 1 return 0 - -if __name__ == '__main__': - sys.exit(run(sys.argv[:])) diff --git a/meson/ninjabackend.py b/meson/ninjabackend.py index 53b59a1..d21cea0 100644 --- a/meson/ninjabackend.py +++ b/meson/ninjabackend.py @@ -18,7 +18,7 @@ from . import build from . import mlog from . import dependencies from .mesonlib import File -from .meson_install import InstallData +from .backends import InstallData from .build import InvalidArguments from .coredata import MesonException import os, sys, pickle, re diff --git a/meson/regen_checker.py b/meson/regen_checker.py deleted file mode 100755 index a0fe028..0000000 --- a/meson/regen_checker.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) - if need_regen(regeninfo): - regen(regeninfo) - sys.exit(0) diff --git a/meson/symbolextractor.py b/meson/symbolextractor.py deleted file mode 100755 index f2c709d..0000000 --- a/meson/symbolextractor.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/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. - -# 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 -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) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) != 2: - print(sys.argv[0], ' ') - sys.exit(1) - libfile = options.args[0] - outfile = options.args[1] - gen_symbols(libfile, outfile, options.cross_host) diff --git a/meson/vcstagger.py b/meson/vcstagger.py deleted file mode 100755 index ccc584e..0000000 --- a/meson/vcstagger.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - infile, outfile, fallback, source_dir, replace_string, regex_selector = sys.argv[1:7] - command = sys.argv[7:] - config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/meson/wrap.py b/meson/wrap.py index e55d3db..ac9c9ce 100644 --- a/meson/wrap.py +++ b/meson/wrap.py @@ -17,7 +17,33 @@ import urllib.request, os, hashlib, shutil import subprocess import sys -from . import wraptool +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 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): @@ -94,7 +120,7 @@ class Resolver: def get_data(self, url): blocksize = 10*1024 if url.startswith('https://wrapdb.mesonbuild.com'): - resp = wraptool.open_wrapdburl(url) + resp = open_wrapdburl(url) else: resp = urllib.request.urlopen(url) dlsize = int(resp.info()['Content-Length']) diff --git a/meson/wraptool.py b/meson/wraptool.py deleted file mode 100755 index 2366b78..0000000 --- a/meson/wraptool.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/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 urllib.request, json -import sys, os -import configparser -import shutil -import platform -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/' - - -ssl_warning_printed = False - -from glob import glob - -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 [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 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) - -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)) - -if __name__ == '__main__': - if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': - print_help() - sys.exit(0) - command = sys.argv[1] - args = sys.argv[2:] - if command == 'list': - list_projects() - elif command == 'search': - if len(args) != 1: - print('Search requires exactly one argument.') - sys.exit(1) - search(args[0]) - elif command == 'install': - if len(args) != 1: - print('Install requires exactly one argument.') - sys.exit(1) - install(args[0]) - elif command == 'update': - if len(args) != 1: - print('update requires exactly one argument.') - sys.exit(1) - update(args[0]) - elif command == 'info': - if len(args) != 1: - print('info requires exactly one argument.') - sys.exit(1) - info(args[0]) - elif command == 'status': - status() - else: - print('Unknown command', command) - sys.exit(1) - diff --git a/run_tests.py b/run_tests.py index f1ea2a9..e24c906 100755 --- a/run_tests.py +++ b/run_tests.py @@ -21,12 +21,13 @@ import sys from meson import environment from meson import mesonlib from meson import mlog -from meson import meson, meson_test, meson_benchmark +from meson import mesonmain +from scripts import meson_test, meson_benchmark import argparse import xml.etree.ElementTree as ET import time -from meson.meson import backendlist +from meson.mesonmain import backendlist class TestResult: def __init__(self, msg, stdo, stde, conftime=0, buildtime=0, testtime=0): @@ -154,7 +155,7 @@ def run_configure_inprocess(commandlist): old_stderr = sys.stderr sys.stderr = mystderr = StringIO() try: - returncode = meson.run(commandlist) + returncode = mesonmain.run(commandlist) finally: sys.stdout = old_stdout sys.stderr = old_stderr diff --git a/scripts/commandrunner.py b/scripts/commandrunner.py new file mode 100755 index 0000000..0dad585 --- /dev/null +++ b/scripts/commandrunner.py @@ -0,0 +1,55 @@ +#!/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) + +if __name__ == '__main__': + if len(sys.argv) < 5: + print(sys.argv[0], ' [arguments]') + 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() + sys.exit(pc.returncode) diff --git a/scripts/delwithsuffix.py b/scripts/delwithsuffix.py new file mode 100755 index 0000000..4b8a60d --- /dev/null +++ b/scripts/delwithsuffix.py @@ -0,0 +1,32 @@ +#!/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 + +if len(sys.argv) != 3: + print('%s ' % sys.argv[0]) + sys.exit(1) + +topdir = sys.argv[1] +suffix = sys.argv[2] +if suffix[0] != '.': + suffix = '.' + suffix + +for (root, dirs, files) in os.walk(topdir): + for f in files: + if f.endswith(suffix): + fullname = os.path.join(root, f) + os.unlink(fullname) diff --git a/scripts/depfixer.py b/scripts/depfixer.py new file mode 100755 index 0000000..4f7ce3d --- /dev/null +++ b/scripts/depfixer.py @@ -0,0 +1,299 @@ +#!/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 + +if __name__ == '__main__': + if len(sys.argv) < 2 or len(sys.argv) > 3: + print('This application resets target rpath.') + print('Don\'t run this unless you know what you are doing.') + print('%s: ' % sys.argv[0]) + exit(1) + e = Elf(sys.argv[1]) + if len(sys.argv) == 2: + e.print_rpath() + else: + new_rpath = sys.argv[2] + e.fix_rpath(new_rpath.encode('utf8')) + #e.fix_deps(prefix.encode()) diff --git a/scripts/dirchanger.py b/scripts/dirchanger.py new file mode 100755 index 0000000..fd3dc23 --- /dev/null +++ b/scripts/dirchanger.py @@ -0,0 +1,26 @@ +#!/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 + +dirname = sys.argv[1] +command = sys.argv[2:] + +os.chdir(dirname) +sys.exit(subprocess.call(command)) diff --git a/scripts/gtkdochelper.py b/scripts/gtkdochelper.py new file mode 100755 index 0000000..7e476b8 --- /dev/null +++ b/scripts/gtkdochelper.py @@ -0,0 +1,118 @@ +#!/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 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) + +if __name__ == '__main__': + options = parser.parse_args(sys.argv[1:]) + 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) diff --git a/scripts/meson_benchmark.py b/scripts/meson_benchmark.py new file mode 100755 index 0000000..26f1f95 --- /dev/null +++ b/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/scripts/meson_install.py b/scripts/meson_install.py new file mode 100755 index 0000000..e0a5eb2 --- /dev/null +++ b/scripts/meson_install.py @@ -0,0 +1,212 @@ +#!/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) + +if __name__ == '__main__': + if len(sys.argv) != 2: + print('Installer script for Meson. Do not run on your own, mmm\'kay?') + print('%s [install info file]' % sys.argv[0]) + datafilename = sys.argv[1] + do_install(datafilename) + diff --git a/scripts/meson_test.py b/scripts/meson_test.py new file mode 100755 index 0000000..43b1cdb --- /dev/null +++ b/scripts/meson_test.py @@ -0,0 +1,233 @@ +#!/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 meson +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/scripts/mesonconf.py b/scripts/mesonconf.py new file mode 100755 index 0000000..e53875f --- /dev/null +++ b/scripts/mesonconf.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +# 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 sys, os +import pickle +import argparse +import coredata, mesonlib +from coredata import build_types, layouts, 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) + +if __name__ == '__main__': + args = mesonlib.expand_arguments(sys.argv[:]) + if not args: + sys.exit(1) + options = parser.parse_args(args[1:]) + if len(options.directory) > 1: + print('%s ' % sys.argv[0]) + print('If you omit the build directory, the current directory is substituted.') + sys.exit(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) + diff --git a/scripts/mesongui.py b/scripts/mesongui.py new file mode 100755 index 0000000..bdd44bb --- /dev/null +++ b/scripts/mesongui.py @@ -0,0 +1,561 @@ +#!/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 +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.buildtype) + combo.currentTextChanged.connect(self.build_type_changed) + self.form.addRow('Build type', combo) + strip = QCheckBox("") + strip.setChecked(self.coredata.strip) + strip.stateChanged.connect(self.strip_changed) + self.form.addRow('Strip on install', strip) + coverage = QCheckBox("") + coverage.setChecked(self.coredata.coverage) + coverage.stateChanged.connect(self.coverage_changed) + self.form.addRow('Enable coverage', coverage) + pch = QCheckBox("") + pch.setChecked(self.coredata.use_pch) + pch.stateChanged.connect(self.pch_changed) + self.form.addRow('Enable pch', pch) + unity = QCheckBox("") + unity.setChecked(self.coredata.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 + +if __name__ == '__main__': + app = QApplication(sys.argv) + if len(sys.argv) == 1: + arg = "" + elif len(sys.argv) == 2: + arg = sys.argv[1] + else: + print(sys.argv[0], "") + sys.exit(1) + if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): + guirespawner = MesonGuiRespawner(arg) + else: + runner = Starter(arg) + sys.exit(app.exec_()) diff --git a/scripts/mesonintrospect.py b/scripts/mesonintrospect.py new file mode 100755 index 0000000..9fcd4db --- /dev/null +++ b/scripts/mesonintrospect.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 + +# 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. + +"""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 +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)) + +if __name__ == '__main__': + options = parser.parse_args() + if len(options.args) > 1: + print('Too many arguments') + sys.exit(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') + sys.exit(1) diff --git a/scripts/regen_checker.py b/scripts/regen_checker.py new file mode 100755 index 0000000..a0fe028 --- /dev/null +++ b/scripts/regen_checker.py @@ -0,0 +1,42 @@ +#!/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 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) + +if __name__ == '__main__': + regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) + if need_regen(regeninfo): + regen(regeninfo) + sys.exit(0) diff --git a/scripts/symbolextractor.py b/scripts/symbolextractor.py new file mode 100755 index 0000000..f2c709d --- /dev/null +++ b/scripts/symbolextractor.py @@ -0,0 +1,102 @@ +#!/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. + +# 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 +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) + +if __name__ == '__main__': + options = parser.parse_args() + if len(options.args) != 2: + print(sys.argv[0], ' ') + sys.exit(1) + libfile = options.args[0] + outfile = options.args[1] + gen_symbols(libfile, outfile, options.cross_host) diff --git a/scripts/vcstagger.py b/scripts/vcstagger.py new file mode 100755 index 0000000..ccc584e --- /dev/null +++ b/scripts/vcstagger.py @@ -0,0 +1,33 @@ +#!/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 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) + +if __name__ == '__main__': + infile, outfile, fallback, source_dir, replace_string, regex_selector = sys.argv[1:7] + command = sys.argv[7:] + config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/scripts/wraptool.py b/scripts/wraptool.py new file mode 100755 index 0000000..46860aa --- /dev/null +++ b/scripts/wraptool.py @@ -0,0 +1,206 @@ +#!/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 json +import sys, os +import configparser +import shutil + + +ssl_warning_printed = False + +from glob import glob + +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 [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 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 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)) + +if __name__ == '__main__': + if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': + print_help() + sys.exit(0) + command = sys.argv[1] + args = sys.argv[2:] + if command == 'list': + list_projects() + elif command == 'search': + if len(args) != 1: + print('Search requires exactly one argument.') + sys.exit(1) + search(args[0]) + elif command == 'install': + if len(args) != 1: + print('Install requires exactly one argument.') + sys.exit(1) + install(args[0]) + elif command == 'update': + if len(args) != 1: + print('update requires exactly one argument.') + sys.exit(1) + update(args[0]) + elif command == 'info': + if len(args) != 1: + print('info requires exactly one argument.') + sys.exit(1) + info(args[0]) + elif command == 'status': + status() + else: + print('Unknown command', command) + sys.exit(1) + -- cgit v1.1 From 61cb4ae9ca5e0ee7b9864e22621fcbc44a9b407b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 15 Jan 2016 21:57:10 +0200 Subject: Can compile full test suite. --- meson/interpreter.py | 2 +- meson/modules/gnome.py | 8 ++++---- meson/modules/pkgconfig.py | 4 ++-- meson/modules/qt4.py | 6 +++--- meson/modules/qt5.py | 6 +++--- meson/modules/rpm.py | 10 +++++----- meson/modules/windows.py | 4 ++-- scripts/symbolextractor.py | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/meson/interpreter.py b/meson/interpreter.py index ec1bda0..5ce2f8d 100644 --- a/meson/interpreter.py +++ b/meson/interpreter.py @@ -1094,7 +1094,7 @@ class Interpreter(): raise InvalidCode('Import takes one argument.') modname = args[0] if not modname in self.environment.coredata.modules: - module = importlib.import_module('modules.' + modname).initialize() + module = importlib.import_module('meson.modules.' + modname).initialize() self.environment.coredata.modules[modname] = module return ModuleHolder(modname, self.environment.coredata.modules[modname], self) diff --git a/meson/modules/gnome.py b/meson/modules/gnome.py index 60c994e..70ac81b 100644 --- a/meson/modules/gnome.py +++ b/meson/modules/gnome.py @@ -15,13 +15,13 @@ '''This module provides helper functions for Gnome/GLib related functionality such as gobject-introspection and gresources.''' -import build +from .. import build import os, sys import subprocess -from coredata import MesonException -import mlog +from ..coredata import MesonException +from .. import mlog import xml.etree.ElementTree as ET -from mesonlib import File +from ..mesonlib import File girwarning_printed = False diff --git a/meson/modules/pkgconfig.py b/meson/modules/pkgconfig.py index 5fa4bd0..f18decf 100644 --- a/meson/modules/pkgconfig.py +++ b/meson/modules/pkgconfig.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import coredata, build -import mesonlib +from .. import coredata, build +from .. import mesonlib import os class PkgConfigModule: diff --git a/meson/modules/qt4.py b/meson/modules/qt4.py index bcf42a3..162b553 100644 --- a/meson/modules/qt4.py +++ b/meson/modules/qt4.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import dependencies, mlog +from .. import dependencies, mlog import os, subprocess -import build -from coredata import MesonException +from .. import build +from ..coredata import MesonException import xml.etree.ElementTree as ET class Qt4Module(): diff --git a/meson/modules/qt5.py b/meson/modules/qt5.py index 0e732a1..81edc76 100644 --- a/meson/modules/qt5.py +++ b/meson/modules/qt5.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import dependencies, mlog +from .. import dependencies, mlog import os, subprocess -import build -from coredata import MesonException +from .. import build +from ..coredata import MesonException import xml.etree.ElementTree as ET class Qt5Module(): diff --git a/meson/modules/rpm.py b/meson/modules/rpm.py index eebecdd..a2c0502 100644 --- a/meson/modules/rpm.py +++ b/meson/modules/rpm.py @@ -15,11 +15,11 @@ '''This module provides helper functions for RPM related functionality such as generating template RPM spec file.''' -import build -import compilers -import datetime -import mlog -import modules.gnome +from .. import build +from .. import compilers +from .. import datetime +from .. import mlog +from .. import modules.gnome import os class RPMModule: diff --git a/meson/modules/windows.py b/meson/modules/windows.py index e01b454..a785250 100644 --- a/meson/modules/windows.py +++ b/meson/modules/windows.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import mesonlib, dependencies, build -from coredata import MesonException +from .. import mesonlib, dependencies, build +from ..coredata import MesonException import os class WindowsModule: diff --git a/scripts/symbolextractor.py b/scripts/symbolextractor.py index f2c709d..fe86d35 100755 --- a/scripts/symbolextractor.py +++ b/scripts/symbolextractor.py @@ -23,7 +23,7 @@ # http://cgit.freedesktop.org/libreoffice/core/commit/?id=3213cd54b76bc80a6f0516aac75a48ff3b2ad67c import sys, subprocess -import mesonlib +from meson import mesonlib import argparse parser = argparse.ArgumentParser() -- cgit v1.1 From d2a14075b3bcee9235ba21e881f0910f8bf9167a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 15 Jan 2016 22:40:14 +0200 Subject: Some fixes for other backends. --- meson/vs2010backend.py | 8 ++++---- meson/xcodebackend.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/meson/vs2010backend.py b/meson/vs2010backend.py index 76e024c..33e9646 100644 --- a/meson/vs2010backend.py +++ b/meson/vs2010backend.py @@ -14,12 +14,12 @@ import os, sys import pickle -import backends, build -import dependencies -import mlog +from . import backends, build +from . import dependencies +from . import mlog import xml.etree.ElementTree as ET import xml.dom.minidom -from coredata import MesonException +from .coredata import MesonException class RegenInfo(): def __init__(self, source_dir, build_dir, depfiles, solutionfile): diff --git a/meson/xcodebackend.py b/meson/xcodebackend.py index d72cbdf..8ac3f67 100644 --- a/meson/xcodebackend.py +++ b/meson/xcodebackend.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import backends, build -import mesonlib +from . import backends, build +from . import mesonlib import uuid, os, sys -from coredata import MesonException +from .coredata import MesonException class XCodeBackend(backends.Backend): def __init__(self, build): -- cgit v1.1 From 66c01401deb186e6daa20cf4bf6a098076dda236 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Fri, 15 Jan 2016 23:37:22 +0200 Subject: Moved scripts in the module and started work to run them via the main meson command. --- meson.py | 22 +- meson/environment.py | 2 +- meson/mesonmain.py | 60 ++++- meson/modules/gnome.py | 2 +- meson/ninjabackend.py | 11 +- meson/scripts/commandrunner.py | 59 ++++ meson/scripts/delwithsuffix.py | 37 +++ meson/scripts/depfixer.py | 302 +++++++++++++++++++++ meson/scripts/dirchanger.py | 30 +++ meson/scripts/gtkdochelper.py | 122 +++++++++ meson/scripts/meson_benchmark.py | 97 +++++++ meson/scripts/meson_install.py | 215 +++++++++++++++ meson/scripts/meson_test.py | 233 ++++++++++++++++ meson/scripts/regen_checker.py | 45 ++++ meson/scripts/symbolextractor.py | 106 ++++++++ meson/scripts/vcstagger.py | 36 +++ mesonconf.py | 205 ++++++++++++++ mesongui.py | 561 +++++++++++++++++++++++++++++++++++++++ mesonintrospect.py | 208 +++++++++++++++ scripts/commandrunner.py | 55 ---- scripts/delwithsuffix.py | 32 --- scripts/depfixer.py | 299 --------------------- scripts/dirchanger.py | 26 -- scripts/gtkdochelper.py | 118 -------- scripts/meson_benchmark.py | 97 ------- scripts/meson_install.py | 212 --------------- scripts/meson_test.py | 233 ---------------- scripts/mesonconf.py | 205 -------------- scripts/mesongui.py | 561 --------------------------------------- scripts/mesonintrospect.py | 208 --------------- scripts/regen_checker.py | 42 --- scripts/symbolextractor.py | 102 ------- scripts/vcstagger.py | 33 --- scripts/wraptool.py | 206 -------------- wraptool.py | 206 ++++++++++++++ 35 files changed, 2538 insertions(+), 2450 deletions(-) create mode 100644 meson/scripts/commandrunner.py create mode 100644 meson/scripts/delwithsuffix.py create mode 100644 meson/scripts/depfixer.py create mode 100644 meson/scripts/dirchanger.py create mode 100644 meson/scripts/gtkdochelper.py create mode 100644 meson/scripts/meson_benchmark.py create mode 100644 meson/scripts/meson_install.py create mode 100644 meson/scripts/meson_test.py create mode 100644 meson/scripts/regen_checker.py create mode 100644 meson/scripts/symbolextractor.py create mode 100644 meson/scripts/vcstagger.py create mode 100644 mesonconf.py create mode 100644 mesongui.py create mode 100644 mesonintrospect.py delete mode 100755 scripts/commandrunner.py delete mode 100755 scripts/delwithsuffix.py delete mode 100755 scripts/depfixer.py delete mode 100755 scripts/dirchanger.py delete mode 100755 scripts/gtkdochelper.py delete mode 100755 scripts/meson_benchmark.py delete mode 100755 scripts/meson_install.py delete mode 100755 scripts/meson_test.py delete mode 100755 scripts/mesonconf.py delete mode 100755 scripts/mesongui.py delete mode 100755 scripts/mesonintrospect.py delete mode 100755 scripts/regen_checker.py delete mode 100755 scripts/symbolextractor.py delete mode 100755 scripts/vcstagger.py delete mode 100755 scripts/wraptool.py create mode 100755 wraptool.py diff --git a/meson.py b/meson.py index 94d6e22..ab8db72 100755 --- a/meson.py +++ b/meson.py @@ -1,6 +1,24 @@ #!/usr/bin/env python3 +# Copyright 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 meson import mesonmain -import sys +import sys, os + +thisfile = __file__ +if not os.path.isabs(thisfile): + thisfile = os.path.join(os.getcwd(), thisfile) -sys.exit(mesonmain.run(sys.argv[:])) +sys.exit(mesonmain.run(thisfile, sys.argv[1:])) diff --git a/meson/environment.py b/meson/environment.py index a99cb30..8df856c 100644 --- a/meson/environment.py +++ b/meson/environment.py @@ -1,4 +1,4 @@ -# Copyright 2012-2014 The Meson development team +# 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. diff --git a/meson/mesonmain.py b/meson/mesonmain.py index 0bf123c..58ba06b 100644 --- a/meson/mesonmain.py +++ b/meson/mesonmain.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2012-2015 The Meson development team +# 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. @@ -18,7 +18,7 @@ import sys, stat, traceback, pickle, argparse import datetime import os.path from . import environment, interpreter, mesonlib -from .import build +from . import build import platform from . import mlog, coredata @@ -163,21 +163,58 @@ itself as required.''' dumpfile = os.path.join(env.get_scratch_dir(), 'build.dat') pickle.dump(b, open(dumpfile, 'wb')) -def run(args): +def run_script_command(args): + cmdname = args[0] + cmdargs = args[1:] + if cmdname == 'test': + import meson.scripts.meson_test as abc + cmdfunc = abc.run + elif cmdname == 'benchmark': + import meson.scripts.meson_benchmark as abc + cmdfunc = abc.run + elif cmdname == 'commandrunner': + import meson.scripts.commandrunner as abc + cmdfunc = abc.run + elif cmdname == 'delsuffix': + import meson.scripts.delwithsuffix as abc + cmdfunc = abc.run + elif cmdname == 'dirchanger': + import meson.scripts.dirchanger as abc + cmdfunc = abc.run + elif cmdname == 'gtkdoc': + import meson.scripts.gtkdochelper as abc + cmdfunc = abc.run + elif cmdname == 'regencheck': + import meson.scripts.regen_checker as abc + cmdfunc = abc.run + elif cmdname == 'symbolextractor': + import meson.scripts.symbolextractor as abc + cmdfunc = abc.run + elif cmdname == 'vcstagger': + import meson.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[-1] == 'secret-handshake': - args = args[:-1] + if args[0] == '--internal': + if args[1] != 'regenerate': + sys.exit(run_script_command(args[1:])) + args = args[2:] handshake = True else: handshake = False + print(args) args = mesonlib.expand_arguments(args) if not args: return 1 - options = parser.parse_args(args[1:]) + options = parser.parse_args(args) if options.print_version: print(coredata.version) return 0 @@ -191,16 +228,15 @@ def run(args): dir2 = args[1] else: dir2 = '.' - this_file = os.path.abspath(__file__) - while os.path.islink(this_file): - resolved = os.readlink(this_file) + while os.path.islink(mainfile): + resolved = os.readlink(mainfile) if resolved[0] != '/': - this_file = os.path.join(os.path.dirname(this_file), resolved) + mainfile = os.path.join(os.path.dirname(mainfile), resolved) else: - this_file = resolved + mainfile = resolved try: - app = MesonApp(dir1, dir2, this_file, handshake, options) + app = MesonApp(dir1, dir2, mainfile, handshake, options) except Exception as e: # Log directory does not exist, so just print # to stdout. diff --git a/meson/modules/gnome.py b/meson/modules/gnome.py index 70ac81b..e552b84 100644 --- a/meson/modules/gnome.py +++ b/meson/modules/gnome.py @@ -1,4 +1,4 @@ -# Copyright 2015 The Meson development team +# 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. diff --git a/meson/ninjabackend.py b/meson/ninjabackend.py index d21cea0..80c94f9 100644 --- a/meson/ninjabackend.py +++ b/meson/ninjabackend.py @@ -1,4 +1,4 @@ -# Copyright 2012-2014 The Meson development team +# 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. @@ -582,9 +582,8 @@ int dummy; self.serialise_tests() valgrind = environment.find_valgrind() 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] + 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.') @@ -604,7 +603,7 @@ int dummy; # 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, benchmark_script, benchmark_data] + 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.') @@ -628,9 +627,11 @@ int dummy; 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 --backend ninja secret-handshake\n" % c) + 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: diff --git a/meson/scripts/commandrunner.py b/meson/scripts/commandrunner.py new file mode 100644 index 0000000..f5a2fff --- /dev/null +++ b/meson/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 [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/meson/scripts/delwithsuffix.py b/meson/scripts/delwithsuffix.py new file mode 100644 index 0000000..38ab406 --- /dev/null +++ b/meson/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 ') + 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/meson/scripts/depfixer.py b/meson/scripts/depfixer.py new file mode 100644 index 0000000..1ab83b6 --- /dev/null +++ b/meson/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: ' % 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/meson/scripts/dirchanger.py b/meson/scripts/dirchanger.py new file mode 100644 index 0000000..93a901d --- /dev/null +++ b/meson/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/meson/scripts/gtkdochelper.py b/meson/scripts/gtkdochelper.py new file mode 100644 index 0000000..68be8f2 --- /dev/null +++ b/meson/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/meson/scripts/meson_benchmark.py b/meson/scripts/meson_benchmark.py new file mode 100644 index 0000000..26f1f95 --- /dev/null +++ b/meson/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/meson/scripts/meson_install.py b/meson/scripts/meson_install.py new file mode 100644 index 0000000..a286864 --- /dev/null +++ b/meson/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/meson/scripts/meson_test.py b/meson/scripts/meson_test.py new file mode 100644 index 0000000..c5814ef --- /dev/null +++ b/meson/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 meson +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/meson/scripts/regen_checker.py b/meson/scripts/regen_checker.py new file mode 100644 index 0000000..f360a7c --- /dev/null +++ b/meson/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/meson/scripts/symbolextractor.py b/meson/scripts/symbolextractor.py new file mode 100644 index 0000000..9607466 --- /dev/null +++ b/meson/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 meson 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 ') + 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/meson/scripts/vcstagger.py b/meson/scripts/vcstagger.py new file mode 100644 index 0000000..390e37a --- /dev/null +++ b/meson/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/mesonconf.py b/mesonconf.py new file mode 100644 index 0000000..e53875f --- /dev/null +++ b/mesonconf.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +# 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 sys, os +import pickle +import argparse +import coredata, mesonlib +from coredata import build_types, layouts, 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) + +if __name__ == '__main__': + args = mesonlib.expand_arguments(sys.argv[:]) + if not args: + sys.exit(1) + options = parser.parse_args(args[1:]) + if len(options.directory) > 1: + print('%s ' % sys.argv[0]) + print('If you omit the build directory, the current directory is substituted.') + sys.exit(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) + diff --git a/mesongui.py b/mesongui.py new file mode 100644 index 0000000..bdd44bb --- /dev/null +++ b/mesongui.py @@ -0,0 +1,561 @@ +#!/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 +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.buildtype) + combo.currentTextChanged.connect(self.build_type_changed) + self.form.addRow('Build type', combo) + strip = QCheckBox("") + strip.setChecked(self.coredata.strip) + strip.stateChanged.connect(self.strip_changed) + self.form.addRow('Strip on install', strip) + coverage = QCheckBox("") + coverage.setChecked(self.coredata.coverage) + coverage.stateChanged.connect(self.coverage_changed) + self.form.addRow('Enable coverage', coverage) + pch = QCheckBox("") + pch.setChecked(self.coredata.use_pch) + pch.stateChanged.connect(self.pch_changed) + self.form.addRow('Enable pch', pch) + unity = QCheckBox("") + unity.setChecked(self.coredata.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 + +if __name__ == '__main__': + app = QApplication(sys.argv) + if len(sys.argv) == 1: + arg = "" + elif len(sys.argv) == 2: + arg = sys.argv[1] + else: + print(sys.argv[0], "") + sys.exit(1) + if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): + guirespawner = MesonGuiRespawner(arg) + else: + runner = Starter(arg) + sys.exit(app.exec_()) diff --git a/mesonintrospect.py b/mesonintrospect.py new file mode 100644 index 0000000..9fcd4db --- /dev/null +++ b/mesonintrospect.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 + +# 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. + +"""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 +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)) + +if __name__ == '__main__': + options = parser.parse_args() + if len(options.args) > 1: + print('Too many arguments') + sys.exit(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') + sys.exit(1) diff --git a/scripts/commandrunner.py b/scripts/commandrunner.py deleted file mode 100755 index 0dad585..0000000 --- a/scripts/commandrunner.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/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) - -if __name__ == '__main__': - if len(sys.argv) < 5: - print(sys.argv[0], ' [arguments]') - 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() - sys.exit(pc.returncode) diff --git a/scripts/delwithsuffix.py b/scripts/delwithsuffix.py deleted file mode 100755 index 4b8a60d..0000000 --- a/scripts/delwithsuffix.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/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 - -if len(sys.argv) != 3: - print('%s ' % sys.argv[0]) - sys.exit(1) - -topdir = sys.argv[1] -suffix = sys.argv[2] -if suffix[0] != '.': - suffix = '.' + suffix - -for (root, dirs, files) in os.walk(topdir): - for f in files: - if f.endswith(suffix): - fullname = os.path.join(root, f) - os.unlink(fullname) diff --git a/scripts/depfixer.py b/scripts/depfixer.py deleted file mode 100755 index 4f7ce3d..0000000 --- a/scripts/depfixer.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/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 - -if __name__ == '__main__': - if len(sys.argv) < 2 or len(sys.argv) > 3: - print('This application resets target rpath.') - print('Don\'t run this unless you know what you are doing.') - print('%s: ' % sys.argv[0]) - exit(1) - e = Elf(sys.argv[1]) - if len(sys.argv) == 2: - e.print_rpath() - else: - new_rpath = sys.argv[2] - e.fix_rpath(new_rpath.encode('utf8')) - #e.fix_deps(prefix.encode()) diff --git a/scripts/dirchanger.py b/scripts/dirchanger.py deleted file mode 100755 index fd3dc23..0000000 --- a/scripts/dirchanger.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 - -dirname = sys.argv[1] -command = sys.argv[2:] - -os.chdir(dirname) -sys.exit(subprocess.call(command)) diff --git a/scripts/gtkdochelper.py b/scripts/gtkdochelper.py deleted file mode 100755 index 7e476b8..0000000 --- a/scripts/gtkdochelper.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - options = parser.parse_args(sys.argv[1:]) - 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) diff --git a/scripts/meson_benchmark.py b/scripts/meson_benchmark.py deleted file mode 100755 index 26f1f95..0000000 --- a/scripts/meson_benchmark.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/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/scripts/meson_install.py b/scripts/meson_install.py deleted file mode 100755 index e0a5eb2..0000000 --- a/scripts/meson_install.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/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) - -if __name__ == '__main__': - if len(sys.argv) != 2: - print('Installer script for Meson. Do not run on your own, mmm\'kay?') - print('%s [install info file]' % sys.argv[0]) - datafilename = sys.argv[1] - do_install(datafilename) - diff --git a/scripts/meson_test.py b/scripts/meson_test.py deleted file mode 100755 index 43b1cdb..0000000 --- a/scripts/meson_test.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/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 meson -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/scripts/mesonconf.py b/scripts/mesonconf.py deleted file mode 100755 index e53875f..0000000 --- a/scripts/mesonconf.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 - -# 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 sys, os -import pickle -import argparse -import coredata, mesonlib -from coredata import build_types, layouts, 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) - -if __name__ == '__main__': - args = mesonlib.expand_arguments(sys.argv[:]) - if not args: - sys.exit(1) - options = parser.parse_args(args[1:]) - if len(options.directory) > 1: - print('%s ' % sys.argv[0]) - print('If you omit the build directory, the current directory is substituted.') - sys.exit(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) - diff --git a/scripts/mesongui.py b/scripts/mesongui.py deleted file mode 100755 index bdd44bb..0000000 --- a/scripts/mesongui.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/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 -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.buildtype) - combo.currentTextChanged.connect(self.build_type_changed) - self.form.addRow('Build type', combo) - strip = QCheckBox("") - strip.setChecked(self.coredata.strip) - strip.stateChanged.connect(self.strip_changed) - self.form.addRow('Strip on install', strip) - coverage = QCheckBox("") - coverage.setChecked(self.coredata.coverage) - coverage.stateChanged.connect(self.coverage_changed) - self.form.addRow('Enable coverage', coverage) - pch = QCheckBox("") - pch.setChecked(self.coredata.use_pch) - pch.stateChanged.connect(self.pch_changed) - self.form.addRow('Enable pch', pch) - unity = QCheckBox("") - unity.setChecked(self.coredata.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 - -if __name__ == '__main__': - app = QApplication(sys.argv) - if len(sys.argv) == 1: - arg = "" - elif len(sys.argv) == 2: - arg = sys.argv[1] - else: - print(sys.argv[0], "") - sys.exit(1) - if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): - guirespawner = MesonGuiRespawner(arg) - else: - runner = Starter(arg) - sys.exit(app.exec_()) diff --git a/scripts/mesonintrospect.py b/scripts/mesonintrospect.py deleted file mode 100755 index 9fcd4db..0000000 --- a/scripts/mesonintrospect.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -"""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 -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)) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) > 1: - print('Too many arguments') - sys.exit(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') - sys.exit(1) diff --git a/scripts/regen_checker.py b/scripts/regen_checker.py deleted file mode 100755 index a0fe028..0000000 --- a/scripts/regen_checker.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - regeninfo = pickle.load(open(os.path.join(sys.argv[1], 'regeninfo.dump'), 'rb')) - if need_regen(regeninfo): - regen(regeninfo) - sys.exit(0) diff --git a/scripts/symbolextractor.py b/scripts/symbolextractor.py deleted file mode 100755 index fe86d35..0000000 --- a/scripts/symbolextractor.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/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. - -# 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 meson 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) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) != 2: - print(sys.argv[0], ' ') - sys.exit(1) - libfile = options.args[0] - outfile = options.args[1] - gen_symbols(libfile, outfile, options.cross_host) diff --git a/scripts/vcstagger.py b/scripts/vcstagger.py deleted file mode 100755 index ccc584e..0000000 --- a/scripts/vcstagger.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/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 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) - -if __name__ == '__main__': - infile, outfile, fallback, source_dir, replace_string, regex_selector = sys.argv[1:7] - command = sys.argv[7:] - config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/scripts/wraptool.py b/scripts/wraptool.py deleted file mode 100755 index 46860aa..0000000 --- a/scripts/wraptool.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/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 json -import sys, os -import configparser -import shutil - - -ssl_warning_printed = False - -from glob import glob - -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 [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 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 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)) - -if __name__ == '__main__': - if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': - print_help() - sys.exit(0) - command = sys.argv[1] - args = sys.argv[2:] - if command == 'list': - list_projects() - elif command == 'search': - if len(args) != 1: - print('Search requires exactly one argument.') - sys.exit(1) - search(args[0]) - elif command == 'install': - if len(args) != 1: - print('Install requires exactly one argument.') - sys.exit(1) - install(args[0]) - elif command == 'update': - if len(args) != 1: - print('update requires exactly one argument.') - sys.exit(1) - update(args[0]) - elif command == 'info': - if len(args) != 1: - print('info requires exactly one argument.') - sys.exit(1) - info(args[0]) - elif command == 'status': - status() - else: - print('Unknown command', command) - sys.exit(1) - diff --git a/wraptool.py b/wraptool.py new file mode 100755 index 0000000..46860aa --- /dev/null +++ b/wraptool.py @@ -0,0 +1,206 @@ +#!/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 json +import sys, os +import configparser +import shutil + + +ssl_warning_printed = False + +from glob import glob + +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 [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 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 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)) + +if __name__ == '__main__': + if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': + print_help() + sys.exit(0) + command = sys.argv[1] + args = sys.argv[2:] + if command == 'list': + list_projects() + elif command == 'search': + if len(args) != 1: + print('Search requires exactly one argument.') + sys.exit(1) + search(args[0]) + elif command == 'install': + if len(args) != 1: + print('Install requires exactly one argument.') + sys.exit(1) + install(args[0]) + elif command == 'update': + if len(args) != 1: + print('update requires exactly one argument.') + sys.exit(1) + update(args[0]) + elif command == 'info': + if len(args) != 1: + print('info requires exactly one argument.') + sys.exit(1) + info(args[0]) + elif command == 'status': + status() + else: + print('Unknown command', command) + sys.exit(1) + -- cgit v1.1 From 4c31e7774d09fe1a59b6cfd692de4f4cade1899c Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 00:04:57 +0200 Subject: Finalize moduleification so that full test suite runs. --- meson/interpreter.py | 12 ++++++++++-- meson/mesonmain.py | 7 ++++++- meson/ninjabackend.py | 13 ++++++++----- meson/scripts/meson_install.py | 2 +- run_cross_test.py | 4 ++-- run_tests.py | 8 ++++---- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/meson/interpreter.py b/meson/interpreter.py index 5ce2f8d..1a28f3f 100644 --- a/meson/interpreter.py +++ b/meson/interpreter.py @@ -1610,9 +1610,17 @@ class Interpreter(): regex_selector = vcs['rev_regex'] else: vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string - scriptfile = os.path.join(self.environment.get_script_dir(), 'vcstagger.py') # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command... - kwargs['command'] = [sys.executable, scriptfile, '@INPUT0@', '@OUTPUT0@', fallback, source_dir, replace_string, regex_selector] + vcs_cmd + 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) diff --git a/meson/mesonmain.py b/meson/mesonmain.py index 58ba06b..7b4f2c2 100644 --- a/meson/mesonmain.py +++ b/meson/mesonmain.py @@ -172,12 +172,18 @@ def run_script_command(args): elif cmdname == 'benchmark': import meson.scripts.meson_benchmark as abc cmdfunc = abc.run + elif cmdname == 'install': + import meson.scripts.meson_install as abc + cmdfunc = abc.run elif cmdname == 'commandrunner': import meson.scripts.commandrunner as abc cmdfunc = abc.run elif cmdname == 'delsuffix': import meson.scripts.delwithsuffix as abc cmdfunc = abc.run + elif cmdname == 'depfixer': + import meson.scripts.depfixer as abc + cmdfunc = abc.run elif cmdname == 'dirchanger': import meson.scripts.dirchanger as abc cmdfunc = abc.run @@ -210,7 +216,6 @@ def run(mainfile, args): handshake = True else: handshake = False - print(args) args = mesonlib.expand_arguments(args) if not args: return 1 diff --git a/meson/ninjabackend.py b/meson/ninjabackend.py index 80c94f9..36c5ce9 100644 --- a/meson/ninjabackend.py +++ b/meson/ninjabackend.py @@ -457,14 +457,14 @@ int dummy; 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 = os.path.join(script_root, 'depfixer.py') + 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, install_script, install_data_file]) + 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) @@ -1094,9 +1094,12 @@ int dummy; scriptdir = self.environment.get_script_dir() outfile.write('\n') symrule = 'rule SHSYM\n' - symcmd = ' command = "%s" "%s" %s %s $CROSS\n' % (ninja_quote(sys.executable), - ninja_quote(os.path.join(scriptdir, 'symbolextractor.py')), - '$in', '$out') + 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) diff --git a/meson/scripts/meson_install.py b/meson/scripts/meson_install.py index a286864..1ede757 100644 --- a/meson/scripts/meson_install.py +++ b/meson/scripts/meson_install.py @@ -193,7 +193,7 @@ def install_targets(d): 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], + p = subprocess.Popen(d.depfixer + [outname, install_rpath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdo, stde) = p.communicate() diff --git a/run_cross_test.py b/run_cross_test.py index 7355c28..3545cfd 100755 --- a/run_cross_test.py +++ b/run_cross_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2013-2014 The Meson development team +# 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. @@ -23,7 +23,7 @@ Not part of the main test suite because of two reasons: Eventually migrate to something fancier.''' import os, subprocess, shutil, sys -import environment +import meson.environment as environment from run_tests import gather_tests diff --git a/run_tests.py b/run_tests.py index e24c906..bf56ea8 100755 --- a/run_tests.py +++ b/run_tests.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2012-2015 The Meson development team +# 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. @@ -22,7 +22,7 @@ from meson import environment from meson import mesonlib from meson import mlog from meson import mesonmain -from scripts import meson_test, meson_benchmark +from meson.scripts import meson_test, meson_benchmark import argparse import xml.etree.ElementTree as ET import time @@ -45,7 +45,7 @@ print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ test_build_dir = 'work area' install_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'install dir') -meson_command = './meson.py' +meson_command = os.path.join(os.getcwd(), 'meson.py') class StopException(Exception): def __init__(self): @@ -155,7 +155,7 @@ def run_configure_inprocess(commandlist): old_stderr = sys.stderr sys.stderr = mystderr = StringIO() try: - returncode = mesonmain.run(commandlist) + returncode = mesonmain.run(commandlist[0], commandlist[1:]) finally: sys.stdout = old_stdout sys.stderr = old_stderr -- cgit v1.1 From ec44795f8ab3719c0d720a79efcc476d2499f259 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 17:00:46 +0200 Subject: Moved all wrap related things to their own submodule. --- meson/interpreter.py | 2 +- meson/modules/__init__.py | 0 meson/wrap.py | 204 -------------------------------------------- meson/wrap/wrap.py | 212 ++++++++++++++++++++++++++++++++++++++++++++++ meson/wrap/wraptool.py | 200 +++++++++++++++++++++++++++++++++++++++++++ wraptool | 20 +++++ wraptool.py | 206 -------------------------------------------- 7 files changed, 433 insertions(+), 411 deletions(-) delete mode 100644 meson/modules/__init__.py delete mode 100644 meson/wrap.py create mode 100644 meson/wrap/wrap.py create mode 100755 meson/wrap/wraptool.py create mode 100755 wraptool delete mode 100755 wraptool.py diff --git a/meson/interpreter.py b/meson/interpreter.py index 1a28f3f..1a32998 100644 --- a/meson/interpreter.py +++ b/meson/interpreter.py @@ -19,7 +19,7 @@ from . import dependencies from . import mlog from . import build from . import optinterpreter -from . import wrap +from .wrap import wrap from . import mesonlib import os, sys, platform, subprocess, shutil, uuid, re diff --git a/meson/modules/__init__.py b/meson/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/meson/wrap.py b/meson/wrap.py deleted file mode 100644 index ac9c9ce..0000000 --- a/meson/wrap.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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 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/meson/wrap/wrap.py b/meson/wrap/wrap.py new file mode 100644 index 0000000..2818fa0 --- /dev/null +++ b/meson/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/meson/wrap/wraptool.py b/meson/wrap/wraptool.py new file mode 100755 index 0000000..d2f0a28 --- /dev/null +++ b/meson/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 [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/wraptool b/wraptool new file mode 100755 index 0000000..46a2c26 --- /dev/null +++ b/wraptool @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Copyright 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 meson.wrap import wraptool +import sys + +sys.exit(wraptool.run(sys.argv[1:])) \ No newline at end of file diff --git a/wraptool.py b/wraptool.py deleted file mode 100755 index 46860aa..0000000 --- a/wraptool.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/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 json -import sys, os -import configparser -import shutil - - -ssl_warning_printed = False - -from glob import glob - -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 [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 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 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)) - -if __name__ == '__main__': - if len(sys.argv) < 2 or sys.argv[1] == '-h' or sys.argv[1] == '--help': - print_help() - sys.exit(0) - command = sys.argv[1] - args = sys.argv[2:] - if command == 'list': - list_projects() - elif command == 'search': - if len(args) != 1: - print('Search requires exactly one argument.') - sys.exit(1) - search(args[0]) - elif command == 'install': - if len(args) != 1: - print('Install requires exactly one argument.') - sys.exit(1) - install(args[0]) - elif command == 'update': - if len(args) != 1: - print('update requires exactly one argument.') - sys.exit(1) - update(args[0]) - elif command == 'info': - if len(args) != 1: - print('info requires exactly one argument.') - sys.exit(1) - info(args[0]) - elif command == 'status': - status() - else: - print('Unknown command', command) - sys.exit(1) - -- cgit v1.1 From 2ee27504a83dca9982c50459e46f1b39108dc278 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 17:07:43 +0200 Subject: Moved mesonconf implementation in the module. --- meson/mconf.py | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mesonconf | 20 ++++++ mesonconf.py | 205 ------------------------------------------------------- 3 files changed, 229 insertions(+), 205 deletions(-) create mode 100644 meson/mconf.py create mode 100755 mesonconf delete mode 100644 mesonconf.py diff --git a/meson/mconf.py b/meson/mconf.py new file mode 100644 index 0000000..f174425 --- /dev/null +++ b/meson/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 ' % 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/mesonconf b/mesonconf new file mode 100755 index 0000000..3b5b5e9 --- /dev/null +++ b/mesonconf @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Copyright 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 meson import mconf +import sys + +sys.exit(mconf.run(sys.argv[1:])) diff --git a/mesonconf.py b/mesonconf.py deleted file mode 100644 index e53875f..0000000 --- a/mesonconf.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 - -# 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 sys, os -import pickle -import argparse -import coredata, mesonlib -from coredata import build_types, layouts, 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) - -if __name__ == '__main__': - args = mesonlib.expand_arguments(sys.argv[:]) - if not args: - sys.exit(1) - options = parser.parse_args(args[1:]) - if len(options.directory) > 1: - print('%s ' % sys.argv[0]) - print('If you omit the build directory, the current directory is substituted.') - sys.exit(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) - -- cgit v1.1 From 1510522b1b9970376a1e1cc5f39e00d8749ec19a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 17:21:05 +0200 Subject: Moved mesongui into module. --- meson/mesonmain.ui | 248 ++++++++++++++++++++++ meson/mesonrunner.ui | 52 +++++ meson/mesonstart.ui | 119 +++++++++++ meson/mgui.py | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ meson/mintro.py | 212 +++++++++++++++++++ mesongui | 20 ++ mesongui.py | 561 -------------------------------------------------- mesonintrospect | 20 ++ mesonintrospect.py | 208 ------------------- mesonmain.ui | 248 ---------------------- mesonrunner.ui | 52 ----- mesonstart.ui | 119 ----------- 12 files changed, 1236 insertions(+), 1188 deletions(-) create mode 100644 meson/mesonmain.ui create mode 100644 meson/mesonrunner.ui create mode 100644 meson/mesonstart.ui create mode 100644 meson/mgui.py create mode 100644 meson/mintro.py create mode 100755 mesongui delete mode 100644 mesongui.py create mode 100755 mesonintrospect delete mode 100644 mesonintrospect.py delete mode 100644 mesonmain.ui delete mode 100644 mesonrunner.ui delete mode 100644 mesonstart.ui diff --git a/meson/mesonmain.ui b/meson/mesonmain.ui new file mode 100644 index 0000000..209584b --- /dev/null +++ b/meson/mesonmain.ui @@ -0,0 +1,248 @@ + + + MainWindow + + + + 0 + 0 + 740 + 613 + + + + Meson + + + + + + + Project + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Source directory + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + + 0 + 0 + + + + Build directory + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Build type + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Backend + + + + + + + Ninja + + + + + + + 2 + + + + Core data + + + + + + + + + + Paths + + + + + + + + + + Options + + + + + + + + + + Dependencies + + + + + + + + + + Build targets + + + + + + + + + + + + + + + Save + + + + + + + Compile + + + + + + + Run tests + + + + + + + Install + + + + + + + Clean + + + + + + + + + + + 0 + 0 + 740 + 25 + + + + + File + + + + + + + + + + &Save + + + + + &Quit + + + + + + diff --git a/meson/mesonrunner.ui b/meson/mesonrunner.ui new file mode 100644 index 0000000..942c6bd --- /dev/null +++ b/meson/mesonrunner.ui @@ -0,0 +1,52 @@ + + + rundialog + + + + 0 + 0 + 581 + 368 + + + + External process output + + + true + + + + + + Compile time: 0:0 + + + + + + + Terminate + + + + + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + + + diff --git a/meson/mesonstart.ui b/meson/mesonstart.ui new file mode 100644 index 0000000..c6c5f96 --- /dev/null +++ b/meson/mesonstart.ui @@ -0,0 +1,119 @@ + + + MainWindow + + + + 0 + 0 + 644 + 192 + + + + Meson + + + + + + + Source directory + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + Build directory + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + Cross file + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + + 0 + 0 + + + + Generate + + + + + + + + + 0 + 0 + 644 + 25 + + + + + + + + diff --git a/meson/mgui.py b/meson/mgui.py new file mode 100644 index 0000000..6e57ce7 --- /dev/null +++ b/meson/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], "") + 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/meson/mintro.py b/meson/mintro.py new file mode 100644 index 0000000..b088117 --- /dev/null +++ b/meson/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/mesongui b/mesongui new file mode 100755 index 0000000..c1cd802 --- /dev/null +++ b/mesongui @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Copyright 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 meson import mgui +import sys + +sys.exit(mgui.run(sys.argv)) diff --git a/mesongui.py b/mesongui.py deleted file mode 100644 index bdd44bb..0000000 --- a/mesongui.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/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 -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.buildtype) - combo.currentTextChanged.connect(self.build_type_changed) - self.form.addRow('Build type', combo) - strip = QCheckBox("") - strip.setChecked(self.coredata.strip) - strip.stateChanged.connect(self.strip_changed) - self.form.addRow('Strip on install', strip) - coverage = QCheckBox("") - coverage.setChecked(self.coredata.coverage) - coverage.stateChanged.connect(self.coverage_changed) - self.form.addRow('Enable coverage', coverage) - pch = QCheckBox("") - pch.setChecked(self.coredata.use_pch) - pch.stateChanged.connect(self.pch_changed) - self.form.addRow('Enable pch', pch) - unity = QCheckBox("") - unity.setChecked(self.coredata.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 - -if __name__ == '__main__': - app = QApplication(sys.argv) - if len(sys.argv) == 1: - arg = "" - elif len(sys.argv) == 2: - arg = sys.argv[1] - else: - print(sys.argv[0], "") - sys.exit(1) - if os.path.exists(os.path.join(arg, 'meson-private/coredata.dat')): - guirespawner = MesonGuiRespawner(arg) - else: - runner = Starter(arg) - sys.exit(app.exec_()) diff --git a/mesonintrospect b/mesonintrospect new file mode 100755 index 0000000..94c05ea --- /dev/null +++ b/mesonintrospect @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Copyright 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 meson import mintro +import sys + +sys.exit(mintro.run(sys.argv[1:])) diff --git a/mesonintrospect.py b/mesonintrospect.py deleted file mode 100644 index 9fcd4db..0000000 --- a/mesonintrospect.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -"""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 -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)) - -if __name__ == '__main__': - options = parser.parse_args() - if len(options.args) > 1: - print('Too many arguments') - sys.exit(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') - sys.exit(1) diff --git a/mesonmain.ui b/mesonmain.ui deleted file mode 100644 index 209584b..0000000 --- a/mesonmain.ui +++ /dev/null @@ -1,248 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 740 - 613 - - - - Meson - - - - - - - Project - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Source directory - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - - 0 - 0 - - - - Build directory - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Build type - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Backend - - - - - - - Ninja - - - - - - - 2 - - - - Core data - - - - - - - - - - Paths - - - - - - - - - - Options - - - - - - - - - - Dependencies - - - - - - - - - - Build targets - - - - - - - - - - - - - - - Save - - - - - - - Compile - - - - - - - Run tests - - - - - - - Install - - - - - - - Clean - - - - - - - - - - - 0 - 0 - 740 - 25 - - - - - File - - - - - - - - - - &Save - - - - - &Quit - - - - - - diff --git a/mesonrunner.ui b/mesonrunner.ui deleted file mode 100644 index 942c6bd..0000000 --- a/mesonrunner.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - rundialog - - - - 0 - 0 - 581 - 368 - - - - External process output - - - true - - - - - - Compile time: 0:0 - - - - - - - Terminate - - - - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - - - - - diff --git a/mesonstart.ui b/mesonstart.ui deleted file mode 100644 index c6c5f96..0000000 --- a/mesonstart.ui +++ /dev/null @@ -1,119 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 644 - 192 - - - - Meson - - - - - - - Source directory - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - Build directory - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - Cross file - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - - 0 - 0 - - - - Generate - - - - - - - - - 0 - 0 - 644 - 25 - - - - - - - - -- cgit v1.1 From 23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 17:35:29 +0200 Subject: Renamed meson package to mesonbuild so that we can have a script named meson in the same toplevel dir. --- meson | 24 + meson.py | 24 - meson/__init__.py | 0 meson/backends.py | 423 ------ meson/build.py | 969 -------------- meson/compilers.py | 1837 --------------------------- meson/coredata.py | 222 ---- meson/dependencies.py | 1120 ---------------- meson/environment.py | 673 ---------- meson/interpreter.py | 2259 --------------------------------- meson/mconf.py | 209 --- meson/mesonlib.py | 284 ----- meson/mesonmain.py | 263 ---- meson/mesonmain.ui | 248 ---- meson/mesonrunner.ui | 52 - meson/mesonstart.ui | 119 -- meson/mgui.py | 565 --------- meson/mintro.py | 212 ---- meson/mlog.py | 81 -- meson/modules/gnome.py | 330 ----- meson/modules/modtest.py | 21 - meson/modules/pkgconfig.py | 82 -- meson/modules/qt4.py | 155 --- meson/modules/qt5.py | 162 --- meson/modules/rpm.py | 163 --- meson/modules/windows.py | 47 - meson/mparser.py | 565 --------- meson/ninjabackend.py | 1819 -------------------------- meson/optinterpreter.py | 148 --- meson/scripts/commandrunner.py | 59 - meson/scripts/delwithsuffix.py | 37 - meson/scripts/depfixer.py | 302 ----- meson/scripts/dirchanger.py | 30 - meson/scripts/gtkdochelper.py | 122 -- meson/scripts/meson_benchmark.py | 97 -- meson/scripts/meson_install.py | 215 ---- meson/scripts/meson_test.py | 233 ---- meson/scripts/regen_checker.py | 45 - meson/scripts/symbolextractor.py | 106 -- meson/scripts/vcstagger.py | 36 - meson/vs2010backend.py | 640 ---------- meson/wrap/wrap.py | 212 ---- meson/wrap/wraptool.py | 200 --- meson/xcodebackend.py | 775 ----------- mesonbuild/__init__.py | 0 mesonbuild/backends.py | 423 ++++++ mesonbuild/build.py | 969 ++++++++++++++ mesonbuild/compilers.py | 1837 +++++++++++++++++++++++++++ mesonbuild/coredata.py | 222 ++++ mesonbuild/dependencies.py | 1120 ++++++++++++++++ mesonbuild/environment.py | 673 ++++++++++ mesonbuild/interpreter.py | 2259 +++++++++++++++++++++++++++++++++ mesonbuild/mconf.py | 209 +++ mesonbuild/mesonlib.py | 284 +++++ mesonbuild/mesonmain.py | 262 ++++ mesonbuild/mesonmain.ui | 248 ++++ mesonbuild/mesonrunner.ui | 52 + mesonbuild/mesonstart.ui | 119 ++ mesonbuild/mgui.py | 565 +++++++++ mesonbuild/mintro.py | 212 ++++ mesonbuild/mlog.py | 81 ++ mesonbuild/modules/gnome.py | 330 +++++ mesonbuild/modules/modtest.py | 21 + mesonbuild/modules/pkgconfig.py | 82 ++ mesonbuild/modules/qt4.py | 155 +++ mesonbuild/modules/qt5.py | 162 +++ mesonbuild/modules/rpm.py | 163 +++ mesonbuild/modules/windows.py | 47 + mesonbuild/mparser.py | 565 +++++++++ mesonbuild/ninjabackend.py | 1819 ++++++++++++++++++++++++++ mesonbuild/optinterpreter.py | 148 +++ mesonbuild/scripts/commandrunner.py | 59 + mesonbuild/scripts/delwithsuffix.py | 37 + mesonbuild/scripts/depfixer.py | 302 +++++ mesonbuild/scripts/dirchanger.py | 30 + mesonbuild/scripts/gtkdochelper.py | 122 ++ mesonbuild/scripts/meson_benchmark.py | 97 ++ mesonbuild/scripts/meson_install.py | 215 ++++ mesonbuild/scripts/meson_test.py | 233 ++++ mesonbuild/scripts/regen_checker.py | 45 + mesonbuild/scripts/symbolextractor.py | 106 ++ mesonbuild/scripts/vcstagger.py | 36 + mesonbuild/vs2010backend.py | 640 ++++++++++ mesonbuild/wrap/wrap.py | 212 ++++ mesonbuild/wrap/wraptool.py | 200 +++ mesonbuild/xcodebackend.py | 775 +++++++++++ mesonconf | 2 +- mesongui | 2 +- mesonintrospect | 2 +- run_cross_test.py | 2 +- run_tests.py | 14 +- 91 files changed, 16171 insertions(+), 16172 deletions(-) create mode 100755 meson delete mode 100755 meson.py delete mode 100644 meson/__init__.py delete mode 100644 meson/backends.py delete mode 100644 meson/build.py delete mode 100644 meson/compilers.py delete mode 100644 meson/coredata.py delete mode 100644 meson/dependencies.py delete mode 100644 meson/environment.py delete mode 100644 meson/interpreter.py delete mode 100644 meson/mconf.py delete mode 100644 meson/mesonlib.py delete mode 100644 meson/mesonmain.py delete mode 100644 meson/mesonmain.ui delete mode 100644 meson/mesonrunner.ui delete mode 100644 meson/mesonstart.ui delete mode 100644 meson/mgui.py delete mode 100644 meson/mintro.py delete mode 100644 meson/mlog.py delete mode 100644 meson/modules/gnome.py delete mode 100644 meson/modules/modtest.py delete mode 100644 meson/modules/pkgconfig.py delete mode 100644 meson/modules/qt4.py delete mode 100644 meson/modules/qt5.py delete mode 100644 meson/modules/rpm.py delete mode 100644 meson/modules/windows.py delete mode 100644 meson/mparser.py delete mode 100644 meson/ninjabackend.py delete mode 100644 meson/optinterpreter.py delete mode 100644 meson/scripts/commandrunner.py delete mode 100644 meson/scripts/delwithsuffix.py delete mode 100644 meson/scripts/depfixer.py delete mode 100644 meson/scripts/dirchanger.py delete mode 100644 meson/scripts/gtkdochelper.py delete mode 100644 meson/scripts/meson_benchmark.py delete mode 100644 meson/scripts/meson_install.py delete mode 100644 meson/scripts/meson_test.py delete mode 100644 meson/scripts/regen_checker.py delete mode 100644 meson/scripts/symbolextractor.py delete mode 100644 meson/scripts/vcstagger.py delete mode 100644 meson/vs2010backend.py delete mode 100644 meson/wrap/wrap.py delete mode 100755 meson/wrap/wraptool.py delete mode 100644 meson/xcodebackend.py create mode 100644 mesonbuild/__init__.py create mode 100644 mesonbuild/backends.py create mode 100644 mesonbuild/build.py create mode 100644 mesonbuild/compilers.py create mode 100644 mesonbuild/coredata.py create mode 100644 mesonbuild/dependencies.py create mode 100644 mesonbuild/environment.py create mode 100644 mesonbuild/interpreter.py create mode 100644 mesonbuild/mconf.py create mode 100644 mesonbuild/mesonlib.py create mode 100644 mesonbuild/mesonmain.py create mode 100644 mesonbuild/mesonmain.ui create mode 100644 mesonbuild/mesonrunner.ui create mode 100644 mesonbuild/mesonstart.ui create mode 100644 mesonbuild/mgui.py create mode 100644 mesonbuild/mintro.py create mode 100644 mesonbuild/mlog.py create mode 100644 mesonbuild/modules/gnome.py create mode 100644 mesonbuild/modules/modtest.py create mode 100644 mesonbuild/modules/pkgconfig.py create mode 100644 mesonbuild/modules/qt4.py create mode 100644 mesonbuild/modules/qt5.py create mode 100644 mesonbuild/modules/rpm.py create mode 100644 mesonbuild/modules/windows.py create mode 100644 mesonbuild/mparser.py create mode 100644 mesonbuild/ninjabackend.py create mode 100644 mesonbuild/optinterpreter.py create mode 100644 mesonbuild/scripts/commandrunner.py create mode 100644 mesonbuild/scripts/delwithsuffix.py create mode 100644 mesonbuild/scripts/depfixer.py create mode 100644 mesonbuild/scripts/dirchanger.py create mode 100644 mesonbuild/scripts/gtkdochelper.py create mode 100644 mesonbuild/scripts/meson_benchmark.py create mode 100644 mesonbuild/scripts/meson_install.py create mode 100644 mesonbuild/scripts/meson_test.py create mode 100644 mesonbuild/scripts/regen_checker.py create mode 100644 mesonbuild/scripts/symbolextractor.py create mode 100644 mesonbuild/scripts/vcstagger.py create mode 100644 mesonbuild/vs2010backend.py create mode 100644 mesonbuild/wrap/wrap.py create mode 100755 mesonbuild/wrap/wraptool.py create mode 100644 mesonbuild/xcodebackend.py diff --git a/meson b/meson new file mode 100755 index 0000000..b977368 --- /dev/null +++ b/meson @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +# Copyright 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 mesonbuild import mesonmain +import sys, os + +thisfile = __file__ +if not os.path.isabs(thisfile): + thisfile = os.path.normpath(os.path.join(os.getcwd(), thisfile)) + +sys.exit(mesonmain.run(thisfile, sys.argv[1:])) diff --git a/meson.py b/meson.py deleted file mode 100755 index ab8db72..0000000 --- a/meson.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 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 meson import mesonmain -import sys, os - -thisfile = __file__ -if not os.path.isabs(thisfile): - thisfile = os.path.join(os.getcwd(), thisfile) - -sys.exit(mesonmain.run(thisfile, sys.argv[1:])) diff --git a/meson/__init__.py b/meson/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/meson/backends.py b/meson/backends.py deleted file mode 100644 index c583a7b..0000000 --- a/meson/backends.py +++ /dev/null @@ -1,423 +0,0 @@ -# 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/meson/build.py b/meson/build.py deleted file mode 100644 index c0ba895..0000000 --- a/meson/build.py +++ /dev/null @@ -1,969 +0,0 @@ -# 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/meson/compilers.py b/meson/compilers.py deleted file mode 100644 index ec0181e..0000000 --- a/meson/compilers.py +++ /dev/null @@ -1,1837 +0,0 @@ -# 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 -%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 -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 -#include - -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\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\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/meson/coredata.py b/meson/coredata.py deleted file mode 100644 index 5b1102c..0000000 --- a/meson/coredata.py +++ /dev/null @@ -1,222 +0,0 @@ -# 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/meson/dependencies.py b/meson/dependencies.py deleted file mode 100644 index 974559f..0000000 --- a/meson/dependencies.py +++ /dev/null @@ -1,1120 +0,0 @@ -# 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/meson/environment.py b/meson/environment.py deleted file mode 100644 index 8df856c..0000000 --- a/meson/environment.py +++ /dev/null @@ -1,673 +0,0 @@ -# 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/meson/interpreter.py b/meson/interpreter.py deleted file mode 100644 index 1a32998..0000000 --- a/meson/interpreter.py +++ /dev/null @@ -1,2259 +0,0 @@ -# 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('meson.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/meson/mconf.py b/meson/mconf.py deleted file mode 100644 index f174425..0000000 --- a/meson/mconf.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/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 ' % 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/meson/mesonlib.py b/meson/mesonlib.py deleted file mode 100644 index 2ab5ce4..0000000 --- a/meson/mesonlib.py +++ /dev/null @@ -1,284 +0,0 @@ -# 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/meson/mesonmain.py b/meson/mesonmain.py deleted file mode 100644 index 7b4f2c2..0000000 --- a/meson/mesonmain.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/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 meson.scripts.meson_test as abc - cmdfunc = abc.run - elif cmdname == 'benchmark': - import meson.scripts.meson_benchmark as abc - cmdfunc = abc.run - elif cmdname == 'install': - import meson.scripts.meson_install as abc - cmdfunc = abc.run - elif cmdname == 'commandrunner': - import meson.scripts.commandrunner as abc - cmdfunc = abc.run - elif cmdname == 'delsuffix': - import meson.scripts.delwithsuffix as abc - cmdfunc = abc.run - elif cmdname == 'depfixer': - import meson.scripts.depfixer as abc - cmdfunc = abc.run - elif cmdname == 'dirchanger': - import meson.scripts.dirchanger as abc - cmdfunc = abc.run - elif cmdname == 'gtkdoc': - import meson.scripts.gtkdochelper as abc - cmdfunc = abc.run - elif cmdname == 'regencheck': - import meson.scripts.regen_checker as abc - cmdfunc = abc.run - elif cmdname == 'symbolextractor': - import meson.scripts.symbolextractor as abc - cmdfunc = abc.run - elif cmdname == 'vcstagger': - import meson.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 ' % 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/meson/mesonmain.ui b/meson/mesonmain.ui deleted file mode 100644 index 209584b..0000000 --- a/meson/mesonmain.ui +++ /dev/null @@ -1,248 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 740 - 613 - - - - Meson - - - - - - - Project - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Source directory - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - - 0 - 0 - - - - Build directory - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Build type - - - - - - - - 1 - 0 - - - - TextLabel - - - - - - - Backend - - - - - - - Ninja - - - - - - - 2 - - - - Core data - - - - - - - - - - Paths - - - - - - - - - - Options - - - - - - - - - - Dependencies - - - - - - - - - - Build targets - - - - - - - - - - - - - - - Save - - - - - - - Compile - - - - - - - Run tests - - - - - - - Install - - - - - - - Clean - - - - - - - - - - - 0 - 0 - 740 - 25 - - - - - File - - - - - - - - - - &Save - - - - - &Quit - - - - - - diff --git a/meson/mesonrunner.ui b/meson/mesonrunner.ui deleted file mode 100644 index 942c6bd..0000000 --- a/meson/mesonrunner.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - rundialog - - - - 0 - 0 - 581 - 368 - - - - External process output - - - true - - - - - - Compile time: 0:0 - - - - - - - Terminate - - - - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - - - - - diff --git a/meson/mesonstart.ui b/meson/mesonstart.ui deleted file mode 100644 index c6c5f96..0000000 --- a/meson/mesonstart.ui +++ /dev/null @@ -1,119 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 644 - 192 - - - - Meson - - - - - - - Source directory - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - Build directory - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - Cross file - - - - - - - - 1 - 0 - - - - - - - - Browse - - - - - - - - 0 - 0 - - - - Generate - - - - - - - - - 0 - 0 - 644 - 25 - - - - - - - - diff --git a/meson/mgui.py b/meson/mgui.py deleted file mode 100644 index 6e57ce7..0000000 --- a/meson/mgui.py +++ /dev/null @@ -1,565 +0,0 @@ -#!/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], "") - 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/meson/mintro.py b/meson/mintro.py deleted file mode 100644 index b088117..0000000 --- a/meson/mintro.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/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/meson/mlog.py b/meson/mlog.py deleted file mode 100644 index 2807c2b..0000000 --- a/meson/mlog.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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/meson/modules/gnome.py b/meson/modules/gnome.py deleted file mode 100644 index e552b84..0000000 --- a/meson/modules/gnome.py +++ /dev/null @@ -1,330 +0,0 @@ -# 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/meson/modules/modtest.py b/meson/modules/modtest.py deleted file mode 100644 index c9247e6..0000000 --- a/meson/modules/modtest.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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/meson/modules/pkgconfig.py b/meson/modules/pkgconfig.py deleted file mode 100644 index f18decf..0000000 --- a/meson/modules/pkgconfig.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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/meson/modules/qt4.py b/meson/modules/qt4.py deleted file mode 100644 index 162b553..0000000 --- a/meson/modules/qt4.py +++ /dev/null @@ -1,155 +0,0 @@ -# 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/meson/modules/qt5.py b/meson/modules/qt5.py deleted file mode 100644 index 81edc76..0000000 --- a/meson/modules/qt5.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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/meson/modules/rpm.py b/meson/modules/rpm.py deleted file mode 100644 index a2c0502..0000000 --- a/meson/modules/rpm.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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 - \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/meson/modules/windows.py b/meson/modules/windows.py deleted file mode 100644 index a785250..0000000 --- a/meson/modules/windows.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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/meson/mparser.py b/meson/mparser.py deleted file mode 100644 index 1d569d5..0000000 --- a/meson/mparser.py +++ /dev/null @@ -1,565 +0,0 @@ -# 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/meson/ninjabackend.py b/meson/ninjabackend.py deleted file mode 100644 index 36c5ce9..0000000 --- a/meson/ninjabackend.py +++ /dev/null @@ -1,1819 +0,0 @@ -# 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 -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/meson/optinterpreter.py b/meson/optinterpreter.py deleted file mode 100644 index f0c93ae..0000000 --- a/meson/optinterpreter.py +++ /dev/null @@ -1,148 +0,0 @@ -# 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/meson/scripts/commandrunner.py b/meson/scripts/commandrunner.py deleted file mode 100644 index f5a2fff..0000000 --- a/meson/scripts/commandrunner.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/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 [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/meson/scripts/delwithsuffix.py b/meson/scripts/delwithsuffix.py deleted file mode 100644 index 38ab406..0000000 --- a/meson/scripts/delwithsuffix.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/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 ') - 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/meson/scripts/depfixer.py b/meson/scripts/depfixer.py deleted file mode 100644 index 1ab83b6..0000000 --- a/meson/scripts/depfixer.py +++ /dev/null @@ -1,302 +0,0 @@ -#!/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: ' % 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/meson/scripts/dirchanger.py b/meson/scripts/dirchanger.py deleted file mode 100644 index 93a901d..0000000 --- a/meson/scripts/dirchanger.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/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/meson/scripts/gtkdochelper.py b/meson/scripts/gtkdochelper.py deleted file mode 100644 index 68be8f2..0000000 --- a/meson/scripts/gtkdochelper.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/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/meson/scripts/meson_benchmark.py b/meson/scripts/meson_benchmark.py deleted file mode 100644 index 26f1f95..0000000 --- a/meson/scripts/meson_benchmark.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/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/meson/scripts/meson_install.py b/meson/scripts/meson_install.py deleted file mode 100644 index 1ede757..0000000 --- a/meson/scripts/meson_install.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/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/meson/scripts/meson_test.py b/meson/scripts/meson_test.py deleted file mode 100644 index c5814ef..0000000 --- a/meson/scripts/meson_test.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/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 meson -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/meson/scripts/regen_checker.py b/meson/scripts/regen_checker.py deleted file mode 100644 index f360a7c..0000000 --- a/meson/scripts/regen_checker.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/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/meson/scripts/symbolextractor.py b/meson/scripts/symbolextractor.py deleted file mode 100644 index 9607466..0000000 --- a/meson/scripts/symbolextractor.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/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 meson 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 ') - 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/meson/scripts/vcstagger.py b/meson/scripts/vcstagger.py deleted file mode 100644 index 390e37a..0000000 --- a/meson/scripts/vcstagger.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/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/meson/vs2010backend.py b/meson/vs2010backend.py deleted file mode 100644 index 33e9646..0000000 --- a/meson/vs2010backend.py +++ /dev/null @@ -1,640 +0,0 @@ -# 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 ['"%s"' % 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 " into &quot; whereas MSVS - # requires quoted but not fixed elements. Enter horrible hack. - txt = open(ofname, 'r').read() - open(ofname, 'w').write(txt.replace('&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/meson/wrap/wrap.py b/meson/wrap/wrap.py deleted file mode 100644 index 2818fa0..0000000 --- a/meson/wrap/wrap.py +++ /dev/null @@ -1,212 +0,0 @@ -# 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/meson/wrap/wraptool.py b/meson/wrap/wraptool.py deleted file mode 100755 index d2f0a28..0000000 --- a/meson/wrap/wraptool.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/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 [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/meson/xcodebackend.py b/meson/xcodebackend.py deleted file mode 100644 index 8ac3f67..0000000 --- a/meson/xcodebackend.py +++ /dev/null @@ -1,775 +0,0 @@ -# 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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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 = "";') - 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') diff --git a/mesonbuild/__init__.py b/mesonbuild/__init__.py new file mode 100644 index 0000000..e69de29 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 +%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 +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 +#include + +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\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\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 ' % 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 ' % 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 @@ + + + MainWindow + + + + 0 + 0 + 740 + 613 + + + + Meson + + + + + + + Project + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Source directory + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + + 0 + 0 + + + + Build directory + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Build type + + + + + + + + 1 + 0 + + + + TextLabel + + + + + + + Backend + + + + + + + Ninja + + + + + + + 2 + + + + Core data + + + + + + + + + + Paths + + + + + + + + + + Options + + + + + + + + + + Dependencies + + + + + + + + + + Build targets + + + + + + + + + + + + + + + Save + + + + + + + Compile + + + + + + + Run tests + + + + + + + Install + + + + + + + Clean + + + + + + + + + + + 0 + 0 + 740 + 25 + + + + + File + + + + + + + + + + &Save + + + + + &Quit + + + + + + 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 @@ + + + rundialog + + + + 0 + 0 + 581 + 368 + + + + External process output + + + true + + + + + + Compile time: 0:0 + + + + + + + Terminate + + + + + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + + + + 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 @@ + + + MainWindow + + + + 0 + 0 + 644 + 192 + + + + Meson + + + + + + + Source directory + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + Build directory + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + Cross file + + + + + + + + 1 + 0 + + + + + + + + Browse + + + + + + + + 0 + 0 + + + + Generate + + + + + + + + + 0 + 0 + 644 + 25 + + + + + + + + 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], "") + 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 - \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 +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 [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 ') + 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: ' % 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 ') + 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 ['"%s"' % 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 " into &quot; whereas MSVS + # requires quoted but not fixed elements. Enter horrible hack. + txt = open(ofname, 'r').read() + open(ofname, 'w').write(txt.replace('&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 [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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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 = "";') + 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') diff --git a/mesonconf b/mesonconf index 3b5b5e9..2b0a1a6 100755 --- a/mesonconf +++ b/mesonconf @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from meson import mconf +from mesonbuild import mconf import sys sys.exit(mconf.run(sys.argv[1:])) diff --git a/mesongui b/mesongui index c1cd802..9e16b00 100755 --- a/mesongui +++ b/mesongui @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from meson import mgui +from mesonbuild import mgui import sys sys.exit(mgui.run(sys.argv)) diff --git a/mesonintrospect b/mesonintrospect index 94c05ea..4d20548 100755 --- a/mesonintrospect +++ b/mesonintrospect @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from meson import mintro +from mesonbuild import mintro import sys sys.exit(mintro.run(sys.argv[1:])) diff --git a/run_cross_test.py b/run_cross_test.py index 3545cfd..a788850 100755 --- a/run_cross_test.py +++ b/run_cross_test.py @@ -23,7 +23,7 @@ Not part of the main test suite because of two reasons: Eventually migrate to something fancier.''' import os, subprocess, shutil, sys -import meson.environment as environment +import mesonbuild.environment as environment from run_tests import gather_tests diff --git a/run_tests.py b/run_tests.py index bf56ea8..2211949 100755 --- a/run_tests.py +++ b/run_tests.py @@ -18,16 +18,16 @@ from glob import glob import os, subprocess, shutil, sys, signal from io import StringIO import sys -from meson import environment -from meson import mesonlib -from meson import mlog -from meson import mesonmain -from meson.scripts import meson_test, meson_benchmark +from mesonbuild import environment +from mesonbuild import mesonlib +from mesonbuild import mlog +from mesonbuild import mesonmain +from mesonbuild.scripts import meson_test, meson_benchmark import argparse import xml.etree.ElementTree as ET import time -from meson.mesonmain import backendlist +from mesonbuild.mesonmain import backendlist class TestResult: def __init__(self, msg, stdo, stde, conftime=0, buildtime=0, testtime=0): @@ -45,7 +45,7 @@ print_debug = 'MESON_PRINT_TEST_OUTPUT' in os.environ test_build_dir = 'work area' install_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'install dir') -meson_command = os.path.join(os.getcwd(), 'meson.py') +meson_command = os.path.join(os.getcwd(), 'meson') class StopException(Exception): def __init__(self): -- cgit v1.1 From d6e176f45589485786fe87ba7ef15fe7fd8302d1 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 18:04:59 +0200 Subject: Made gtkdoc and run targets work. --- mesonbuild/mesonmain.py | 2 +- mesonbuild/modules/gnome.py | 8 ++++---- mesonbuild/ninjabackend.py | 4 ++-- mesonbuild/scripts/commandrunner.py | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 82f30fe..7b0834c 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -188,7 +188,7 @@ def run_script_command(args): import mesonbuild.scripts.dirchanger as abc cmdfunc = abc.run elif cmdname == 'gtkdoc': - import meson.scripts.gtkdochelper as abc + import mesonbuild.scripts.gtkdochelper as abc cmdfunc = abc.run elif cmdname == 'regencheck': import mesonbuild.scripts.regen_checker as abc diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index e552b84..a351694 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -260,11 +260,11 @@ class GnomeModule: 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")) + command = [state.environment.get_build_command(), '--internal', 'gtkdoc'] 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.') + raise MesonException('Invalid 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.') @@ -279,9 +279,9 @@ class GnomeModule: '--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)] + res = [build.RunTarget(targetname, command[0], command[1:] + args, state.subdir)] if kwargs.get('install', True): - res.append(build.InstallScript([command] + args)) + res.append(build.InstallScript(command + args)) return res def unpack_args(self, arg, kwarg_name, kwargs): diff --git a/mesonbuild/ninjabackend.py b/mesonbuild/ninjabackend.py index 36c5ce9..89aa573 100644 --- a/mesonbuild/ninjabackend.py +++ b/mesonbuild/ninjabackend.py @@ -362,7 +362,7 @@ int dummy; 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') + runnerscript = [sys.executable, self.environment.get_build_command(), '--internal', 'commandrunner'] deps = [] arg_strings = [] for i in target.args: @@ -376,7 +376,7 @@ int dummy; 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] + cmd = runnerscript + [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir] texe = target.command try: texe = texe.held_object diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py index f5a2fff..1c37f5c 100644 --- a/mesonbuild/scripts/commandrunner.py +++ b/mesonbuild/scripts/commandrunner.py @@ -43,14 +43,14 @@ def run_command(source_dir, build_dir, subdir, command, arguments): sys.exit(1) def run(args): - if len(sys.argv) < 4: + if len(args) < 4: print('commandrunner.py [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:] + return 1 + src_dir = args[0] + build_dir = args[1] + subdir = args[2] + command = args[3] + arguments = args[4:] pc = run_command(src_dir, build_dir, subdir, command, arguments) pc.wait() return pc.returncode -- cgit v1.1 From 53688207ab547260dceced9159bdbed1684b039a Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 19:50:29 +0200 Subject: Added distutils setup.py. --- .gitignore | 3 ++ MANIFEST.in | 14 +++++++ install_meson.py | 120 ------------------------------------------------------- readme.txt | 13 ++---- setup.py | 50 +++++++++++++++++++++++ 5 files changed, 70 insertions(+), 130 deletions(-) create mode 100644 MANIFEST.in delete mode 100755 install_meson.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 4df65c8..ddd6e0e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ __pycache__ .DS_Store *~ packagecache +/MANIFEST +/build +/dist diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..d0b5ffb --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,14 @@ +graft test?cases +graft manual?tests +graft cross +graft data +graft graphics +graft tools +include run_tests.py +include run_cross_test.py +include readme.txt +include authors.txt +include contributing.txt +include COPYING +include run_cross_test.py +include run_tests.py diff --git a/install_meson.py b/install_meson.py deleted file mode 100755 index f942a9b..0000000 --- a/install_meson.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/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. - -# This script installs Meson. We can't use Meson to install itself -# because of the bootstrap problem. We can't use any other build system -# either becaust that would be just silly. - -import os, sys, glob, shutil, gzip -import argparse - -parser = argparse.ArgumentParser() - -parser.add_argument('--prefix', default='/usr/local', dest='prefix', - help='the installation prefix (default: %(default)s)') -parser.add_argument('--destdir', default='', dest='destdir', - help='the destdir (default: %(default)s)') - -options = parser.parse_args() -if options.prefix[0] != '/': - print('Error, prefix must be an absolute path.') - sys.exit(1) - -if options.destdir == '': - install_root = options.prefix -else: - install_root = os.path.join(options.destdir, options.prefix[1:]) - -script_dir = os.path.join(install_root, 'share/meson') -module_dir = os.path.join(script_dir, 'modules') -bin_dir = os.path.join(install_root, 'bin') -bin_script = os.path.join(script_dir, 'meson.py') -gui_script = os.path.join(script_dir, 'mesongui.py') -conf_script = os.path.join(script_dir, 'mesonconf.py') -intro_script = os.path.join(script_dir, 'mesonintrospect.py') -wraptool_script = os.path.join(script_dir, 'wraptool.py') -bin_name = os.path.join(bin_dir, 'meson') -gui_name = os.path.join(bin_dir, 'mesongui') -conf_name = os.path.join(bin_dir, 'mesonconf') -intro_name = os.path.join(bin_dir, 'mesonintrospect') -wraptool_name = os.path.join(bin_dir, 'wraptool') -man_dir = os.path.join(install_root, 'share/man/man1') -in_manfile = 'man/meson.1' -out_manfile = os.path.join(man_dir, 'meson.1.gz') -in_guimanfile = 'man/mesongui.1' -out_guimanfile = os.path.join(man_dir, 'mesongui.1.gz') -in_confmanfile = 'man/mesonconf.1' -out_confmanfile = os.path.join(man_dir, 'mesonconf.1.gz') -in_intromanfile = 'man/mesonintrospect.1' -out_intromanfile = os.path.join(man_dir, 'mesonintrospect.1.gz') -in_wrapmanfile = 'man/wraptool.1' -out_wrapmanfile = os.path.join(man_dir, 'wraptool.1.gz') -rpmmacros_dir = os.path.join(install_root, 'lib/rpm/macros.d') - -symlink_value = os.path.relpath(bin_script, os.path.dirname(bin_name)) -guisymlink_value = os.path.relpath(gui_script, os.path.dirname(gui_name)) -confsymlink_value = os.path.relpath(conf_script, os.path.dirname(conf_name)) -introsymlink_value = os.path.relpath(intro_script, os.path.dirname(intro_name)) -wrapsymlink_value = os.path.relpath(wraptool_script, os.path.dirname(wraptool_name)) - -files = glob.glob('*.py') -files += glob.glob('*.ui') - -noinstall = ['compile_meson.py', 'install_meson.py', 'run_tests.py', 'run_cross_test.py'] - -files = [x for x in files if x not in noinstall] - -os.makedirs(script_dir, exist_ok=True) -os.makedirs(bin_dir, exist_ok=True) -os.makedirs(man_dir, exist_ok=True) - -for f in files: - print('Installing %s to %s.' %(f, script_dir)) - outfilename = os.path.join(script_dir, f) - shutil.copyfile(f, outfilename) - shutil.copystat(f, outfilename) - -print('Creating symlinks.') -for i in [bin_name, gui_name, conf_name, intro_name, wraptool_name]: - try: - os.unlink(i) - except FileNotFoundError: - pass -os.symlink(symlink_value, bin_name) -os.symlink(guisymlink_value, gui_name) -os.symlink(confsymlink_value, conf_name) -os.symlink(introsymlink_value, intro_name) -os.symlink(wrapsymlink_value, wraptool_name) -print('Installing manfiles to %s.' % man_dir) -open(out_manfile, 'wb').write(gzip.compress(open(in_manfile, 'rb').read())) -open(out_confmanfile, 'wb').write(gzip.compress(open(in_confmanfile, 'rb').read())) -open(out_guimanfile, 'wb').write(gzip.compress(open(in_guimanfile, 'rb').read())) -open(out_intromanfile, 'wb').write(gzip.compress(open(in_intromanfile, 'rb').read())) -open(out_wrapmanfile, 'wb').write(gzip.compress(open(in_wrapmanfile, 'rb').read())) - -print('Installing modules to %s.' % module_dir) -if os.path.exists('modules/__pycache__'): - shutil.rmtree('modules/__pycache__') -if os.path.exists(module_dir): - shutil.rmtree(module_dir) -shutil.copytree('modules', module_dir) - -if os.path.exists('/usr/bin/rpm'): - print('Installing RPM macros to %s.' % rpmmacros_dir) - outfilename = os.path.join(rpmmacros_dir, 'macros.meson') - os.makedirs(rpmmacros_dir, exist_ok=True) - shutil.copyfile('data/macros.meson', outfilename) - shutil.copystat('data/macros.meson', outfilename) diff --git a/readme.txt b/readme.txt index 2ecc346..a14ef7d 100644 --- a/readme.txt +++ b/readme.txt @@ -4,22 +4,15 @@ build system. Dependencies -Python http://python.org (version 3.3 or newer) +Python http://python.org (version 3.4 or newer) Ninja http://martine.github.com/ninja/ Installing from source You can run Meson directly from a revision control checkout or an -extracted tarball. Installing it system-wide is simple. - -Configure step: None -Compile step: None -Unit test step: ./run_tests.py -Install step: [sudo] ./install_meson.py --prefix /your/prefix --destdir /destdir/path - -The default value of prefix is /usr/local. The default value of destdir -is empty. +extracted tarball. Installation can be done with standard Python +distutils/setuptools commands. Running diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..36eab6b --- /dev/null +++ b/setup.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# Copyright 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 distutils.core import setup +from mesonbuild.coredata import version + +setup(name='meson', + version=version, + description='A high performance build system', + author='Jussi Pakkanen', + author_email='jpakkane@gmail.com', + url='http://mesonbuild.com', + packages=['mesonbuild'], + package_data={'mesonbuild': ['*.ui']}, + scripts=['meson', 'mesonconf', 'mesongui', 'mesonintrospect', 'wraptool'], + data_files=[('share/man/man1', ['man/meson.1', + 'man/mesonconf.1', + 'man/mesongui.1', + 'man/mesonintrospect.1', + 'man/wraptool.1'])], + classifiers=['Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: BSD', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3 :: Only', + 'Topic :: Software Development :: Build Tools', + ], + license=' Apache License, Version 2.0', + long_description='''Meson is a cross-platform build system designed to be both as +fast and as user friendly as possible. It supports many languages and compilers, includint +Gcc, clang and Visual Studio. Its build definitions are written in a simple non-turing +complete DSL.''') -- cgit v1.1 From 2e42427d4ac17e3fce819c7d3095c354c4c3c555 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 20:09:47 +0200 Subject: Support both setuptools and distutils. --- mesonbuild/coredata.py | 2 +- setup.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 5b1102c..f0ace4a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -14,7 +14,7 @@ import pickle, os, uuid -version = '0.29.0-research' +version = '0.29.0.dev1' build_types = ['plain', 'debug', 'debugoptimized', 'release'] layouts = ['mirror', 'flat'] diff --git a/setup.py b/setup.py index 36eab6b..5885683 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from distutils.core import setup +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + from mesonbuild.coredata import version setup(name='meson', -- cgit v1.1 From 5a77aff4ad322d753681aae6fb2f0ca41145ca24 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 20:13:56 +0200 Subject: Removed custom tar generator. --- create_release.sh | 5 ----- setup.py | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100755 create_release.sh diff --git a/create_release.sh b/create_release.sh deleted file mode 100755 index 0a04454..0000000 --- a/create_release.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -version=`./meson.py -v` -git archive --prefix meson-${version}/ HEAD | gzip > meson_${version}.tar.gz - diff --git a/setup.py b/setup.py index 5885683..48aca5a 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +# We need to support Python installations that have nothing but the basic +# Python installation. Use setuptools when possible and fall back to +# plain distutils when setuptools is not available. try: from setuptools import setup except ImportError: -- cgit v1.1 From 2f868a19c66ee22663f8735610739ecbfd7ef610 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 20:29:53 +0200 Subject: Some text tweaks. --- readme.txt | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index a14ef7d..4ed822f 100644 --- a/readme.txt +++ b/readme.txt @@ -11,8 +11,8 @@ Ninja http://martine.github.com/ninja/ Installing from source You can run Meson directly from a revision control checkout or an -extracted tarball. Installation can be done with standard Python -distutils/setuptools commands. +extracted tarball. Meson is also available from PyPi, so it can +be installed with 'pip install meson'. Running diff --git a/setup.py b/setup.py index 48aca5a..980671f 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,6 @@ setup(name='meson', ], license=' Apache License, Version 2.0', long_description='''Meson is a cross-platform build system designed to be both as -fast and as user friendly as possible. It supports many languages and compilers, includint +fast and as user friendly as possible. It supports many languages and compilers, including Gcc, clang and Visual Studio. Its build definitions are written in a simple non-turing complete DSL.''') -- cgit v1.1 From 84456537da9a77bbfe4fe9027c9fb36111c6b38d Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 20:59:34 +0200 Subject: Fix some issues that break pypi installs and bump version to upload new version. --- .gitignore | 2 ++ mesonbuild/coredata.py | 2 +- mesonbuild/mesonlib.py | 13 +++++++++++-- mesonbuild/modules/__init__.py | 0 mesonbuild/scripts/__init__.py | 0 mesonbuild/wrap/__init__.py | 0 setup.py | 7 +++++-- 7 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 mesonbuild/modules/__init__.py create mode 100644 mesonbuild/scripts/__init__.py create mode 100644 mesonbuild/wrap/__init__.py diff --git a/.gitignore b/.gitignore index ddd6e0e..1e42da4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ packagecache /MANIFEST /build /dist +/meson.egg-info + diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f0ace4a..7f2254b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -14,7 +14,7 @@ import pickle, os, uuid -version = '0.29.0.dev1' +version = '0.29.0.dev2' build_types = ['plain', 'debug', 'debugoptimized', 'release'] layouts = ['mirror', 'flat'] diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 2ab5ce4..a814567 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -116,6 +116,15 @@ def detect_vcs(source_dir): return vcs return None +def grab_leading_numbers(vstr): + result = [] + for x in vstr.split('.'): + try: + result.append(int(x)) + except ValueError: + break + return result + numpart = re.compile('[0-9.]+') def version_compare(vstr1, vstr2): @@ -146,8 +155,8 @@ def version_compare(vstr1, vstr2): vstr2 = vstr2[1:] else: cmpop = operator.eq - varr1 = [int(x) for x in vstr1.split('.')] - varr2 = [int(x) for x in vstr2.split('.')] + varr1 = grab_leading_numbers(vstr1) + varr2 = grab_leading_numbers(vstr2) return cmpop(varr1, varr2) def default_libdir(): diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mesonbuild/scripts/__init__.py b/mesonbuild/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mesonbuild/wrap/__init__.py b/mesonbuild/wrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py index 980671f..91f686b 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,11 @@ setup(name='meson', author='Jussi Pakkanen', author_email='jpakkane@gmail.com', url='http://mesonbuild.com', - packages=['mesonbuild'], + license=' Apache License, Version 2.0', + packages=['mesonbuild', + 'mesonbuild.modules', + 'mesonbuild.scripts', + 'mesonbuild.wrap'], package_data={'mesonbuild': ['*.ui']}, scripts=['meson', 'mesonconf', 'mesongui', 'mesonintrospect', 'wraptool'], data_files=[('share/man/man1', ['man/meson.1', @@ -50,7 +54,6 @@ setup(name='meson', 'Programming Language :: Python :: 3 :: Only', 'Topic :: Software Development :: Build Tools', ], - license=' Apache License, Version 2.0', long_description='''Meson is a cross-platform build system designed to be both as fast and as user friendly as possible. It supports many languages and compilers, including Gcc, clang and Visual Studio. Its build definitions are written in a simple non-turing -- cgit v1.1 From eda57f887a0cd62d29c0b9fa938a831ff7ba7307 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 23:04:41 +0200 Subject: Improve command line arg behaviour. --- mesonbuild/mesonmain.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 7b0834c..6317c95 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -209,7 +209,7 @@ def run(mainfile, args): print('You have python %s.' % sys.version) print('Please update your environment') return 1 - if args[0] == '--internal': + if len(args) >= 2 and args[0] == '--internal': if args[1] != 'regenerate': sys.exit(run_script_command(args[1:])) args = args[2:] @@ -217,16 +217,15 @@ def run(mainfile, args): 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 ' % sys.argv[0]) + print('{} '.format(sys.argv[0])) print('If you omit either directory, the current directory is substituted.') + print('Run {} --help for more information.'.format(sys.argv[0])) return 1 dir1 = args[0] if len(args) > 1: -- cgit v1.1 From 74927246552e4e6e32d50603719ccf03411910c9 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 16 Jan 2016 23:56:45 +0200 Subject: Fix wraptool. --- setup.py | 2 +- wraptool | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 91f686b..49607ee 100644 --- a/setup.py +++ b/setup.py @@ -56,5 +56,5 @@ setup(name='meson', ], long_description='''Meson is a cross-platform build system designed to be both as fast and as user friendly as possible. It supports many languages and compilers, including -Gcc, clang and Visual Studio. Its build definitions are written in a simple non-turing +GCC, Clang and Visual Studio. Its build definitions are written in a simple non-turing complete DSL.''') diff --git a/wraptool b/wraptool index 46a2c26..5e03efd 100755 --- a/wraptool +++ b/wraptool @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from meson.wrap import wraptool +from mesonbuild.wrap import wraptool import sys -sys.exit(wraptool.run(sys.argv[1:])) \ No newline at end of file +sys.exit(wraptool.run(sys.argv[1:])) -- cgit v1.1 From 9644a928d2ff1f18de3fa0ca6503cda3299b9511 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 17 Jan 2016 00:14:36 +0200 Subject: Some more command line guarding. --- mesonbuild/wrap/wraptool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index d2f0a28..c5f8eef 100755 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -162,7 +162,7 @@ def status(): 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': + if len(args) == 0 or args[0] == '-h' or args[0] == '--help': print_help() return 0 command = args[0] -- cgit v1.1 From 7e467207dc3ff90c0b64f41b90547a0defd7309e Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 17 Jan 2016 01:12:57 +0200 Subject: Put man page dir in manifest. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index d0b5ffb..8d8f136 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ graft manual?tests graft cross graft data graft graphics +graft man graft tools include run_tests.py include run_cross_test.py -- cgit v1.1 From f7e1d256ad78521052a41fe8f850793fa6cd4f01 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 17 Jan 2016 01:26:44 +0200 Subject: Fix rpm imports. --- mesonbuild/modules/rpm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index a2c0502..c8035ec 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -17,9 +17,10 @@ functionality such as generating template RPM spec file.''' from .. import build from .. import compilers -from .. import datetime +import datetime from .. import mlog -from .. import modules.gnome +from ..modules import gnome + import os class RPMModule: -- cgit v1.1