Basic work on extracting build target types from the interpreter.

pull/15/head
Jussi Pakkanen 12 years ago
parent b897c1ee48
commit a2959fd0f4
  1. 37
      backends.py
  2. 239
      build.py
  3. 252
      interpreter.py

@ -16,7 +16,8 @@ import os, sys, re, pickle
import interpreter, nodes
import environment, mlog
from meson_install import InstallData
from interpreter import InvalidArguments
from build import InvalidArguments
import build
import shutil
from coredata import MesonException
@ -53,7 +54,7 @@ def do_replacement(regex, line, confdata):
def do_mesondefine(line, confdata):
arr = line.split()
if len(arr) != 2:
raise interpreter.InvalidArguments('#mesondefine does not contain exactly two tokens: %s', line.strip())
raise build.InvalidArguments('#mesondefine does not contain exactly two tokens: %s', line.strip())
varname = arr[1]
try:
v = confdata.get(varname)
@ -71,7 +72,7 @@ def do_mesondefine(line, confdata):
elif isinstance(v, str):
return '#define %s %s\n' % (varname, v)
else:
raise interpreter.InvalidArguments('#mesondefine argument "%s" is of unknown type.' % varname)
raise build.InvalidArguments('#mesondefine argument "%s" is of unknown type.' % varname)
def do_conf_file(src, dst, confdata):
data = open(src).readlines()
@ -205,11 +206,11 @@ class Backend():
commands += compiler.get_std_opt_flags()
if self.environment.coredata.coverage:
commands += compiler.get_coverage_flags()
if isinstance(target, interpreter.SharedLibrary):
if isinstance(target, build.SharedLibrary):
commands += compiler.get_pic_flags()
for dep in target.get_external_deps():
commands += dep.get_compile_flags()
if isinstance(target, interpreter.Executable):
if isinstance(target, build.Executable):
commands += dep.get_exe_flags()
return commands
@ -217,8 +218,8 @@ class Backend():
def build_target_link_arguments(self, compiler, deps):
args = []
for d in deps:
if not isinstance(d, interpreter.StaticLibrary) and\
not isinstance(d, interpreter.SharedLibrary):
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':
@ -425,7 +426,7 @@ class NinjaBackend(Backend):
should_strip = self.environment.coredata.strip
for t in self.build.get_targets().values():
if t.should_install():
if isinstance(t, interpreter.Executable):
if isinstance(t, build.Executable):
outdir = bindir
else:
outdir = libdir
@ -682,7 +683,7 @@ class NinjaBackend(Backend):
generator = genlist.get_generator()
exe = generator.get_exe()
if self.environment.is_cross_build() and \
isinstance(exe, interpreter.BuildTarget) and exe.is_cross:
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.'
@ -690,7 +691,7 @@ class NinjaBackend(Backend):
raise MesonException(s)
infilelist = genlist.get_infilelist()
outfilelist = genlist.get_outfilelist()
if isinstance(exe, interpreter.BuildTarget):
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()
@ -714,7 +715,7 @@ class NinjaBackend(Backend):
cmdlist = [exe_file] + args
elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename)
elem.add_item('DESC', 'Generating $out')
if isinstance(exe, interpreter.BuildTarget):
if isinstance(exe, build.BuildTarget):
elem.add_dep(self.get_target_filename(exe))
elem.add_item('COMMAND', cmdlist)
elem.write(outfile)
@ -805,7 +806,7 @@ class NinjaBackend(Backend):
if len(pch) == 0:
continue
if '/' not in pch[0] or '/' not in pch[-1]:
raise interpreter.InvalidArguments('Precompiled header of "%s" must not be in the same direcotory as source, please put it in a subdirectory.' % target.get_basename())
raise build.InvalidArguments('Precompiled header of "%s" must not be in the same direcotory 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])
@ -833,25 +834,25 @@ class NinjaBackend(Backend):
elem.write(outfile)
def generate_link(self, target, outfile, outname, obj_list):
if isinstance(target, interpreter.StaticLibrary):
if isinstance(target, build.StaticLibrary):
linker = self.build.static_linker
linker_base = 'STATIC'
else:
linker = self.build.compilers[0]
linker_base = linker.get_language() # Fixme.
if isinstance(target, interpreter.SharedLibrary):
if isinstance(target, build.SharedLibrary):
self.generate_shsym(outfile, target)
crstr = ''
if target.is_cross:
crstr = '_CROSS'
linker_rule = linker_base + crstr + '_LINKER'
commands = []
if isinstance(target, interpreter.Executable):
if isinstance(target, build.Executable):
commands += linker.get_std_exe_link_flags()
elif isinstance(target, interpreter.SharedLibrary):
elif isinstance(target, build.SharedLibrary):
commands += linker.get_std_shared_lib_link_flags()
commands += linker.get_pic_flags()
elif isinstance(target, interpreter.StaticLibrary):
elif isinstance(target, build.StaticLibrary):
commands += linker.get_std_link_flags()
else:
raise RuntimeError('Unknown build target type.')
@ -868,7 +869,7 @@ class NinjaBackend(Backend):
return elem
def get_dependency_filename(self, t):
if isinstance(t, interpreter.SharedLibrary):
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)

@ -12,6 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import coredata
import copy
class InvalidArguments(coredata.MesonException):
pass
class Build:
"""A class that holds the status of one build including
all dependencies and so on.
@ -66,3 +72,236 @@ class Build:
def get_global_flags(self, compiler):
return self.global_args.get(compiler.get_language(), [])
class BuildTarget():
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
self.name = name
self.subdir = subdir
self.is_cross = is_cross
self.sources = []
self.external_deps = []
self.include_dirs = []
self.link_targets = []
self.filename = 'no_name'
self.need_install = False
self.pch = {}
self.extra_args = {}
self.generated = []
self.process_sourcelist(sources)
self.process_kwargs(kwargs)
if len(self.sources) == 0 and len(self.generated) == 0:
raise InvalidArguments('Build target %s has no sources.' % name)
def process_sourcelist(self, sources):
if not isinstance(sources, list):
sources = [sources]
for s in sources:
if isinstance(s, str):
self.sources.append(s)
elif isinstance(s, GeneratedList):
self.generated.append(s)
else:
raise InvalidArguments('Bad source in target %s.' % self.name)
def get_original_kwargs(self):
return self.kwargs
def process_kwargs(self, kwargs):
self.kwargs = copy.copy(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:
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)
if 'version' in kwargs:
self.set_version(kwargs['version'])
if 'soversion' in kwargs:
self.set_soversion(kwargs['soversion'])
inclist = kwargs.get('include_dirs', [])
if not isinstance(inclist, list):
inclist = [inclist]
self.add_include_dirs(inclist)
deplist = kwargs.get('deps', [])
if not isinstance(deplist, list):
deplist = [deplist]
self.add_external_deps(deplist)
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):
return self.link_targets
def get_basename(self):
return self.name
def get_source_subdir(self):
return self.subdir
def get_sources(self):
return self.sources
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):
for dep in deps:
if not isinstance(dep, dependencies.Dependency) and\
not isinstance(dep, ExternalLibraryHolder):
raise InvalidArguments('Argument is not an external dependency')
self.external_deps.append(dep)
if isinstance(dep, dependencies.Dependency):
self.process_sourcelist(dep.get_sources())
def get_external_deps(self):
return self.external_deps
def add_dep(self, args):
[self.add_external_dep(dep) for dep in args]
def link(self, target):
if not isinstance(target, StaticLibrary) and \
not isinstance(target, SharedLibrary):
print(target)
raise InvalidArguments('Link target is not library.')
self.link_targets.append(target)
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
if len(pchlist) == 2:
if environment.is_header(pchlist[0]):
if not environment.is_source(pchlist[1]):
raise InterpreterException('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 InterpreterException('PCH definition must contain one header and at most one source.')
pchlist = [pchlist[1], pchlist[0]]
else:
raise InterpreterException('PCH argument %s is of unknown type.' % pchlist[0])
elif len(pchlist) > 2:
raise InterpreterException('PCH definition may have a maximum of 2 files.')
self.pch[language] = pchlist
def add_include_dirs(self, args):
for a in args:
if not isinstance(a, IncludeDirs):
raise InvalidArguments('Include directory to be added is not an include directory object.')
self.include_dirs += args
def add_compiler_args(self, language, flags):
for a in flags:
if not isinstance(a, str):
raise InvalidArguments('A non-string passed to compiler args.')
if language in self.extra_args:
self.extra_args[language] += flags
else:
self.extra_args[language] = flags
def get_aliaslist(self):
return []
class Executable(BuildTarget):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
super().__init__(name, subdir, is_cross, sources, environment, kwargs)
suffix = environment.get_exe_suffix()
if suffix != '':
self.filename = self.name + '.' + suffix
else:
self.filename = self.name
class StaticLibrary(BuildTarget):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
super().__init__(name, subdir, is_cross, sources, environment, kwargs)
prefix = environment.get_static_lib_prefix()
suffix = environment.get_static_lib_suffix()
self.filename = prefix + self.name + '.' + suffix
class SharedLibrary(BuildTarget):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
self.version = None
self.soversion = None
super().__init__(name, subdir, is_cross, sources, environment, kwargs);
self.prefix = environment.get_shared_lib_prefix()
self.suffix = environment.get_shared_lib_suffix()
def get_shbase(self):
return self.prefix + self.name + '.' + self.suffix
def get_filename(self):
fname = self.get_shbase()
if self.version is None:
return fname
else:
return fname + '.' + self.version
def set_version(self, version):
if isinstance(version, nodes.StringStatement):
version = version.get_value()
if not isinstance(version, str):
print(version)
raise InvalidArguments('Shared library version is not a string.')
self.version = version
def set_soversion(self, version):
if isinstance(version, nodes.StringStatement) or isinstance(version, nodes.IntStatement):
version = version.get_value()
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

@ -18,7 +18,8 @@ import environment
import coredata
import dependencies
import mlog
import os, sys, platform, copy, subprocess, shutil
import build
import os, sys, platform, subprocess, shutil
class InterpreterException(coredata.MesonException):
pass
@ -26,9 +27,6 @@ class InterpreterException(coredata.MesonException):
class InvalidCode(InterpreterException):
pass
class InvalidArguments(InterpreterException):
pass
class InterpreterObject():
def __init__(self):
self.methods = {}
@ -409,237 +407,21 @@ class Man(InterpreterObject):
def get_sources(self):
return self.sources
class BuildTarget(InterpreterObject):
def __init__(self, name, subdir, is_cross, sources, kwargs):
InterpreterObject.__init__(self)
self.name = name
self.subdir = subdir
self.is_cross = is_cross
self.sources = []
self.external_deps = []
self.include_dirs = []
self.link_targets = []
self.filename = 'no_name'
self.need_install = False
self.pch = {}
self.extra_args = {}
self.generated = []
self.process_sourcelist(sources)
self.process_kwargs(kwargs)
if len(self.sources) == 0 and len(self.generated) == 0:
raise InvalidArguments('Build target %s has no sources.' % name)
def process_sourcelist(self, sources):
if not isinstance(sources, list):
sources = [sources]
for s in sources:
if isinstance(s, str):
self.sources.append(s)
elif isinstance(s, GeneratedList):
self.generated.append(s)
else:
raise InvalidArguments('Bad source in target %s.' % self.name)
def get_original_kwargs(self):
return self.kwargs
def process_kwargs(self, kwargs):
self.kwargs = copy.copy(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:
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)
if 'version' in kwargs:
self.set_version(kwargs['version'])
if 'soversion' in kwargs:
self.set_soversion(kwargs['soversion'])
inclist = kwargs.get('include_dirs', [])
if not isinstance(inclist, list):
inclist = [inclist]
self.add_include_dirs(inclist)
deplist = kwargs.get('deps', [])
if not isinstance(deplist, list):
deplist = [deplist]
self.add_external_deps(deplist)
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):
return self.link_targets
def get_basename(self):
return self.name
def get_source_subdir(self):
return self.subdir
class BuildTargetHolder(InterpreterObject):
def __init__(self, targetttype, name, subdir, is_cross, sources, environment, kwargs):
self.target = targetttype(name, subdir, is_cross, sources, environment, kwargs)
def get_sources(self):
return self.sources
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):
for dep in deps:
if not isinstance(dep, dependencies.Dependency) and\
not isinstance(dep, ExternalLibraryHolder):
raise InvalidArguments('Argument is not an external dependency')
self.external_deps.append(dep)
if isinstance(dep, dependencies.Dependency):
self.process_sourcelist(dep.get_sources())
def get_external_deps(self):
return self.external_deps
def add_dep(self, args):
[self.add_external_dep(dep) for dep in args]
def link(self, target):
if not isinstance(target, StaticLibrary) and \
not isinstance(target, SharedLibrary):
raise InvalidArguments('Link target is not library.')
self.link_targets.append(target)
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
if len(pchlist) == 2:
if environment.is_header(pchlist[0]):
if not environment.is_source(pchlist[1]):
raise InterpreterException('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 InterpreterException('PCH definition must contain one header and at most one source.')
pchlist = [pchlist[1], pchlist[0]]
else:
raise InterpreterException('PCH argument %s is of unknown type.' % pchlist[0])
elif len(pchlist) > 2:
raise InterpreterException('PCH definition may have a maximum of 2 files.')
self.pch[language] = pchlist
def add_include_dirs(self, args):
for a in args:
if not isinstance(a, IncludeDirs):
raise InvalidArguments('Include directory to be added is not an include directory object.')
self.include_dirs += args
def add_compiler_args(self, language, flags):
for a in flags:
if not isinstance(a, str):
raise InvalidArguments('A non-string passed to compiler args.')
if language in self.extra_args:
self.extra_args[language] += flags
else:
self.extra_args[language] = flags
def get_aliaslist(self):
return []
class Executable(BuildTarget):
class ExecutableHolder(BuildTargetHolder):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
BuildTarget.__init__(self, name, subdir, is_cross, sources, kwargs)
suffix = environment.get_exe_suffix()
if suffix != '':
self.filename = self.name + '.' + suffix
else:
self.filename = self.name
super().__init__(build.Executable, name, subdir, is_cross, sources, environment, kwargs)
class StaticLibrary(BuildTarget):
class StaticLibraryHolder(BuildTargetHolder):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
BuildTarget.__init__(self, name, subdir, is_cross, sources, kwargs)
prefix = environment.get_static_lib_prefix()
suffix = environment.get_static_lib_suffix()
self.filename = prefix + self.name + '.' + suffix
super().__init__(build.StaticLibrary, name, subdir, is_cross, sources, environment, kwargs)
class SharedLibrary(BuildTarget):
class SharedLibraryHolder(BuildTargetHolder):
def __init__(self, name, subdir, is_cross, sources, environment, kwargs):
self.version = None
self.soversion = None
BuildTarget.__init__(self, name, subdir, is_cross, sources, kwargs)
self.prefix = environment.get_shared_lib_prefix()
self.suffix = environment.get_shared_lib_suffix()
def get_shbase(self):
return self.prefix + self.name + '.' + self.suffix
def get_filename(self):
fname = self.get_shbase()
if self.version is None:
return fname
else:
return fname + '.' + self.version
def set_version(self, version):
if isinstance(version, nodes.StringStatement):
version = version.get_value()
if not isinstance(version, str):
print(version)
raise InvalidArguments('Shared library version is not a string.')
self.version = version
def set_soversion(self, version):
if isinstance(version, nodes.StringStatement) or isinstance(version, nodes.IntStatement):
version = version.get_value()
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
super().__init__(build.SharedLibrary, name, subdir, is_cross, sources, environment, kwargs)
class Test(InterpreterObject):
def __init__(self, name, exe, is_parallel):
@ -1107,13 +889,13 @@ class Interpreter():
return dep
def func_executable(self, node, args, kwargs):
return self.build_target(node, args, kwargs, Executable)
return self.build_target(node, args, kwargs, ExecutableHolder)
def func_static_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, StaticLibrary)
return self.build_target(node, args, kwargs, StaticLibraryHolder)
def func_shared_lib(self, node, args, kwargs):
return self.build_target(node, args, kwargs, SharedLibrary)
return self.build_target(node, args, kwargs, SharedLibraryHolder)
def func_generator(self, node, args, kwargs):
gen = Generator(args, kwargs)
@ -1121,11 +903,11 @@ class Interpreter():
return gen
def func_test(self, node, args, kwargs):
self.validate_arguments(args, 2, [str, Executable])
self.validate_arguments(args, 2, [str, ExecutableHolder])
par = kwargs.get('is_parallel', True)
if not isinstance(par, bool):
raise InterpreterException('Keyword argument is_parallel must be a boolean.')
t = Test(args[0], args[1], par)
t = Test(args[0], args[1].target, par)
self.build.tests.append(t)
mlog.debug('Adding test "', mlog.bold(args[0]), '".', sep='')
@ -1265,7 +1047,7 @@ class Interpreter():
raise InvalidCode('Tried to create target "%s", but a target of that name already exists.' % name)
self.check_sources_exist(os.path.join(self.environment.source_dir, self.subdir), sources)
l = targetclass(name, self.subdir, is_cross, sources, self.environment, kwargs)
self.build.targets[name] = l
self.build.targets[name] = l.target
if self.environment.is_cross_build() and l.is_cross:
txt = ' cross build '
else:

Loading…
Cancel
Save