diff --git a/docs/markdown/Kconfig-module.md b/docs/markdown/Kconfig-module.md new file mode 100644 index 000000000000..c361b5b65e84 --- /dev/null +++ b/docs/markdown/Kconfig-module.md @@ -0,0 +1,52 @@ +--- +short-description: Unstable kconfig module +authors: + - name: Mark Schulte, Paolo Bonzini + years: [2017, 2019] + has-copyright: false +... + +# Unstable kconfig module + +This module parses Kconfig output files to allow use of kconfig +configurations in meson projects. + +**Note**: this does not provide kconfig frontend tooling to generate a +configuration. You still need something such as kconfig frontends (see +link below) to parse your Kconfig files, and then (after you've +choosen the configuration options), output a ".config" file. + + [kconfig-frontends]: http://ymorin.is-a-geek.org/projects/kconfig-frontends + +## Usage + +The module may be imported as follows: + +``` meson +kconfig = import('unstable-kconfig') +``` + +The following functions will then be available as methods on the object +with the name `kconfig`. You can, of course, replace the name +`kconfig` with anything else. + +### kconfig.load() + +This function loads a kconfig output file and returns a dictionary object. + +`kconfig.load()` makes no attempt at parsing the values in the +file. Therefore, true boolean values will be represented as the string "y" +and integer values will have to be converted with `.to_int()`. + +Kconfig frontends usually have ".config" as the default name for the +configuration file. However, placing the configuration file in the source +directory limits the user to one configuration per source directory. +In order to allow separate configurations for each build directory, as is +the Meson standard, `meson.build` should not hardcode ".config" as the +argument to `kconfig.load()`, and should instead make the argument to +`kconfig.load()` a [project build option](Build-options.md). + +* The first (and only) argument is the path to the configuration file to + load (usually ".config"). + +**Returns**: a [dictionary object](Reference-manual.md#dictionary-object). diff --git a/docs/markdown/snippets/kconfig.md b/docs/markdown/snippets/kconfig.md new file mode 100644 index 000000000000..d4d5c9bdfb77 --- /dev/null +++ b/docs/markdown/snippets/kconfig.md @@ -0,0 +1,5 @@ +## New module to parse kconfig output files + +The new module `unstable-kconfig` adds the ability to parse and use kconfig output +files from `meson.build`. + diff --git a/docs/sitemap.txt b/docs/sitemap.txt index bea2a316c830..2e6eb6847116 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -45,6 +45,7 @@ index.md Simd-module.md Windows-module.md Cuda-module.md + Kconfig-module.md Java.md Vala.md D.md diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 3c3cfae08707..ca4414a2bde3 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2119,6 +2119,9 @@ def build_func_dict(self): def holderify(self, item): if isinstance(item, list): return [self.holderify(x) for x in item] + if isinstance(item, dict): + return {k: self.holderify(v) for k, v in item.items()} + if isinstance(item, build.CustomTarget): return CustomTargetHolder(item, self) elif isinstance(item, (int, str, bool)) or item is None: diff --git a/mesonbuild/modules/unstable_kconfig.py b/mesonbuild/modules/unstable_kconfig.py new file mode 100644 index 000000000000..1639eed8e08b --- /dev/null +++ b/mesonbuild/modules/unstable_kconfig.py @@ -0,0 +1,72 @@ +# Copyright 2017, 2019 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import ExtensionModule + +from .. import mesonlib +from ..mesonlib import typeslistify +from ..interpreterbase import FeatureNew, noKwargs +from ..interpreter import InvalidCode + +import os + +class KconfigModule(ExtensionModule): + + @FeatureNew('Kconfig Module', '0.51.0') + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.snippets.add('load') + + def _load_file(self, path_to_config): + result = dict() + try: + with open(path_to_config) as f: + for line in f: + if '#' in line: + comment_idx = line.index('#') + line = line[:comment_idx] + line = line.strip() + try: + name, val = line.split('=', 1) + except ValueError: + continue + result[name.strip()] = val.strip() + except IOError as e: + raise mesonlib.MesonException('Failed to load {}: {}'.format(path_to_config, e)) + + return result + + @noKwargs + def load(self, interpreter, state, args, kwargs): + sources = typeslistify(args, (str, mesonlib.File)) + if len(sources) != 1: + raise InvalidCode('load takes only one file input.') + + s = sources[0] + if isinstance(s, mesonlib.File): + # kconfig input is processed at "meson setup" time, not during + # the build, so it cannot reside in the build directory. + if s.is_built: + raise InvalidCode('kconfig input must be a source file.') + s = s.relative_name() + + s = os.path.join(interpreter.environment.source_dir, s) + if s not in interpreter.build_def_files: + interpreter.build_def_files.append(s) + + return self._load_file(s) + + +def initialize(*args, **kwargs): + return KconfigModule(*args, **kwargs) diff --git a/run_project_tests.py b/run_project_tests.py index 89f11d382f9f..00fca6cdaf27 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -550,6 +550,7 @@ def detect_tests_to_run(): ('failing-meson', 'failing', False), ('failing-build', 'failing build', False), ('failing-test', 'failing test', False), + ('kconfig', 'kconfig', False), ('platform-osx', 'osx', not mesonlib.is_osx()), ('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()), diff --git a/test cases/kconfig/1 basic/.config b/test cases/kconfig/1 basic/.config new file mode 100644 index 000000000000..071d1854d433 --- /dev/null +++ b/test cases/kconfig/1 basic/.config @@ -0,0 +1,3 @@ +CONFIG_VAL1=y +# CONFIG_VAL2 is not set +CONFIG_VAL_VAL=4 diff --git a/test cases/kconfig/1 basic/meson.build b/test cases/kconfig/1 basic/meson.build new file mode 100644 index 000000000000..5dc8d194a704 --- /dev/null +++ b/test cases/kconfig/1 basic/meson.build @@ -0,0 +1,16 @@ +project('kconfig basic test') + +k = import('unstable-kconfig') +conf = k.load('.config') + +if not conf.has_key('CONFIG_VAL1') + error('Expected CONFIG_VAL1 to be set, but it wasn\'t') +endif + +if conf.has_key('CONFIG_VAL2') + error('Expected CONFIG_VAL2 not be set, but it was') +endif + +if conf.get('CONFIG_VAL_VAL').to_int() != 4 + error('Expected CONFIG_VAL_VAL to be 4') +endif diff --git a/test cases/kconfig/2 subdir/.config b/test cases/kconfig/2 subdir/.config new file mode 100644 index 000000000000..0599d4616da5 --- /dev/null +++ b/test cases/kconfig/2 subdir/.config @@ -0,0 +1,2 @@ +CONFIG_IS_SET=y +# CONFIG_NOT_IS_SET is not set diff --git a/test cases/kconfig/2 subdir/dir/meson.build b/test cases/kconfig/2 subdir/dir/meson.build new file mode 100644 index 000000000000..12f150297004 --- /dev/null +++ b/test cases/kconfig/2 subdir/dir/meson.build @@ -0,0 +1,13 @@ + +k = import('unstable-kconfig') + +conf = k.load(meson.source_root() / '.config') + +if not conf.has_key('CONFIG_IS_SET') + error('Expected CONFIG_IS_SET to be set, but it wasn\'t') +endif + +if conf.has_key('CONFIG_NOT_IS_SET') + error('Expected CONFIG_NOT_IS_SET not be set, but it was') +endif + diff --git a/test cases/kconfig/2 subdir/meson.build b/test cases/kconfig/2 subdir/meson.build new file mode 100644 index 000000000000..1245b1802441 --- /dev/null +++ b/test cases/kconfig/2 subdir/meson.build @@ -0,0 +1,4 @@ +project('kconfig subdir test') + +# Test into sub directory +subdir('dir') diff --git a/test cases/kconfig/3 load_config files/dir/config b/test cases/kconfig/3 load_config files/dir/config new file mode 100644 index 000000000000..0599d4616da5 --- /dev/null +++ b/test cases/kconfig/3 load_config files/dir/config @@ -0,0 +1,2 @@ +CONFIG_IS_SET=y +# CONFIG_NOT_IS_SET is not set diff --git a/test cases/kconfig/3 load_config files/dir/meson.build b/test cases/kconfig/3 load_config files/dir/meson.build new file mode 100644 index 000000000000..d7b8d4477936 --- /dev/null +++ b/test cases/kconfig/3 load_config files/dir/meson.build @@ -0,0 +1,13 @@ + +k = import('unstable-kconfig') + +conf = k.load(files('config')) + +if not conf.has_key('CONFIG_IS_SET') + error('Expected CONFIG_IS_SET to be set, but it wasn\'t') +endif + +if conf.has_key('CONFIG_NOT_IS_SET') + error('Expected CONFIG_NOT_IS_SET not be set, but it was') +endif + diff --git a/test cases/kconfig/3 load_config files/meson.build b/test cases/kconfig/3 load_config files/meson.build new file mode 100644 index 000000000000..1245b1802441 --- /dev/null +++ b/test cases/kconfig/3 load_config files/meson.build @@ -0,0 +1,4 @@ +project('kconfig subdir test') + +# Test into sub directory +subdir('dir') diff --git a/test cases/kconfig/4 load_config builddir/config b/test cases/kconfig/4 load_config builddir/config new file mode 100644 index 000000000000..0599d4616da5 --- /dev/null +++ b/test cases/kconfig/4 load_config builddir/config @@ -0,0 +1,2 @@ +CONFIG_IS_SET=y +# CONFIG_NOT_IS_SET is not set diff --git a/test cases/kconfig/4 load_config builddir/meson.build b/test cases/kconfig/4 load_config builddir/meson.build new file mode 100644 index 000000000000..93136ba57bdb --- /dev/null +++ b/test cases/kconfig/4 load_config builddir/meson.build @@ -0,0 +1,14 @@ +project('kconfig builddir test') + +k = import('unstable-kconfig') + +configure_file(input: 'config', output: 'out-config', copy: true) +conf = k.load(meson.build_root() / 'out-config') + +if not conf.has_key('CONFIG_IS_SET') + error('Expected CONFIG_IS_SET to be set, but it wasn\'t') +endif + +if conf.has_key('CONFIG_NOT_IS_SET') + error('Expected CONFIG_NOT_IS_SET not be set, but it was') +endif