From 4a014d17240f50059e20ccae3e9faaa395bdbf98 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 14 Dec 2022 10:59:36 -0800 Subject: Add support for meson.options as a replacement for meson_options.txt We will still try to load `meson_options.txt` if `meson.options` doesn't exist. Because there are some advantages to using `meson.options` even with older versions of meson (such as better text editor handling) we will not warn about the existence of a `meson.options` file if a `meson_options.txt` file or symlink also exists. The name `meson.options` was picked instead of alternative proposals, such as `meson_options.build` for a couple of reasons: 1. meson.options is shorter 2. While the syntax is the same, only the `option()` function may be called in meson.options, while, it may not be called in meson.build 3. While the two files share a syntax and elementary types (strings, arrays, etc), they have different purposes: `meson.build` declares build targets, `meson.options` declares options. This is similar to the difference between C's `.c` and `.h` extensions. As an implementation detail `Interpreter.option_file` has been removed, as it is used exactly once, in the `project()` call to read the options, and we can just calculate it there and not store it. Fixes: #11176 --- data/syntax-highlighting/vim/ftdetect/meson.vim | 1 + docs/markdown/snippets/meson_options.md | 7 ++++++ docs/yaml/functions/dependency.yaml | 2 +- docs/yaml/functions/subproject.yaml | 4 ++-- man/meson.1 | 2 +- mesonbuild/ast/introspection.py | 8 ++++--- mesonbuild/backend/xcodebackend.py | 1 + mesonbuild/interpreter/interpreter.py | 28 +++++++++++++++++----- mesonbuild/mintro.py | 8 +++---- test cases/warning/9 meson.options/meson.build | 3 +++ test cases/warning/9 meson.options/meson.options | 1 + .../subprojects/no-warn/meson.build | 1 + .../subprojects/no-warn/meson.options | 1 + .../subprojects/no-warn/meson_options.txt | 1 + test cases/warning/9 meson.options/test.json | 7 ++++++ unittests/failuretests.py | 4 +++- 16 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 docs/markdown/snippets/meson_options.md create mode 100644 test cases/warning/9 meson.options/meson.build create mode 100644 test cases/warning/9 meson.options/meson.options create mode 100644 test cases/warning/9 meson.options/subprojects/no-warn/meson.build create mode 100644 test cases/warning/9 meson.options/subprojects/no-warn/meson.options create mode 120000 test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt create mode 100644 test cases/warning/9 meson.options/test.json diff --git a/data/syntax-highlighting/vim/ftdetect/meson.vim b/data/syntax-highlighting/vim/ftdetect/meson.vim index 3233c58..28e3a29 100644 --- a/data/syntax-highlighting/vim/ftdetect/meson.vim +++ b/data/syntax-highlighting/vim/ftdetect/meson.vim @@ -1,3 +1,4 @@ au BufNewFile,BufRead meson.build set filetype=meson +au BufNewFile,BufRead meson.options set filetype=meson au BufNewFile,BufRead meson_options.txt set filetype=meson au BufNewFile,BufRead *.wrap set filetype=dosini diff --git a/docs/markdown/snippets/meson_options.md b/docs/markdown/snippets/meson_options.md new file mode 100644 index 0000000..f9d582a --- /dev/null +++ b/docs/markdown/snippets/meson_options.md @@ -0,0 +1,7 @@ +## Support for reading options from meson.options + +Support has been added for reading options from `meson.options` instead of +`meson_options.txt`. These are equivalent, but not using the `.txt` extension +for a build file has a few advantages, chief among them many tools and text +editors expect a file with the `.txt` extension to be plain text files, not +build scripts. diff --git a/docs/yaml/functions/dependency.yaml b/docs/yaml/functions/dependency.yaml index 2d9e366..85255b9 100644 --- a/docs/yaml/functions/dependency.yaml +++ b/docs/yaml/functions/dependency.yaml @@ -82,7 +82,7 @@ kwargs: since: 0.38.0 description: | An array of default option values - that override those set in the subproject's `meson_options.txt` + that override those set in the subproject's `meson.options` (like `default_options` in [[project]], they only have effect when Meson is run for the first time, and command line arguments override any default options in build files) diff --git a/docs/yaml/functions/subproject.yaml b/docs/yaml/functions/subproject.yaml index 4d19a31..14a778d 100644 --- a/docs/yaml/functions/subproject.yaml +++ b/docs/yaml/functions/subproject.yaml @@ -9,7 +9,7 @@ description: | `${MESON_SOURCE_ROOT}/subprojects/foo`. - `default_options` *(since 0.37.0)*: an array of default option values - that override those set in the subproject's `meson_options.txt` + that override those set in the subproject's `meson.options` (like `default_options` in `project`, they only have effect when Meson is run for the first time, and command line arguments override any default options in build files). *(since 0.54.0)*: `default_library` @@ -45,7 +45,7 @@ kwargs: since: 0.37.0 description: | An array of default option values - that override those set in the subproject's `meson_options.txt` + that override those set in the subproject's `meson.options` (like `default_options` in [[project]], they only have effect when Meson is run for the first time, and command line arguments override any default options in build files). *(since 0.54.0)*: `default_library` diff --git a/man/meson.1 b/man/meson.1 index 7e7f486..5a4aa6e 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -105,7 +105,7 @@ print all top level targets (executables, libraries, etc) print the source files of the given target .TP \fB\-\-buildsystem\-files\fR -print all files that make up the build system (meson.build, meson_options.txt etc) +print all files that make up the build system (meson.build, meson.options, meson_options.txt etc) .TP \fB\-\-tests\fR print all unit tests diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 98f03fd..e811f11 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -76,7 +76,6 @@ class IntrospectionInterpreter(AstInterpreter): self.environment = env self.subproject_dir = subproject_dir self.coredata = self.environment.get_coredata() - self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') self.backend = backend self.default_options = {OptionKey('backend'): self.backend} self.project_data = {} # type: T.Dict[str, T.Any] @@ -113,9 +112,12 @@ class IntrospectionInterpreter(AstInterpreter): proj_vers = 'undefined' self.project_data = {'descriptive_name': proj_name, 'version': proj_vers} - if os.path.exists(self.option_file): + optfile = os.path.join(self.source_root, self.subdir, 'meson.options') + if not os.path.exists(optfile): + optfile = os.path.join(self.source_root, self.subdir, 'meson_options.txt') + if os.path.exists(optfile): oi = optinterpreter.OptionInterpreter(self.subproject) - oi.process(self.option_file) + oi.process(optfile) self.coredata.update_project_options(oi.options) def_opts = self.flatten_args(kwargs.get('default_options', [])) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index e4195e2..b5cf83a 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1025,6 +1025,7 @@ class XCodeBackend(backends.Backend): group_id = self.write_group_target_entry(objects_dict, target) children_array.add_item(group_id) potentials = [os.path.join(current_subdir, 'meson.build'), + os.path.join(current_subdir, 'meson.options'), os.path.join(current_subdir, 'meson_options.txt')] for bf in potentials: i = self.fileref_ids.get(bf, None) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index a033c4f..72bc8e2 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -23,8 +23,9 @@ from .. import compilers from .. import envconfig from ..wrap import wrap, WrapMode from .. import mesonlib -from ..mesonlib import (MesonBugException, HoldableObject, FileMode, MachineChoice, OptionKey, - listify, extract_as_list, has_path_sep, PerMachine) +from ..mesonlib import (MesonBugException, MesonException, HoldableObject, + FileMode, MachineChoice, OptionKey, listify, + extract_as_list, has_path_sep, PerMachine) from ..programs import ExternalProgram, NonExistingExternalProgram from ..dependencies import Dependency from ..depfile import DepFile @@ -290,7 +291,6 @@ class Interpreter(InterpreterBase, HoldableObject): # be different for dependencies provided by wrap files. self.subproject_directory_name = subdir.split(os.path.sep)[-1] self.subproject_dir = subproject_dir - self.option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') self.relaxations = relaxations or set() if not mock and ast is None: self.load_root_meson_file() @@ -1174,11 +1174,27 @@ class Interpreter(InterpreterBase, HoldableObject): if kwargs['meson_version']: self.handle_meson_version(kwargs['meson_version'], node) - if os.path.exists(self.option_file): + # Load "meson.options" before "meson_options.txt", and produce a warning if + # it is being used with an old version. I have added check that if both + # exist the warning isn't raised + option_file = os.path.join(self.source_root, self.subdir, 'meson.options') + old_option_file = os.path.join(self.source_root, self.subdir, 'meson_options.txt') + + if os.path.exists(option_file): + if os.path.exists(old_option_file): + if os.path.samefile(option_file, old_option_file): + mlog.debug("Not warning about meson.options with version minimum < 1.1 because meson_options.txt also exists") + else: + raise MesonException("meson.options and meson_options.txt both exist, but are not the same file.") + else: + FeatureNew.single_use('meson.options file', '1.1', self.subproject, 'Use meson_options.txt instead') + else: + option_file = old_option_file + if os.path.exists(option_file): oi = optinterpreter.OptionInterpreter(self.subproject) - oi.process(self.option_file) + oi.process(option_file) self.coredata.update_project_options(oi.options) - self.add_build_def_file(self.option_file) + self.add_build_def_file(option_file) # Do not set default_options on reconfigure otherwise it would override # values previously set from command line. That means that changing diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 68820d6..1842313 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -329,12 +329,12 @@ def list_buildoptions(coredata: cdata.CoreData, subprojects: T.Optional[T.List[s return optlist def find_buildsystem_files_list(src_dir: str) -> T.List[str]: + build_files = frozenset({'meson.build', 'meson.options', 'meson_options.txt'}) # I feel dirty about this. But only slightly. - filelist = [] # type: T.List[str] + filelist: T.List[str] = [] for root, _, files in os.walk(src_dir): - for f in files: - if f in {'meson.build', 'meson_options.txt'}: - filelist.append(os.path.relpath(os.path.join(root, f), src_dir)) + filelist.extend(os.path.relpath(os.path.join(root, f), src_dir) + for f in build_files.intersection(files)) return filelist def list_buildsystem_files(builddata: build.Build, interpreter: Interpreter) -> T.List[str]: diff --git a/test cases/warning/9 meson.options/meson.build b/test cases/warning/9 meson.options/meson.build new file mode 100644 index 0000000..59c3872 --- /dev/null +++ b/test cases/warning/9 meson.options/meson.build @@ -0,0 +1,3 @@ +project('options too old', meson_version : '>= 0.63') + +subproject('no-warn') diff --git a/test cases/warning/9 meson.options/meson.options b/test cases/warning/9 meson.options/meson.options new file mode 100644 index 0000000..b84ee83 --- /dev/null +++ b/test cases/warning/9 meson.options/meson.options @@ -0,0 +1 @@ +option('foo', type : 'string') diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson.build b/test cases/warning/9 meson.options/subprojects/no-warn/meson.build new file mode 100644 index 0000000..f86fbf7 --- /dev/null +++ b/test cases/warning/9 meson.options/subprojects/no-warn/meson.build @@ -0,0 +1 @@ +project('has both no warn', meson_version : '>= 0.63') diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson.options b/test cases/warning/9 meson.options/subprojects/no-warn/meson.options new file mode 100644 index 0000000..b84ee83 --- /dev/null +++ b/test cases/warning/9 meson.options/subprojects/no-warn/meson.options @@ -0,0 +1 @@ +option('foo', type : 'string') diff --git a/test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt b/test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt new file mode 120000 index 0000000..7b28df2 --- /dev/null +++ b/test cases/warning/9 meson.options/subprojects/no-warn/meson_options.txt @@ -0,0 +1 @@ +meson.options \ No newline at end of file diff --git a/test cases/warning/9 meson.options/test.json b/test cases/warning/9 meson.options/test.json new file mode 100644 index 0000000..f711924 --- /dev/null +++ b/test cases/warning/9 meson.options/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "WARNING: Project targets '>= 0.63' but uses feature introduced in '1.1': meson.options file. Use meson_options.txt instead" + } + ] +} diff --git a/unittests/failuretests.py b/unittests/failuretests.py index 54a6c58..ce58f3f 100644 --- a/unittests/failuretests.py +++ b/unittests/failuretests.py @@ -78,7 +78,9 @@ class FailureTests(BasePlatformTests): super().setUp() self.srcdir = os.path.realpath(tempfile.mkdtemp()) self.mbuild = os.path.join(self.srcdir, 'meson.build') - self.moptions = os.path.join(self.srcdir, 'meson_options.txt') + self.moptions = os.path.join(self.srcdir, 'meson.options') + if not os.path.exists(self.moptions): + self.moptions = os.path.join(self.srcdir, 'meson_options.txt') def tearDown(self): super().tearDown() -- cgit v1.1