From ea3b54d40252fcb87eb1852223f125398b1edbdf Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 25 Feb 2018 15:49:58 +0200 Subject: Use include_directories for D impdirs. Change the code to store D properties as plain data. Only convert them to compiler flags in the backend. This also means we can fully parse D arguments without needing to know the compiler being used. --- mesonbuild/backend/ninjabackend.py | 3 +++ mesonbuild/build.py | 10 +++++++--- mesonbuild/compilers/d.py | 14 +++++++++++--- mesonbuild/interpreter.py | 38 ++++++++++++++++++++++++++++++------- test cases/d/9 features/meson.build | 17 ++++++++++++++++- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8b616a6..8577bc7 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2257,6 +2257,9 @@ rule FORTRAN_DEP_HACK depelem.write(outfile) commands += compiler.get_module_outdir_args(self.get_target_private_dir(target)) + if compiler.language == 'd': + commands += compiler.get_feature_args(target.d_features, self.build_to_src) + element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) for d in header_deps: if isinstance(d, File): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 400b9e5..c5fc7f6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -355,6 +355,7 @@ class BuildTarget(Target): self.extra_args = {} self.generated = [] self.extra_files = [] + self.d_features = {} # Sources can be: # 1. Pre-existing source files in the source tree # 2. Pre-existing sources generated by configure_file in the build tree @@ -682,12 +683,15 @@ just like those detected with the dependency() function.''') dfeature_versions = kwargs.get('d_module_versions', None) if dfeature_versions: dfeatures['versions'] = dfeature_versions - dfeature_import_dirs = kwargs.get('d_import_dirs', None) - if dfeature_import_dirs: + if 'd_import_dirs' in kwargs: + dfeature_import_dirs = extract_as_list(kwargs, 'd_import_dirs', unholder=True) + for d in dfeature_import_dirs: + if not isinstance(d, IncludeDirs): + raise InvalidArguments('Arguments to d_import_dirs must be include_directories.') dfeatures['import_dirs'] = dfeature_import_dirs if dfeatures: if 'd' in self.compilers: - self.add_compiler_args('d', self.compilers['d'].get_feature_args(dfeatures)) + self.d_features = dfeatures self.link_args = extract_as_list(kwargs, 'link_args') for i in self.link_args: diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 3320736..474e1bd 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -93,7 +93,7 @@ class DCompiler(Compiler): # FIXME: Make this work for Windows, MacOS and cross-compiling return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, is_shared_module) - def get_feature_args(self, kwargs): + def get_feature_args(self, kwargs, build_to_src): res = [] if 'unittest' in kwargs: unittest = kwargs.pop('unittest') @@ -122,8 +122,16 @@ class DCompiler(Compiler): import_dir_arg = d_feature_args[self.id]['import_dir'] if not import_dir_arg: raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string()) - for d in import_dirs: - res.append('{0}{1}'.format(import_dir_arg, d)) + for idir_obj in import_dirs: + basedir = idir_obj.get_curdir() + for idir in idir_obj.get_incdirs(): + # Avoid superfluous '/.' at the end of paths when d is '.' + if idir not in ('', '.'): + expdir = os.path.join(basedir, idir) + else: + expdir = basedir + srctreedir = os.path.join(build_to_src, expdir) + res.append('{0}{1}'.format(import_dir_arg, srctreedir)) if kwargs: raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 8041526..5c333a8 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -37,6 +37,7 @@ from pathlib import PurePath import importlib + def stringifyUserArguments(args): if isinstance(args, list): return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args]) @@ -247,7 +248,7 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder): return val def get(self, name): - return self.held_object.values[name] # (val, desc) + return self.held_object.values[name] # (val, desc) def keys(self): return self.held_object.values.keys() @@ -816,7 +817,8 @@ class CompilerHolder(InterpreterObject): ''' if not hasattr(self.compiler, 'get_feature_args'): raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language())) - return self.compiler.get_feature_args({'unittest': 'true'}) + build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) + return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src) def has_member_method(self, args, kwargs): if len(args) != 2: @@ -1309,6 +1311,7 @@ class MesonMain(InterpreterObject): return args[1] raise InterpreterException('Unknown cross property: %s.' % propname) + pch_kwargs = set(['c_pch', 'cpp_pch']) lang_arg_kwargs = set([ @@ -2847,12 +2850,17 @@ root and issuing %s. @permittedKwargs(permitted_kwargs['include_directories']) @stringArgs def func_include_directories(self, node, args, kwargs): + return self.build_incdir_object(args, kwargs.get('is_system', False)) + + def build_incdir_object(self, incdir_strings, is_system=False): + if not isinstance(is_system, bool): + raise InvalidArguments('Is_system must be boolean.') src_root = self.environment.get_source_dir() build_root = self.environment.get_build_dir() absbase_src = os.path.join(src_root, self.subdir) absbase_build = os.path.join(build_root, self.subdir) - for a in args: + for a in incdir_strings: if a.startswith(src_root): raise InvalidArguments('''Tried to form an absolute path to a source dir. You should not do that but use relative paths instead. @@ -2875,10 +2883,7 @@ different subdirectory. absdir_build = os.path.join(absbase_build, a) if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build): raise InvalidArguments('Include dir %s does not exist.' % a) - is_system = kwargs.get('is_system', False) - if not isinstance(is_system, bool): - raise InvalidArguments('Is_system must be boolean.') - i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system)) + i = IncludeDirsHolder(build.IncludeDirs(self.subdir, incdir_strings, is_system)) return i @permittedKwargs(permitted_kwargs['add_test_setup']) @@ -3106,6 +3111,7 @@ different subdirectory. else: mlog.debug('Unknown target type:', str(targetholder)) raise RuntimeError('Unreachable code') + self.kwarg_strings_to_includedirs(kwargs) target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs) if is_cross: self.add_cross_stdlib_info(target) @@ -3114,6 +3120,23 @@ different subdirectory. self.project_args_frozen = True return l + def kwarg_strings_to_includedirs(self, kwargs): + if 'd_import_dirs' in kwargs: + items = mesonlib.extract_as_list(kwargs, 'd_import_dirs') + cleaned_items = [] + for i in items: + if isinstance(i, str): + # BW compatibility. This was permitted so we must support it + # for a few releases so people can transition to "correct" + # path declarations. + if i.startswith(self.environment.get_source_dir()): + mlog.warning('''Building a path to the source dir is not supported. Use a relative path instead. +This will become a hard error in the future.''') + i = os.path.relpath(i, os.path.join(self.environment.get_source_dir(), self.subdir)) + i = self.build_incdir_object([i]) + cleaned_items.append(i) + kwargs['d_import_dirs'] = cleaned_items + def get_used_languages(self, target): result = {} for i in target.sources: @@ -3152,6 +3175,7 @@ different subdirectory. if idx >= len(arg_strings): raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx)) return arg_strings[idx] + return re.sub(r'@(\d+)@', arg_replace, templ) # Only permit object extraction from the same subproject diff --git a/test cases/d/9 features/meson.build b/test cases/d/9 features/meson.build index 9e63710..356e9f3 100644 --- a/test cases/d/9 features/meson.build +++ b/test cases/d/9 features/meson.build @@ -1,8 +1,22 @@ project('D Features', 'd') -# directory for data +# ONLY FOR BACKWARDS COMPATIBILITY. +# DO NOT DO THIS IN NEW CODE! +# USE include_directories() INSTEAD OF BUILDING +# STRINGS TO PATHS MANUALLY! data_dir = join_paths(meson.current_source_dir(), 'data') +e_plain_bcompat = executable('dapp_menu_bcompat', + 'app.d', + d_import_dirs: [data_dir] +) +test('dapp_menu_t_fail_bcompat', e_plain_bcompat, should_fail: true) +test('dapp_menu_t_bcompat', e_plain_bcompat, args: ['menu']) + +# directory for data +# This is the correct way to do this. +data_dir = include_directories('data') + e_plain = executable('dapp_menu', 'app.d', d_import_dirs: [data_dir] @@ -10,6 +24,7 @@ e_plain = executable('dapp_menu', test('dapp_menu_t_fail', e_plain, should_fail: true) test('dapp_menu_t', e_plain, args: ['menu']) + # test feature versions and string imports e_versions = executable('dapp_versions', 'app.d', -- cgit v1.1