aboutsummaryrefslogtreecommitdiff
path: root/meson/build.py
diff options
context:
space:
mode:
Diffstat (limited to 'meson/build.py')
-rw-r--r--meson/build.py969
1 files changed, 969 insertions, 0 deletions
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