Merge pull request #8616 from mesonbuild/xcodewarnopt

Set debug, optimization and warning flags in Xcode
pull/8621/head
Jussi Pakkanen 4 years ago committed by GitHub
commit b58e7d8478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 294
      mesonbuild/backend/xcodebackend.py

@ -20,7 +20,7 @@ from .. import mlog
import uuid, os, operator
import typing as T
from ..mesonlib import MesonException
from ..mesonlib import MesonException, OptionKey
from ..interpreter import Interpreter
INDENT = '\t'
@ -42,6 +42,19 @@ XCODETYPEMAP = {'c': 'sourcecode.c.c',
's': 'sourcecode.asm',
'asm': 'sourcecode.asm',
}
LANGNAMEMAP = {'c': 'C',
'cpp': 'CPLUSPLUS',
'objc': 'OBJC',
'objcpp': 'OBJCPLUSPLUS',
}
OPT2XCODEOPT = {'0': '0',
'g': '0',
'1': '1',
'2': '2',
'3': '3',
's': 's',
}
BOOL2XCODEBOOL = {True: 'YES', False: 'NO'}
class PbxItem:
def __init__(self, value, comment = ''):
@ -146,6 +159,9 @@ class PbxDict:
ofile.write(indent_level*INDENT + f'{i.key} = ')
i.value.write(ofile, indent_level)
else:
print(i)
print(i.key)
print(i.value)
raise RuntimeError('missing code')
else:
print(i)
@ -163,11 +179,12 @@ class XCodeBackend(backends.Backend):
super().__init__(build, interpreter)
self.name = 'xcode'
self.project_uid = self.environment.coredata.lang_guids['default'].replace('-', '')[:24]
self.buildtype = self.environment.coredata.get_option(OptionKey('buildtype'))
self.project_conflist = self.gen_id()
self.maingroup_id = self.gen_id()
self.all_id = self.gen_id()
self.all_buildconf_id = self.gen_id()
self.buildtypes = ['debug']
self.buildtypes = [self.buildtype]
self.test_id = self.gen_id()
self.test_command_id = self.gen_id()
self.test_buildconf_id = self.gen_id()
@ -182,7 +199,7 @@ class XCodeBackend(backends.Backend):
return str(uuid.uuid4()).upper().replace('-', '')[:24]
def get_target_dir(self, target):
dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_option(mesonlib.OptionKey('buildtype')))
dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_option(OptionKey('buildtype')))
os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
return dirname
@ -291,7 +308,7 @@ class XCodeBackend(backends.Backend):
self.buildmap[o] = self.gen_id()
def generate_buildstylemap(self):
self.buildstylemap = {'debug': self.gen_id()}
self.buildstylemap = {self.buildtype: self.gen_id()}
def generate_build_phase_map(self):
for tname, t in self.build.get_build_targets().items():
@ -306,17 +323,17 @@ class XCodeBackend(backends.Backend):
def generate_build_configuration_map(self):
self.buildconfmap = {}
for t in self.build.get_build_targets():
bconfs = {'debug': self.gen_id()}
bconfs = {self.buildtype: self.gen_id()}
self.buildconfmap[t] = bconfs
def generate_project_configurations_map(self):
self.project_configurations = {'debug': self.gen_id()}
self.project_configurations = {self.buildtype: self.gen_id()}
def generate_buildall_configurations_map(self):
self.buildall_configurations = {'debug': self.gen_id()}
self.buildall_configurations = {self.buildtype: self.gen_id()}
def generate_test_configurations_map(self):
self.test_configurations = {'debug': self.gen_id()}
self.test_configurations = {self.buildtype: self.gen_id()}
def generate_build_configurationlist_map(self):
self.buildconflistmap = {}
@ -725,9 +742,7 @@ class XCodeBackend(backends.Backend):
settings_dict = PbxDict()
bt_dict.add_item('buildSettings', settings_dict)
settings_dict.add_item('COMBINE_HIDPI_IMAGES', 'YES')
settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', 'NO')
settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', 0)
settings_dict.add_item('GCC_PREPROCESSOR_DEFINITIONS', '""')
settings_dict.add_item('GCC_SYMBOLS_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('INSTALL_PATH', '""')
@ -739,10 +754,8 @@ class XCodeBackend(backends.Backend):
settings_dict.add_item('SYMROOT', '"%s"' % self.environment.get_build_dir())
settings_dict.add_item('USE_HEADERMAP', 'NO')
warn_array = PbxArray()
warn_array.add_item('"$(inherited)"')
settings_dict.add_item('WARNING_CFLAGS', warn_array)
warn_array.add_item('"-Wmost"')
warn_array.add_item('"-Wno-four-char-constants"')
warn_array.add_item('"-Wno-unknown-pragmas"')
bt_dict.add_item('name', f'"{buildtype}"')
@ -754,9 +767,7 @@ class XCodeBackend(backends.Backend):
settings_dict = PbxDict()
bt_dict.add_item('buildSettings', settings_dict)
settings_dict.add_item('COMBINE_HIDPI_IMAGES', 'YES')
settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', 'NO')
settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', 0)
settings_dict.add_item('GCC_PREPROCESSOR_DEFINITIONS', '""')
settings_dict.add_item('GCC_SYMBOLS_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('INSTALL_PATH', '""')
@ -769,127 +780,133 @@ class XCodeBackend(backends.Backend):
settings_dict.add_item('USE_HEADERMAP', 'NO')
warn_array = PbxArray()
settings_dict.add_item('WARNING_CFLAGS', warn_array)
warn_array.add_item('"-Wmost"')
warn_array.add_item('"-Wno-four-char-constants"')
warn_array.add_item('"-Wno-unknown-pragmas"')
warn_array.add_item('"$(inherited)"')
bt_dict.add_item('name', f'"{buildtype}"')
# Now finally targets.
langnamemap = {'c': 'C', 'cpp': 'CPLUSPLUS', 'objc': 'OBJC', 'objcpp': 'OBJCPLUSPLUS'}
for target_name, target in self.build.get_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_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.soversion
else:
ldargs = dep_libs
install_path = ''
if dylib_version is not None:
product_name = target.get_basename() + '.' + dylib_version
else:
product_name = target.get_basename()
ldargs += target.link_args
linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
ldargs += self.build.get_project_link_args(linker, target.subproject, target.for_machine)
ldargs += self.build.get_global_link_args(linker, target.for_machine)
cargs = []
for dep in target.get_external_deps():
cargs += dep.get_compile_args()
ldargs += dep.get_link_args()
ldstr = ' '.join(ldargs)
valid = self.buildconfmap[target_name][buildtype]
langargs = {}
for lang in self.environment.coredata.compilers[target.for_machine]:
if lang not in langnamemap:
continue
# Add compile args added using add_project_arguments()
pargs = self.build.projects_args[target.for_machine].get(target.subproject, {}).get(lang, [])
# Add compile args added using add_global_arguments()
# These override per-project arguments
gargs = self.build.global_args[target.for_machine].get(lang, [])
targs = target.get_extra_args(lang)
args = pargs + gargs + targs
if args:
langname = langnamemap[lang]
compiler = target.compilers.get(lang)
lang_cargs = cargs
if compiler and target.implicit_include_directories:
lang_cargs += self.get_build_dir_include_args(target, compiler)
langargs[langname] = args
langargs[langname] += lang_cargs
symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
bt_dict = PbxDict()
objects_dict.add_item(valid, bt_dict, buildtype)
bt_dict.add_item('isa', 'XCBuildConfiguration')
settings_dict = PbxDict()
bt_dict.add_item('buildSettings', settings_dict)
settings_dict.add_item('COMBINE_HIDPI_IMAGES', 'YES')
if dylib_version is not None:
settings_dict.add_item('DYLIB_CURRENT_VERSION', f'"{dylib_version}')
settings_dict.add_item('EXECUTABLE_PREFIX', f'"{target.prefix}"')
if target.suffix == '':
suffix = ''
else:
suffix = '.' + target.suffix
settings_dict.add_item('EXECUTABLE_SUFFIX', f'"{suffix}"')
settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES')
settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', 0)
if target.has_pch:
# Xcode uses GCC_PREFIX_HEADER which only allows one file per target/executable. Precompiling various header files and
# applying a particular pch to each source file will require custom scripts (as a build phase) and build flags per each
# file. Since Xcode itself already discourages precompiled headers in favor of modules we don't try much harder here.
pchs = target.get_pch('c') + target.get_pch('cpp') + target.get_pch('objc') + target.get_pch('objcpp')
# Make sure to use headers (other backends require implementation files like *.c *.cpp, etc; these should not be used here)
pchs = [pch for pch in pchs if pch.endswith('.h') or pch.endswith('.hh') or pch.endswith('hpp')]
if pchs:
if len(pchs) > 1:
mlog.warning('Unsupported Xcode configuration: More than 1 precompiled header found "{}". Target "{}" might not compile correctly.'.format(str(pchs), target.name))
relative_pch_path = os.path.join(target.get_subdir(), pchs[0]) # Path relative to target so it can be used with "$(PROJECT_DIR)"
settings_dict.add_item('GCC_PRECOMPILE_PREFIX_HEADER', 'YES')
settings_dict.add_item('GCC_PREFIX_HEADER', f'"$(PROJECT_DIR)/{relative_pch_path}"')
settings_dict.add_item('GCC_PREPROCESSOR_DEFINITIONS', '""')
settings_dict.add_item('GCC_SYMBOLS_PRIVATE_EXTERN', 'NO')
if headerdirs:
quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs])
settings_dict.add_item('HEADER_SEARCH_PATHS', f'({quotedh}')
settings_dict.add_item('INSTALL_PATH', f'"{install_path}"')
settings_dict.add_item('LIBRARY_SEARCH_PATHS', '""')
if isinstance(target, build.SharedLibrary):
settings_dict.add_item('LIBRARY_STYLE', 'DYNAMIC')
for langname, args in langargs.items():
settings_dict.add_item(f'OTHER_{langname}FLAGS', args)
settings_dict.add_item('OTHER_LDFLAGS', f'"{ldstr}"')
settings_dict.add_item('OTHER_REZFLAGS', '""')
settings_dict.add_item('PRODUCT_NAME', product_name)
settings_dict.add_item('SECTORDER_FLAGS', '""')
settings_dict.add_item('SYMROOT', f'"{symroot}"')
settings_dict.add_item('SYSTEM_HEADER_SEARCH_PATHS', '"{}"'.format(self.environment.get_build_dir()))
settings_dict.add_item('USE_HEADERMAP', 'NO')
warn_array = PbxArray()
settings_dict.add_item('WARNING_CFLAGS', warn_array)
warn_array.add_item('"-Wmost"')
warn_array.add_item('"-Wno-four-char-constants"')
warn_array.add_item('"-Wno-unknown-pragmas"')
bt_dict.add_item('name', buildtype)
self.generate_single_build_target(objects_dict, target_name, target)
def generate_single_build_target(self, objects_dict, target_name, target):
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_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.soversion
else:
ldargs = dep_libs
install_path = ''
if dylib_version is not None:
product_name = target.get_basename() + '.' + dylib_version
else:
product_name = target.get_basename()
ldargs += target.link_args
linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
ldargs += self.build.get_project_link_args(linker, target.subproject, target.for_machine)
ldargs += self.build.get_global_link_args(linker, target.for_machine)
cargs = []
for dep in target.get_external_deps():
cargs += dep.get_compile_args()
ldargs += dep.get_link_args()
ldstr = ' '.join(ldargs)
valid = self.buildconfmap[target_name][buildtype]
langargs = {}
for lang in self.environment.coredata.compilers[target.for_machine]:
if lang not in LANGNAMEMAP:
continue
compiler = target.compilers.get(lang)
# Start with warning args
warn_args = compiler.get_warn_args(self.get_option_for_target(OptionKey('warning_level'), target))
# Add compile args added using add_project_arguments()
pargs = self.build.projects_args[target.for_machine].get(target.subproject, {}).get(lang, [])
# Add compile args added using add_global_arguments()
# These override per-project arguments
gargs = self.build.global_args[target.for_machine].get(lang, [])
targs = target.get_extra_args(lang)
args = warn_args + pargs + gargs + targs
if args:
langname = LANGNAMEMAP[lang]
lang_cargs = cargs
if compiler and target.implicit_include_directories:
lang_cargs += self.get_build_dir_include_args(target, compiler)
langargs[langname] = args
langargs[langname] += lang_cargs
symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
bt_dict = PbxDict()
objects_dict.add_item(valid, bt_dict, buildtype)
bt_dict.add_item('isa', 'XCBuildConfiguration')
settings_dict = PbxDict()
bt_dict.add_item('buildSettings', settings_dict)
settings_dict.add_item('COMBINE_HIDPI_IMAGES', 'YES')
if dylib_version is not None:
settings_dict.add_item('DYLIB_CURRENT_VERSION', f'"{dylib_version}')
settings_dict.add_item('EXECUTABLE_PREFIX', f'"{target.prefix}"')
if target.suffix == '':
suffix = ''
else:
suffix = '.' + target.suffix
settings_dict.add_item('EXECUTABLE_SUFFIX', f'"{suffix}"')
settings_dict.add_item('GCC_GENERATE_DEBUGGING_SYMBOLS', BOOL2XCODEBOOL[self.get_option_for_target(OptionKey('debug'), target)])
settings_dict.add_item('GCC_INLINES_ARE_PRIVATE_EXTERN', 'NO')
settings_dict.add_item('GCC_OPTIMIZATION_LEVEL', OPT2XCODEOPT[self.get_option_for_target(OptionKey('optimization'), target)])
if target.has_pch:
# Xcode uses GCC_PREFIX_HEADER which only allows one file per target/executable. Precompiling various header files and
# applying a particular pch to each source file will require custom scripts (as a build phase) and build flags per each
# file. Since Xcode itself already discourages precompiled headers in favor of modules we don't try much harder here.
pchs = target.get_pch('c') + target.get_pch('cpp') + target.get_pch('objc') + target.get_pch('objcpp')
# Make sure to use headers (other backends require implementation files like *.c *.cpp, etc; these should not be used here)
pchs = [pch for pch in pchs if pch.endswith('.h') or pch.endswith('.hh') or pch.endswith('hpp')]
if pchs:
if len(pchs) > 1:
mlog.warning('Unsupported Xcode configuration: More than 1 precompiled header found "{}". Target "{}" might not compile correctly.'.format(str(pchs), target.name))
relative_pch_path = os.path.join(target.get_subdir(), pchs[0]) # Path relative to target so it can be used with "$(PROJECT_DIR)"
settings_dict.add_item('GCC_PRECOMPILE_PREFIX_HEADER', 'YES')
settings_dict.add_item('GCC_PREFIX_HEADER', f'"$(PROJECT_DIR)/{relative_pch_path}"')
settings_dict.add_item('GCC_PREPROCESSOR_DEFINITIONS', '""')
settings_dict.add_item('GCC_SYMBOLS_PRIVATE_EXTERN', 'NO')
if headerdirs:
quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs])
settings_dict.add_item('HEADER_SEARCH_PATHS', f'({quotedh}')
settings_dict.add_item('INSTALL_PATH', f'"{install_path}"')
settings_dict.add_item('LIBRARY_SEARCH_PATHS', '""')
if isinstance(target, build.SharedLibrary):
settings_dict.add_item('LIBRARY_STYLE', 'DYNAMIC')
self.add_otterargs(settings_dict, langargs)
settings_dict.add_item('OTHER_LDFLAGS', f'"{ldstr}"')
settings_dict.add_item('OTHER_REZFLAGS', '""')
settings_dict.add_item('PRODUCT_NAME', product_name)
settings_dict.add_item('SECTORDER_FLAGS', '""')
settings_dict.add_item('SYMROOT', f'"{symroot}"')
settings_dict.add_item('SYSTEM_HEADER_SEARCH_PATHS', '"{}"'.format(self.environment.get_build_dir()))
settings_dict.add_item('USE_HEADERMAP', 'NO')
warn_array = PbxArray()
settings_dict.add_item('WARNING_CFLAGS', warn_array)
warn_array.add_item('"$(inherited)"')
bt_dict.add_item('name', buildtype)
def add_otterargs(self, settings_dict, langargs):
for langname, args in langargs.items():
if args:
# FIXME, proper quoting
settings_dict.add_item(f'OTHER_{langname}FLAGS', '"' + ' '.join(args) + '"')
def generate_xc_configurationList(self, objects_dict):
# FIXME: sort items
@ -901,7 +918,7 @@ class XCodeBackend(backends.Backend):
for buildtype in self.buildtypes:
confs_arr.add_item(self.project_configurations[buildtype], buildtype)
conf_dict.add_item('defaultConfigurationIsVisible', 0)
conf_dict.add_item('defaultConfigurationName', 'debug')
conf_dict.add_item('defaultConfigurationName', self.buildtype)
# Now the all target
all_dict = PbxDict()
@ -912,7 +929,7 @@ class XCodeBackend(backends.Backend):
for buildtype in self.buildtypes:
conf_arr.add_item(self.buildall_configurations[buildtype], buildtype)
all_dict.add_item('defaultConfigurationIsVisible', 0)
all_dict.add_item('defaultConfigurationName', 'debug')
all_dict.add_item('defaultConfigurationName', self.buildtype)
# Test target
test_dict = PbxDict()
@ -923,7 +940,7 @@ class XCodeBackend(backends.Backend):
for buildtype in self.buildtypes:
conf_arr.add_item(self.test_configurations[buildtype], buildtype)
test_dict.add_item('defaultConfigurationIsVisible', 0)
test_dict.add_item('defaultConfigurationName', 'debug')
test_dict.add_item('defaultConfigurationName', self.buildtype)
for target_name in self.build.get_build_targets():
t_dict = PbxDict()
@ -932,11 +949,10 @@ class XCodeBackend(backends.Backend):
t_dict.add_item('isa', 'XCConfigurationList')
conf_arr = PbxArray()
t_dict.add_item('buildConfigurations', conf_arr)
typestr = 'debug'
idval = self.buildconfmap[target_name][typestr]
conf_arr.add_item(idval, typestr)
idval = self.buildconfmap[target_name][self.buildtype]
conf_arr.add_item(idval, self.buildtype)
t_dict.add_item('defaultConfigurationIsVisible', 0)
t_dict.add_item('defaultConfigurationName', typestr)
t_dict.add_item('defaultConfigurationName', self.buildtype)
def generate_prefix(self, pbxdict):

Loading…
Cancel
Save