diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7757300c5..59382a43a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1072,11 +1072,22 @@ class Generator: if not isinstance(capture, bool): raise InvalidArguments('Capture must be boolean.') self.capture = capture + if 'preserve_path_from' in kwargs: + self.preserve_path_from = kwargs['preserve_path_from'] + if not isinstance(self.preserve_path_from, str): + raise InvalidArguments('Preserve_path_from must be a string.') + self.preserve_path_from = os.path.normpath(self.preserve_path_from) + if not os.path.isabs(self.preserve_path_from): + # This is a bit of a hack. Fix properly before merging. + raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.') + else: + self.preserve_path_from = None def get_base_outnames(self, inname): plainname = os.path.split(inname)[1] basename = os.path.splitext(plainname)[0] - return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] + bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] + return bases def get_dep_outname(self, inname): if self.depfile is None: @@ -1090,6 +1101,10 @@ class Generator: basename = os.path.splitext(plainname)[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] + def is_parent_path(self, parent, trial): + relpath = os.path.relpath(os.path.normpath(trial), os.path.normpath(parent)) + return not relpath.startswith('..') # For subdirs we can only go "down". + def process_files(self, name, files, state, extra_args=[]): output = GeneratedList(self, extra_args=extra_args) for f in files: @@ -1097,7 +1112,11 @@ class Generator: f = File.from_source_file(state.environment.source_dir, state.subdir, f) elif not isinstance(f, File): raise InvalidArguments('{} arguments must be strings or files not {!r}.'.format(name, f)) - output.add_file(f) + if self.preserve_path_from: + abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir) + if not self.is_parent_path(self.preserve_path_from, abs_f): + raise InvalidArguments('When using preserve_path_from, all input files must be in a subdirectory of the given dir.') + output.add_file(f, state) return output @@ -1113,9 +1132,21 @@ class GeneratedList: self.extra_depends = [] self.extra_args = extra_args - def add_file(self, newfile): + def add_preserved_path_segment(self, infile, outfiles, state): + result = [] + in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir) + assert(os.path.isabs(self.generator.preserve_path_from)) + rel = os.path.relpath(in_abs, self.generator.preserve_path_from) + path_segment = os.path.split(rel)[0] + for of in outfiles: + result.append(os.path.join(path_segment, of)) + return result + + def add_file(self, newfile, state): self.infilelist.append(newfile) outfiles = self.generator.get_base_outnames(newfile.fname) + if self.generator.preserve_path_from: + outfiles = self.add_preserved_path_segment(newfile, outfiles, state) self.outfilelist += outfiles self.outmap[newfile] = outfiles diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index f33d437bf..ea741a07d 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1372,7 +1372,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'}, 'executable': exe_kwargs, 'find_program': {'required', 'native'}, - 'generator': {'arguments', 'output', 'depfile', 'capture'}, + 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'}, 'include_directories': {'is_system'}, 'install_data': {'install_dir', 'install_mode', 'sources'}, 'install_headers': {'install_dir', 'subdir'}, diff --git a/test cases/frameworks/5 protocol buffers/asubdir/defs.proto b/test cases/frameworks/5 protocol buffers/asubdir/defs.proto index f7956517c..dad575470 100644 --- a/test cases/frameworks/5 protocol buffers/asubdir/defs.proto +++ b/test cases/frameworks/5 protocol buffers/asubdir/defs.proto @@ -1,3 +1,5 @@ +syntax = "proto3"; + message Dummy { - required string text = 1; + string text = 1; } diff --git a/test cases/frameworks/5 protocol buffers/defs.proto b/test cases/frameworks/5 protocol buffers/defs.proto index f7956517c..dad575470 100644 --- a/test cases/frameworks/5 protocol buffers/defs.proto +++ b/test cases/frameworks/5 protocol buffers/defs.proto @@ -1,3 +1,5 @@ +syntax = "proto3"; + message Dummy { - required string text = 1; + string text = 1; } diff --git a/test cases/frameworks/5 protocol buffers/meson.build b/test cases/frameworks/5 protocol buffers/meson.build index 58666f9b9..f8ca7b1a6 100644 --- a/test cases/frameworks/5 protocol buffers/meson.build +++ b/test cases/frameworks/5 protocol buffers/meson.build @@ -18,3 +18,4 @@ e = executable('prog', 'main.cpp', generated, test('prototest', e) subdir('asubdir') +subdir('withpath') diff --git a/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/simple.proto b/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/simple.proto new file mode 100644 index 000000000..336779f3d --- /dev/null +++ b/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/simple.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package subdirectorial; + +message SimpleMessage { + int32 the_integer = 1; +} diff --git a/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/subsite/complex.proto b/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/subsite/complex.proto new file mode 100644 index 000000000..8dc32c205 --- /dev/null +++ b/test cases/frameworks/5 protocol buffers/withpath/com/mesonbuild/subsite/complex.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package subdirectorial; + +import "com/mesonbuild/simple.proto"; + +message ComplexMessage { + string a_message = 1; + SimpleMessage sm = 2; +} diff --git a/test cases/frameworks/5 protocol buffers/withpath/meson.build b/test cases/frameworks/5 protocol buffers/withpath/meson.build new file mode 100644 index 000000000..f5b453ecd --- /dev/null +++ b/test cases/frameworks/5 protocol buffers/withpath/meson.build @@ -0,0 +1,14 @@ +# Testing protobuf files that are deeply hierarchical +# and must preserve their path segments in output files +# because protoc will always put it in there. + +gen = generator(protoc, \ + output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'], + preserve_path_from : meson.current_source_dir(), + arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--cpp_out=@BUILD_DIR@', '@INPUT@']) + +generated = gen.process('com/mesonbuild/simple.proto', + 'com/mesonbuild/subsite/complex.proto') +e = executable('pathprog', 'pathprog.cpp', generated, + dependencies : dep) +test('pathprog', e) diff --git a/test cases/frameworks/5 protocol buffers/withpath/pathprog.cpp b/test cases/frameworks/5 protocol buffers/withpath/pathprog.cpp new file mode 100644 index 000000000..83af4b256 --- /dev/null +++ b/test cases/frameworks/5 protocol buffers/withpath/pathprog.cpp @@ -0,0 +1,16 @@ +#include"com/mesonbuild/simple.pb.h" +#include"com/mesonbuild/subsite/complex.pb.h" + +#include + +int main(int argc, char **argv) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + { + subdirectorial::SimpleMessage *s = new subdirectorial::SimpleMessage(); + s->set_the_integer(3); + subdirectorial::ComplexMessage c; + c.set_allocated_sm(s); + } + google::protobuf::ShutdownProtobufLibrary(); + return 0; +}