diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b36b98c9d..b3b6df93c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1158,7 +1158,8 @@ class Backend: "language": "", "compiler": ["result", "of", "comp.get_exelist()"], "parameters": ["list", "of", "compiler", "parameters], - "source_files": ["list", "of", "all", "", "source", "files"] + "sources": ["list", "of", "all", "", "source", "files"], + "generated_sources": ["list", "of", "generated", "source", "files"] } ] @@ -1169,20 +1170,23 @@ class Backend: source_list = [] for i in source_list_raw: if isinstance(i, mesonlib.File): - source_list += [os.path.join(i.subdir, i.fname)] + source_list += [i.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())] elif isinstance(i, str): - source_list += [i] + source_list += [os.path.join(self.environment.get_source_dir(), i)] + source_list = list(map(lambda x: os.path.normpath(x), source_list)) return [{ 'language': 'unknown', 'compiler': [], 'parameters': [], - 'source_files': source_list + 'sources': source_list, + 'generated_sources': [] }] return [{ 'language': 'unknown', 'compiler': [], 'parameters': [], - 'source_files': [] + 'sources': [], + 'generated_sources': [] }] diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 44bdaab55..06b450678 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -150,6 +150,8 @@ class NinjaBackend(backends.Backend): self.ninja_filename = 'build.ninja' self.fortran_deps = {} self.all_outputs = {} + self.introspection_data = {} + self._intro_last_index = 0 def create_target_alias(self, to_target, outfile): # We need to use aliases for targets that might be used as directory @@ -321,6 +323,73 @@ int dummy; return False return True + def create_target_source_introspection(self, target, comp, parameters, sources, generated_sources): + ''' + Adds the source file introspection information for a language of a target + + Internal introspection storage formart: + self.introspection_data = { + '': [ + { + 'language: 'lang', + 'compiler': ['comp', 'exe', 'list'], + 'parameters': ['UNIQUE', 'parameter', 'list'], + 'sources': [], + 'generated_sources': [], + 'id_hash': 634523234445 # Internal unique hash to identify a compiler / language / paramerter combo + } + ] + } + ''' + build_dir = self.environment.get_build_dir() + id = target.get_id() + lang = comp.get_language() + tgt = self.introspection_data[id] + id_hash = hash((lang, CompilerArgs)) + src_block = None + # Find an existing entry or create a new one + # ... first check the last used index + if self._intro_last_index < len(tgt): + tmp = tgt[self._intro_last_index] + if tmp['id_hash'] == id_hash: + src_block = tmp + # ... check all entries + if src_block is None: + for idx, i in enumerate(tgt): + if i['id_hash'] == id_hash: + src_block = i + self._intro_last_index = idx + break + # ... create a new one + if src_block is None: + # Convert parameters + if isinstance(parameters, CompilerArgs): + parameters = parameters.to_native(copy=True) + for idx, i in enumerate(parameters): + if i[:2] == '-I' or i[:2] == '/I' or i[:2] == '-L': + parameters[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + if target.is_cross: + parameters += comp.get_cross_extra_flags(self.environment, False) + # The new entry + src_block = { + 'language': lang, + 'compiler': comp.get_exelist(), + 'parameters': parameters, + 'sources': [], + 'generated_sources': [], + 'id_hash': id_hash + } + self._intro_last_index = len(tgt) + tgt.append(src_block) + # Make source files absolute + sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in sources] + sources = [os.path.normpath(os.path.join(build_dir, x)) for x in sources] + generated_sources = [x.rel_to_builddir(self.build_to_src) if isinstance(x, File) else x for x in generated_sources] + generated_sources = [os.path.normpath(os.path.join(build_dir, x)) for x in generated_sources] + # Add the source files + src_block['sources'] += sources + src_block['generated_sources'] += generated_sources + def generate_target(self, target, outfile): if isinstance(target, build.CustomTarget): self.generate_custom_target(target, outfile) @@ -330,6 +399,8 @@ int dummy; if name in self.processed_targets: return self.processed_targets[name] = True + # Initialize an empty introspection source list + self.introspection_data[name] = [] # Generate rules for all dependency targets self.process_target_dependencies(target, outfile) # If target uses a language that cannot link to C objects, @@ -770,14 +841,16 @@ int dummy; # Add possible java generated files to src list generated_sources = self.get_target_generated_sources(target) + gen_src_list = [] for rel_src, gensrc in generated_sources.items(): dirpart, fnamepart = os.path.split(rel_src) raw_src = File(True, dirpart, fnamepart) if rel_src.endswith('.java'): - src_list.append(raw_src) + gen_src_list.append(raw_src) - for src in src_list: - plain_class_path = self.generate_single_java_compile(src, target, compiler, outfile) + compile_args = self.determine_single_java_compile_args(target, compiler) + for src in src_list + gen_src_list: + plain_class_path = self.generate_single_java_compile(src, target, compiler, compile_args, outfile) class_list.append(plain_class_path) class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list] manifest_path = os.path.join(self.get_target_private_dir(target), 'META-INF', 'MANIFEST.MF') @@ -803,6 +876,8 @@ int dummy; elem.add_dep(class_dep_list) elem.add_item('ARGS', commands) elem.write(outfile) + # Create introspection information + self.create_target_source_introspection(target, compiler, compile_args, src_list, gen_src_list) def generate_cs_resource_tasks(self, target, outfile): args = [] @@ -856,10 +931,11 @@ int dummy; else: outputs = [outname_rel] generated_sources = self.get_target_generated_sources(target) + generated_rel_srcs = [] for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) if rel_src.lower().endswith('.cs'): - rel_srcs.append(os.path.normpath(rel_src)) + generated_rel_srcs.append(os.path.normpath(rel_src)) deps.append(os.path.normpath(rel_src)) for dep in target.get_external_deps(): @@ -867,19 +943,15 @@ int dummy; commands += self.build.get_project_args(compiler, target.subproject, target.is_cross) commands += self.build.get_global_args(compiler, target.is_cross) - elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs) + elem = NinjaBuildElement(self.all_outputs, outputs, 'cs_COMPILER', rel_srcs + generated_rel_srcs) elem.add_dep(deps) elem.add_item('ARGS', commands) elem.write(outfile) self.generate_generator_list_rules(target, outfile) + self.create_target_source_introspection(target, compiler, commands, rel_srcs, generated_rel_srcs) - def generate_single_java_compile(self, src, target, compiler, outfile): - deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] - generated_sources = self.get_target_generated_sources(target) - for rel_src, gensrc in generated_sources.items(): - if rel_src.endswith('.java'): - deps.append(rel_src) + def determine_single_java_compile_args(self, target, compiler): args = [] args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target)) args += self.build.get_global_args(compiler, target.is_cross) @@ -894,6 +966,14 @@ int dummy; for idir in i.get_incdirs(): sourcepath += os.path.join(self.build_to_src, i.curdir, idir) + os.pathsep args += ['-sourcepath', sourcepath] + return args + + def generate_single_java_compile(self, src, target, compiler, args, outfile): + deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets] + generated_sources = self.get_target_generated_sources(target) + for rel_src, gensrc in generated_sources.items(): + if rel_src.endswith('.java'): + deps.append(rel_src) rel_src = src.rel_to_builddir(self.build_to_src) plain_class_path = src.fname[:-4] + 'class' rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path) @@ -1102,6 +1182,7 @@ int dummy; element.add_item('ARGS', args) element.add_dep(extra_dep_files) element.write(outfile) + self.create_target_source_introspection(target, valac, args, all_files, []) return other_src[0], other_src[1], vala_c_src def generate_rust_target(self, target, outfile): @@ -1193,6 +1274,7 @@ int dummy; element.write(outfile) if isinstance(target, build.SharedLibrary): self.generate_shsym(outfile, target) + self.create_target_source_introspection(target, rustc, args, [main_rust_file], []) def swift_module_file_name(self, target): return os.path.join(self.get_target_private_dir(target), @@ -1241,12 +1323,14 @@ int dummy; module_name = self.target_swift_modulename(target) swiftc = target.compilers['swift'] abssrc = [] + relsrc = [] abs_headers = [] header_imports = [] for i in target.get_sources(): if swiftc.can_compile(i): - relsrc = i.rel_to_builddir(self.build_to_src) - abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), relsrc)) + rels = i.rel_to_builddir(self.build_to_src) + abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), rels)) + relsrc.append(rels) abssrc.append(abss) elif self.environment.is_header(i): relh = i.rel_to_builddir(self.build_to_src) @@ -1330,6 +1414,8 @@ int dummy; elem.write(outfile) else: raise MesonException('Swift supports only executable and static library targets.') + # Introspection information + self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated) def generate_static_link_rules(self, is_cross, outfile): num_pools = self.environment.coredata.backend_options['backend_max_links'].value @@ -2049,6 +2135,12 @@ rule FORTRAN_DEP_HACK%s commands = self._generate_single_compile(target, compiler, is_generated) commands = CompilerArgs(commands.compiler, commands) + # Create introspection information + if is_generated is False: + self.create_target_source_introspection(target, compiler, commands, [src], []) + else: + self.create_target_source_introspection(target, compiler, commands, [], [src]) + build_dir = self.environment.get_build_dir() if isinstance(src, File): rel_src = src.rel_to_builddir(self.build_to_src) @@ -2663,6 +2755,15 @@ rule FORTRAN_DEP_HACK%s elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '') elem.write(outfile) + def get_introspection_data(self, target_id, target): + if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0: + return super().get_introspection_data(target_id, target) + + result = self.introspection_data[target_id].copy() + for i in result: + i.pop('id_hash') + return result + def load(build_dir): filename = os.path.join(build_dir, 'meson-private', 'install.dat') with open(filename, 'rb') as f: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index e10b95bf0..9372ed897 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -272,7 +272,7 @@ def list_target_files(target_name, targets): raise RuntimeError('Target with the ID "{}" could not be found'.format(target_name)) for i in tgt['sources']: - result += i['source_files'] + result += i['sources'] + i['generated_sources'] return ('target_files', result) diff --git a/run_unittests.py b/run_unittests.py index 0c313d26c..f3f3fa9c8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -2562,6 +2562,7 @@ int main(int argc, char **argv) { for t in t_intro: id = t['id'] tf_intro = self.introspect(['--target-files', id]) + tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) self.assertEqual(tf_intro, expected[id]) self.wipe() @@ -2576,6 +2577,9 @@ int main(int argc, char **argv) { for t in t_intro: id = t['id'] tf_intro = self.introspect(['--target-files', id]) + print(tf_intro) + tf_intro = list(map(lambda x: os.path.relpath(x, testdir), tf_intro)) + print(tf_intro) self.assertEqual(tf_intro, expected[id]) self.wipe() @@ -3179,7 +3183,8 @@ recommended as it is not supported on some platforms''') ('language', str), ('compiler', list), ('parameters', list), - ('source_files', list), + ('sources', list), + ('generated_sources', list), ] # First load all files