Use cross-info c_args, c_link_args, etc for all compiler checks

This allows the user to specify custom arguments to the compiler to be used
while performing cross-compiler checks. For example, passing a GCC specs file as
c_link_args so that a "prefix" filled with libraries that are to be compiled
against can be found with cc.find_library, or an `-mcpu` c_arg that is required
for compilation.

Also ensure that unix_link_flags_to_native() and unix_compile_flags_to_native()
always return a copy of the original arguments and not a reference to the
original arguments. We never want to modify the original arguments.
pull/586/head
Nirbheek Chauhan 9 years ago
parent 777c1e9c19
commit abf81aab77
  1. 116
      mesonbuild/compilers.py
  2. 20
      mesonbuild/interpreter.py

@ -282,12 +282,14 @@ class Compiler():
raise EnvironmentException('Language %s does not support function checks.' % self.language) raise EnvironmentException('Language %s does not support function checks.' % self.language)
def unix_link_flags_to_native(self, args): def unix_link_flags_to_native(self, args):
return args "Always returns a copy that can be independently mutated"
return args[:]
def unix_compile_flags_to_native(self, args): def unix_compile_flags_to_native(self, args):
return args "Always returns a copy that can be independently mutated"
return args[:]
def find_library(self, libname, extra_dirs): def find_library(self, *args, **kwargs):
raise EnvironmentException('Language {} does not support library finding.'.format(self.language)) raise EnvironmentException('Language {} does not support library finding.'.format(self.language))
def get_library_dirs(self): def get_library_dirs(self):
@ -298,7 +300,7 @@ class Compiler():
def get_cross_extra_flags(self, environment, *, compile, link): def get_cross_extra_flags(self, environment, *, compile, link):
extra_flags = [] extra_flags = []
if self.is_cross: if self.is_cross and environment:
if 'properties' in environment.cross_info.config: if 'properties' in environment.cross_info.config:
lang_args_key = self.language + '_args' lang_args_key = self.language + '_args'
if compile: if compile:
@ -493,19 +495,19 @@ class CCompiler(Compiler):
code = 'int main(int argc, char **argv) { int class=0; return class; }\n' code = 'int main(int argc, char **argv) { int class=0; return class; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def has_header(self, hname, extra_args=[]): def has_header(self, hname, env, extra_args=[]):
templ = '''#include<%s> templ = '''#include<%s>
int someSymbolHereJustForFun; int someSymbolHereJustForFun;
''' '''
return self.compiles(templ % hname, extra_args) return self.compiles(templ % hname, env, extra_args)
def has_header_symbol(self, hname, symbol, prefix, extra_args=[]): def has_header_symbol(self, hname, symbol, prefix, env, extra_args=[]):
templ = '''{2} templ = '''{2}
#include <{0}> #include <{0}>
int main () {{ {1}; }}''' int main () {{ {1}; }}'''
# Pass -O0 to ensure that the symbol isn't optimized away # Pass -O0 to ensure that the symbol isn't optimized away
extra_args += self.get_no_optimization_args() extra_args += self.get_no_optimization_args()
return self.compiles(templ.format(hname, symbol, prefix), extra_args) return self.compiles(templ.format(hname, symbol, prefix), env, extra_args)
def compile(self, code, srcname, extra_args=[]): def compile(self, code, srcname, extra_args=[]):
commands = self.get_exelist() commands = self.get_exelist()
@ -523,7 +525,7 @@ int main () {{ {1}; }}'''
os.remove(srcname) os.remove(srcname)
return p return p
def compiles(self, code, extra_args = []): def compiles(self, code, env, extra_args=[]):
if isinstance(extra_args, str): if isinstance(extra_args, str):
extra_args = [extra_args] extra_args = [extra_args]
suflen = len(self.default_suffix) suflen = len(self.default_suffix)
@ -532,8 +534,13 @@ int main () {{ {1}; }}'''
ofile = open(srcname, 'w') ofile = open(srcname, 'w')
ofile.write(code) ofile.write(code)
ofile.close() ofile.close()
extra_args = extra_args + self.get_compile_only_args() # Convert flags to the native type of the selected compiler
p = self.compile(code, srcname, extra_args) args = self.unix_link_flags_to_native(extra_args)
# Read c_args/cpp_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=False)
# We only want to compile; not link
args += self.get_compile_only_args()
p = self.compile(code, srcname, args)
try: try:
trial = srcname[:-suflen] + 'o' trial = srcname[:-suflen] + 'o'
os.remove(trial) os.remove(trial)
@ -545,7 +552,7 @@ int main () {{ {1}; }}'''
pass pass
return p.returncode == 0 return p.returncode == 0
def links(self, code, extra_args = []): def links(self, code, env, extra_args=[]):
(fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
os.close(fd) os.close(fd)
(fd, dstname) = tempfile.mkstemp() (fd, dstname) = tempfile.mkstemp()
@ -553,21 +560,25 @@ int main () {{ {1}; }}'''
ofile = open(srcname, 'w') ofile = open(srcname, 'w')
ofile.write(code) ofile.write(code)
ofile.close() ofile.close()
# Convert flags to the native type of the selected compiler
args = self.unix_link_flags_to_native(extra_args)
# Need to add buildtype args to select the CRT to use with MSVC # Need to add buildtype args to select the CRT to use with MSVC
# This is needed especially while trying to link with static libraries # This is needed especially while trying to link with static libraries
# since MSVC won't auto-select a CRT for us in that case and will error # since MSVC won't auto-select a CRT for us in that case and will error
# out asking us to select one. # out asking us to select one.
extra_args = self.get_buildtype_args('debug') + \ args += self.get_buildtype_args('debug')
self.unix_link_flags_to_native(extra_args) + \ # Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed)
self.get_output_args(dstname) args += self.get_cross_extra_flags(env, compile=True, link=True)
p = self.compile(code, srcname, extra_args) # Arguments specifying the output filename
args += self.get_output_args(dstname)
p = self.compile(code, srcname, args)
try: try:
os.remove(dstname) os.remove(dstname)
except FileNotFoundError: except FileNotFoundError:
pass pass
return p.returncode == 0 return p.returncode == 0
def run(self, code, extra_args=[]): def run(self, code, env, extra_args=[]):
mlog.debug('Running code:\n\n', code) mlog.debug('Running code:\n\n', code)
if self.is_cross and self.exe_wrapper is None: if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.') raise CrossNoRunException('Can not run test applications in this cross environment.')
@ -576,11 +587,15 @@ int main () {{ {1}; }}'''
ofile = open(srcname, 'w') ofile = open(srcname, 'w')
ofile.write(code) ofile.write(code)
ofile.close() ofile.close()
exename = srcname + '.exe' # Is guaranteed to be executable on every platform. # Convert flags to the native type of the selected compiler
commands = self.get_exelist() args = self.unix_link_flags_to_native(extra_args)
# Same reasoning as self.links() above # Same reasoning as self.links() above
commands += self.get_buildtype_args('debug') args += self.get_buildtype_args('debug')
commands += self.unix_link_flags_to_native(extra_args) # Read c_link_args/cpp_link_args/etc from the cross-info file
args += self.get_cross_extra_flags(env, compile=True, link=True)
# Create command list
exename = srcname + '.exe' # Is guaranteed to be executable on every platform.
commands = self.get_exelist() + args
commands.append(srcname) commands.append(srcname)
commands += self.get_output_args(exename) commands += self.get_output_args(exename)
p = subprocess.Popen(commands, cwd=os.path.split(srcname)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(commands, cwd=os.path.split(srcname)[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -627,16 +642,12 @@ int main(int argc, char **argv) {{
%s %s
int temparray[%d-sizeof(%s)]; int temparray[%d-sizeof(%s)];
''' '''
try:
extra_args += env.cross_info.config['properties'][self.language + '_args']
except KeyError:
pass
extra_args += self.get_no_optimization_args() extra_args += self.get_no_optimization_args()
if not self.compiles(element_exists_templ.format(prefix, element)): if not self.compiles(element_exists_templ.format(prefix, element), env, extra_args):
return -1 return -1
for i in range(1, 1024): for i in range(1, 1024):
code = templ % (prefix, i, element) code = templ % (prefix, i, element)
if self.compiles(code, extra_args): if self.compiles(code, env, extra_args):
if self.id == 'msvc': if self.id == 'msvc':
# MSVC refuses to construct an array of zero size, so # MSVC refuses to construct an array of zero size, so
# the test only succeeds when i is sizeof(element) + 1 # the test only succeeds when i is sizeof(element) + 1
@ -655,7 +666,7 @@ int main(int argc, char **argv) {
return 0; return 0;
}; };
''' '''
res = self.run(templ % (prefix, element), extra_args) res = self.run(templ % (prefix, element), env, extra_args)
if not res.compiled: if not res.compiled:
return -1 return -1
if res.returncode != 0: if res.returncode != 0:
@ -676,16 +687,12 @@ struct tmp {
int testarray[%d-offsetof(struct tmp, target)]; int testarray[%d-offsetof(struct tmp, target)];
''' '''
try:
extra_args += env.cross_info.config['properties'][self.language + '_args']
except KeyError:
pass
extra_args += self.get_no_optimization_args() extra_args += self.get_no_optimization_args()
if not self.compiles(type_exists_templ.format(typename)): if not self.compiles(type_exists_templ.format(typename), env, extra_args):
return -1 return -1
for i in range(1, 1024): for i in range(1, 1024):
code = templ % (typename, i) code = templ % (typename, i)
if self.compiles(code, extra_args): if self.compiles(code, env, extra_args):
if self.id == 'msvc': if self.id == 'msvc':
# MSVC refuses to construct an array of zero size, so # MSVC refuses to construct an array of zero size, so
# the test only succeeds when i is sizeof(element) + 1 # the test only succeeds when i is sizeof(element) + 1
@ -709,7 +716,7 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
''' '''
res = self.run(templ % typename, extra_args) res = self.run(templ % typename, env, extra_args)
if not res.compiled: if not res.compiled:
raise EnvironmentException('Could not compile alignment test.') raise EnvironmentException('Could not compile alignment test.')
if res.returncode != 0: if res.returncode != 0:
@ -774,7 +781,7 @@ int main(int argc, char **argv) {
if isinstance(val, bool): if isinstance(val, bool):
return val return val
raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
if self.links(templ.format(prefix, funcname), extra_args): if self.links(templ.format(prefix, funcname), env, extra_args):
return True return True
# Add -O0 to ensure that the symbol isn't optimized away by the compiler # Add -O0 to ensure that the symbol isn't optimized away by the compiler
extra_args += self.get_no_optimization_args() extra_args += self.get_no_optimization_args()
@ -783,32 +790,32 @@ int main(int argc, char **argv) {
# still detect the function. We still want to fail if __stub_foo or # still detect the function. We still want to fail if __stub_foo or
# _stub_foo are defined, of course. # _stub_foo are defined, of course.
header_templ = '#include <limits.h>\n{0}\n' + stubs_fail + '\nint main() {{ {1}; }}' header_templ = '#include <limits.h>\n{0}\n' + stubs_fail + '\nint main() {{ {1}; }}'
if self.links(header_templ.format(prefix, funcname), extra_args): if self.links(header_templ.format(prefix, funcname), env, extra_args):
return True return True
# Some functions like alloca() are defined as compiler built-ins which # Some functions like alloca() are defined as compiler built-ins which
# are inlined by the compiler, so test for that instead. Built-ins are # are inlined by the compiler, so test for that instead. Built-ins are
# special functions that ignore all includes and defines, so we just # special functions that ignore all includes and defines, so we just
# directly try to link via main(). # directly try to link via main().
return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), extra_args) return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), env, extra_args)
def has_member(self, typename, membername, prefix, extra_args=[]): def has_member(self, typename, membername, prefix, env, extra_args=[]):
templ = '''%s templ = '''%s
void bar() { void bar() {
%s foo; %s foo;
foo.%s; foo.%s;
}; };
''' '''
return self.compiles(templ % (prefix, typename, membername), extra_args) return self.compiles(templ % (prefix, typename, membername), env, extra_args)
def has_type(self, typename, prefix, extra_args): def has_type(self, typename, prefix, env, extra_args):
templ = '''%s templ = '''%s
void bar() { void bar() {
sizeof(%s); sizeof(%s);
}; };
''' '''
return self.compiles(templ % (prefix, typename), extra_args) return self.compiles(templ % (prefix, typename), env, extra_args)
def find_library(self, libname, extra_dirs): def find_library(self, libname, env, extra_dirs):
# First try if we can just add the library as -l. # First try if we can just add the library as -l.
code = '''int main(int argc, char **argv) { code = '''int main(int argc, char **argv) {
return 0; return 0;
@ -818,7 +825,7 @@ void bar() {
# Only try to find std libs if no extra dirs specified. # Only try to find std libs if no extra dirs specified.
if len(extra_dirs) == 0: if len(extra_dirs) == 0:
args = ['-l' + libname] args = ['-l' + libname]
if self.links(code, extra_args=args): if self.links(code, env, extra_args=args):
return args return args
# Not found? Try to find the library file itself. # Not found? Try to find the library file itself.
extra_dirs += self.get_library_dirs() extra_dirs += self.get_library_dirs()
@ -839,8 +846,8 @@ void bar() {
def thread_link_flags(self): def thread_link_flags(self):
return ['-pthread'] return ['-pthread']
def has_argument(self, arg): def has_argument(self, arg, env):
return self.compiles('int i;\n', extra_args=arg) return self.compiles('int i;\n', env, extra_args=arg)
class CPPCompiler(CCompiler): class CPPCompiler(CCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap): def __init__(self, exelist, version, is_cross, exe_wrap):
@ -1440,7 +1447,7 @@ class VisualStudioCCompiler(CCompiler):
if i.startswith('-L'): if i.startswith('-L'):
i = '/LIBPATH:' + i[2:] i = '/LIBPATH:' + i[2:]
# Translate GNU-style -lfoo library name to the import library # Translate GNU-style -lfoo library name to the import library
if i.startswith('-l'): elif i.startswith('-l'):
name = i[2:] name = i[2:]
if name in ('m', 'c'): if name in ('m', 'c'):
# With MSVC, these are provided by the C runtime which is # With MSVC, these are provided by the C runtime which is
@ -1469,7 +1476,7 @@ class VisualStudioCCompiler(CCompiler):
# Visual Studio is special. It ignores arguments it does not # Visual Studio is special. It ignores arguments it does not
# understand and you can't tell it to error out on those. # understand and you can't tell it to error out on those.
# http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
def has_argument(self, arg): def has_argument(self, arg, env):
warning_text = b'9002' warning_text = b'9002'
code = 'int i;\n' code = 'int i;\n'
(fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix) (fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
@ -1477,7 +1484,10 @@ class VisualStudioCCompiler(CCompiler):
ofile = open(srcname, 'w') ofile = open(srcname, 'w')
ofile.write(code) ofile.write(code)
ofile.close() ofile.close()
commands = self.exelist + [arg] + self.get_compile_only_args() + [srcname] # Read c_args/cpp_args/etc from the cross-info file (if needed)
extra_args = self.get_cross_extra_flags(env, compile=True, link=False)
extra_args += self.get_compile_only_args()
commands = self.exelist + [arg] + extra_args + [srcname]
mlog.debug('Running VS compile:') mlog.debug('Running VS compile:')
mlog.debug('Command line: ', ' '.join(commands)) mlog.debug('Command line: ', ' '.join(commands))
mlog.debug('Code:\n', code) mlog.debug('Code:\n', code)
@ -2121,10 +2131,10 @@ class VisualStudioLinker():
return [] return []
def unix_link_flags_to_native(self, args): def unix_link_flags_to_native(self, args):
return args return args[:]
def unix_compile_flags_to_native(self, args): def unix_compile_flags_to_native(self, args):
return args return args[:]
class ArLinker(): class ArLinker():
@ -2170,7 +2180,7 @@ class ArLinker():
return [] return []
def unix_link_flags_to_native(self, args): def unix_link_flags_to_native(self, args):
return args return args[:]
def unix_compile_flags_to_native(self, args): def unix_compile_flags_to_native(self, args):
return args return args[:]

@ -624,7 +624,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str): if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.') raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
result = self.compiler.run(code, extra_args) result = self.compiler.run(code, self.environment, extra_args)
if len(testname) > 0: if len(testname) > 0:
if not result.compiled: if not result.compiled:
h = mlog.red('DID NOT COMPILE') h = mlog.red('DID NOT COMPILE')
@ -648,7 +648,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str): if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_function must be a string.') raise InterpreterException('Prefix argument of has_function must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
had = self.compiler.has_member(typename, membername, prefix, extra_args) had = self.compiler.has_member(typename, membername, prefix, self.environment, extra_args)
if had: if had:
hadtxt = mlog.green('YES') hadtxt = mlog.green('YES')
else: else:
@ -683,7 +683,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str): if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_type must be a string.') raise InterpreterException('Prefix argument of has_type must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
had = self.compiler.has_type(typename, prefix, extra_args) had = self.compiler.has_type(typename, prefix, self.environment, extra_args)
if had: if had:
hadtxt = mlog.green('YES') hadtxt = mlog.green('YES')
else: else:
@ -713,7 +713,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str): if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.') raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
result = self.compiler.compiles(code, extra_args) result = self.compiler.compiles(code, self.environment, extra_args)
if len(testname) > 0: if len(testname) > 0:
if result: if result:
h = mlog.green('YES') h = mlog.green('YES')
@ -731,7 +731,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str): if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.') raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
result = self.compiler.links(code, extra_args) result = self.compiler.links(code, self.environment, extra_args)
if len(testname) > 0: if len(testname) > 0:
if result: if result:
h = mlog.green('YES') h = mlog.green('YES')
@ -746,7 +746,7 @@ class CompilerHolder(InterpreterObject):
check_stringlist(args) check_stringlist(args)
string = args[0] string = args[0]
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
haz = self.compiler.has_header(string, extra_args) haz = self.compiler.has_header(string, self.environment, extra_args)
if haz: if haz:
h = mlog.green('YES') h = mlog.green('YES')
else: else:
@ -764,7 +764,7 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str): if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_function must be a string.') raise InterpreterException('Prefix argument of has_function must be a string.')
extra_args = self.determine_args(kwargs) extra_args = self.determine_args(kwargs)
haz = self.compiler.has_header_symbol(hname, symbol, prefix, extra_args) haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, extra_args)
if haz: if haz:
h = mlog.green('YES') h = mlog.green('YES')
else: else:
@ -785,7 +785,7 @@ class CompilerHolder(InterpreterObject):
for i in search_dirs: for i in search_dirs:
if not os.path.isabs(i): if not os.path.isabs(i):
raise InvalidCode('Search directory %s is not an absolute path.' % i) raise InvalidCode('Search directory %s is not an absolute path.' % i)
linkargs = self.compiler.find_library(libname, search_dirs) linkargs = self.compiler.find_library(libname, self.environment, search_dirs)
if required and linkargs is None: if required and linkargs is None:
raise InterpreterException('Library {} not found'.format(libname)) raise InterpreterException('Library {} not found'.format(libname))
lib = dependencies.ExternalLibrary(libname, linkargs) lib = dependencies.ExternalLibrary(libname, linkargs)
@ -795,7 +795,7 @@ class CompilerHolder(InterpreterObject):
args = mesonlib.stringlistify(args) args = mesonlib.stringlistify(args)
if len(args) != 1: if len(args) != 1:
raise InterpreterException('Has_arg takes exactly one argument.') raise InterpreterException('Has_arg takes exactly one argument.')
result = self.compiler.has_argument(args[0]) result = self.compiler.has_argument(args[0], self.environment)
if result: if result:
h = mlog.green('YES') h = mlog.green('YES')
else: else:
@ -805,7 +805,7 @@ class CompilerHolder(InterpreterObject):
def first_supported_argument_method(self, args, kwargs): def first_supported_argument_method(self, args, kwargs):
for i in mesonlib.stringlistify(args): for i in mesonlib.stringlistify(args):
if self.compiler.has_argument(i): if self.compiler.has_argument(i, self.environment):
mlog.log('First supported argument:', mlog.bold(i)) mlog.log('First supported argument:', mlog.bold(i))
return [i] return [i]
mlog.log('First supported argument:', mlog.red('None')) mlog.log('First supported argument:', mlog.red('None'))

Loading…
Cancel
Save