vala: Implement valac.find_library

Move CCompiler.compile to Compiler.compile so that ValaCompiler can use
it. Also rewrite ValaCompiler.sanity_check to use it since it does
a simple compile check.

At the same time, it enhances ExternalLibrary to support arguments for
languages other than C-like.

Includes a test for this that links against zlib through Vala.

Closes #983
pull/1022/head
Nirbheek Chauhan 8 years ago committed by Jussi Pakkanen
parent 4cdd22f094
commit 085650a1e3
  1. 2
      mesonbuild/backend/ninjabackend.py
  2. 133
      mesonbuild/compilers.py
  3. 28
      mesonbuild/dependencies.py
  4. 14
      mesonbuild/interpreter.py
  5. 9
      test cases/vala/13 find library/meson.build
  6. 6
      test cases/vala/13 find library/test.vala
  7. 1
      test cases/vala/4 config/meson-something-else.vapi
  8. 8
      test cases/vala/4 config/meson.build
  9. 1
      test cases/vala/4 config/prog.vala

@ -1045,6 +1045,8 @@ int dummy;
and d.version_requirement.startswith(('>=', '==')):
args += ['--target-glib', d.version_requirement[2:]]
args += ['--pkg', d.name]
elif isinstance(d, dependencies.ExternalLibrary):
args += d.get_lang_args('vala')
extra_args = []
for a in target.extra_args.get('vala', []):

@ -430,6 +430,51 @@ class Compiler():
extra_flags += environment.cross_info.config['properties'].get(lang_link_args_key, [])
return extra_flags
@contextlib.contextmanager
def compile(self, code, extra_args=None):
if extra_args is None:
extra_args = []
try:
with tempfile.TemporaryDirectory() as tmpdirname:
if isinstance(code, str):
srcname = os.path.join(tmpdirname,
'testfile.' + self.default_suffix)
with open(srcname, 'w') as ofile:
ofile.write(code)
elif isinstance(code, mesonlib.File):
srcname = code.fname
# Extension only matters if running results; '.exe' is
# guaranteed to be executable on every platform.
output = os.path.join(tmpdirname, 'output.exe')
commands = self.get_exelist()
commands.append(srcname)
commands += extra_args
commands += self.get_output_args(output)
mlog.debug('Running compile:')
mlog.debug('Working directory: ', tmpdirname)
mlog.debug('Command line: ', ' '.join(commands), '\n')
mlog.debug('Code:\n', code)
p = subprocess.Popen(commands, cwd=tmpdirname,
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)
p.input_name = srcname
p.output_name = output
yield p
except (PermissionError, OSError):
# On Windows antivirus programs and the like hold on to files so
# they can't be deleted. There's not much to do in this case. Also,
# catch OSError because the directory is then no longer empty.
pass
def get_colorout_args(self, colortype):
return []
@ -659,51 +704,6 @@ int main () {{
args = extra_args + self.get_compiler_check_args()
return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies)
@contextlib.contextmanager
def compile(self, code, extra_args=None):
if extra_args is None:
extra_args = []
try:
with tempfile.TemporaryDirectory() as tmpdirname:
if isinstance(code, str):
srcname = os.path.join(tmpdirname,
'testfile.' + self.default_suffix)
with open(srcname, 'w') as ofile:
ofile.write(code)
elif isinstance(code, mesonlib.File):
srcname = code.fname
# Extension only matters if running results; '.exe' is
# guaranteed to be executable on every platform.
output = os.path.join(tmpdirname, 'output.exe')
commands = self.get_exelist()
commands.append(srcname)
commands += extra_args
commands += self.get_output_args(output)
mlog.debug('Running compile:')
mlog.debug('Working directory: ', tmpdirname)
mlog.debug('Command line: ', ' '.join(commands), '\n')
mlog.debug('Code:\n', code)
p = subprocess.Popen(commands, cwd=tmpdirname,
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)
p.input_name = srcname
p.output_name = output
yield p
except (PermissionError, OSError):
# On Windows antivirus programs and the like hold on to files so
# they can't be deleted. There's not much to do in this case. Also,
# catch OSError because the directory is then no longer empty.
pass
def compiles(self, code, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
@ -1330,27 +1330,48 @@ class ValaCompiler(Compiler):
def needs_static_linker(self):
return False # Because compiles into C.
def get_output_args(self, target):
return ['-o', target]
def get_werror_args(self):
return ['--fatal-warnings']
def sanity_check(self, work_dir, environment):
src = 'valatest.vala'
source_name = os.path.join(work_dir, src)
with open(source_name, 'w') as ofile:
ofile.write('''class SanityCheck : Object {
}
''')
extra_flags = self.get_cross_extra_flags(environment, compile=True, link=False)
pc = subprocess.Popen(self.exelist + extra_flags + ['-C', '-c', src], cwd=work_dir)
pc.wait()
if pc.returncode != 0:
raise EnvironmentException('Vala compiler %s can not compile programs.' % self.name_string())
code = 'class MesonSanityCheck : Object { }'
args = self.get_cross_extra_flags(environment, compile=True, link=False)
args += ['-C']
with self.compile(code, args) as p:
if p.returncode != 0:
msg = 'Vala compiler {!r} can not compile programs' \
''.format(self.name_string())
raise EnvironmentException(msg)
def get_buildtype_args(self, buildtype):
if buildtype == 'debug' or buildtype == 'debugoptimized' or buildtype == 'minsize':
return ['--debug']
return []
def find_library(self, libname, env, extra_dirs):
if extra_dirs and isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
# Valac always looks in the default vapi dir, so only search there if
# no extra dirs are specified.
if len(extra_dirs) == 0:
code = 'class MesonFindLibrary : Object { }'
vapi_args = ['--pkg', libname]
args = self.get_cross_extra_flags(env, compile=True, link=False)
args += ['-C'] + vapi_args
with self.compile(code, args) as p:
if p.returncode == 0:
return vapi_args
# Not found? Try to find the vapi file itself.
for d in extra_dirs:
vapi = os.path.join(d, libname + '.vapi')
if os.path.isfile(vapi):
return vapi
mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname))
return None
class RustCompiler(Compiler):
def __init__(self, exelist, version):
self.language = 'rust'

@ -455,29 +455,37 @@ class ExternalProgram():
return self.name
class ExternalLibrary(Dependency):
def __init__(self, name, link_args=None, silent=False):
# TODO: Add `lang` to the parent Dependency object so that dependencies can
# be expressed for languages other than C-like
def __init__(self, name, link_args=None, language=None, silent=False):
super().__init__('external')
self.name = name
# Rename fullpath to link_args once standalone find_library() gets removed.
if link_args is not None:
if isinstance(link_args, list):
self.link_args = link_args
else:
self.link_args = [link_args]
self.is_found = False
self.link_args = []
self.lang_args = []
if link_args:
self.is_found = True
if not isinstance(link_args, list):
link_args = [link_args]
if language:
self.lang_args = {language: link_args}
else:
self.link_args = link_args
if not silent:
if self.found():
if self.is_found:
mlog.log('Library', mlog.bold(name), 'found:', mlog.green('YES'))
else:
mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO'))
def found(self):
return self.link_args is not None
return self.is_found
def get_link_args(self):
if self.found():
return self.link_args
def get_lang_args(self, lang):
if lang in self.lang_args:
return self.lang_args[lang]
return []
class BoostDependency(Dependency):

@ -944,9 +944,17 @@ class CompilerHolder(InterpreterObject):
if not os.path.isabs(i):
raise InvalidCode('Search directory %s is not an absolute path.' % i)
linkargs = self.compiler.find_library(libname, self.environment, search_dirs)
if required and linkargs is None:
raise InterpreterException('Library {} not found'.format(libname))
lib = dependencies.ExternalLibrary(libname, linkargs)
if required and not linkargs:
l = self.compiler.language.capitalize()
raise InterpreterException('{} library {!r} not found'.format(l, libname))
# If this is set to None, the library and link arguments are for
# a C-like compiler. Otherwise, it's for some other language that has
# a find_library implementation. We do this because it's easier than
# maintaining a list of languages that can consume C libraries.
lang = None
if self.compiler.language == 'vala':
lang = 'vala'
lib = dependencies.ExternalLibrary(libname, linkargs, language=lang)
return ExternalLibraryHolder(lib)
def has_argument_method(self, args, kwargs):

@ -0,0 +1,9 @@
project('find vala library', 'vala', 'c')
valac = meson.get_compiler('vala')
gobject = dependency('gobject-2.0')
zlib = valac.find_library('zlib')
e = executable('zlibtest', 'test.vala', dependencies : [gobject, zlib])
test('testzlib', e)

@ -0,0 +1,6 @@
using ZLib;
public static int main(string[] args) {
stdout.printf("ZLIB_VERSION is: %s\n", ZLib.VERSION.STRING);
return 0;
}

@ -0,0 +1 @@
public const string SOMETHING_ELSE;

@ -1,11 +1,15 @@
project('valatest', 'vala', 'c')
valadeps = [dependency('glib-2.0'), dependency('gobject-2.0')]
valac = meson.get_compiler('vala')
# Try to find our library
valadeps = [valac.find_library('meson-something-else', dirs : meson.current_source_dir())]
valadeps += [dependency('glib-2.0'), dependency('gobject-2.0')]
e = executable(
'valaprog',
sources : ['config.vapi', 'prog.vala'],
dependencies : valadeps,
c_args : '-DDATA_DIRECTORY="@0@"'.format(meson.current_source_dir())
c_args : ['-DDATA_DIRECTORY="@0@"'.format(meson.current_source_dir()),
'-DSOMETHING_ELSE="Out of this world!"']
)
test('valatest', e)

@ -2,6 +2,7 @@ class MainProg : GLib.Object {
public static int main(string[] args) {
stdout.printf("DATA_DIRECTORY is: %s.\n", DATA_DIRECTORY);
stdout.printf("SOMETHING_ELSE is: %s.\n", SOMETHING_ELSE);
return 0;
}
}

Loading…
Cancel
Save