aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2024-04-09 21:43:33 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2024-07-17 19:14:45 +0300
commitcb5736304b5dcd2d15d0b422149b6c3926467889 (patch)
tree90f6d093ed8fa3c49caf905b381dfc5a8d97ad50
parentb204454731e5edb0c44fc64d8a8ae684fddc1b15 (diff)
downloadmeson-cb5736304b5dcd2d15d0b422149b6c3926467889.zip
meson-cb5736304b5dcd2d15d0b422149b6c3926467889.tar.gz
meson-cb5736304b5dcd2d15d0b422149b6c3926467889.tar.bz2
Can specify per-subproject options from the command line.
-rw-r--r--mesonbuild/coredata.py53
-rw-r--r--mesonbuild/mconf.py28
-rw-r--r--test cases/unit/123 persp options/meson.build22
-rw-r--r--test cases/unit/123 persp options/meson.options1
-rw-r--r--test cases/unit/123 persp options/subprojects/sub1/meson.build20
-rw-r--r--test cases/unit/123 persp options/subprojects/sub1/meson.options1
-rw-r--r--test cases/unit/123 persp options/subprojects/sub1/sub1.c6
-rw-r--r--test cases/unit/123 persp options/subprojects/sub2/meson.build19
-rw-r--r--test cases/unit/123 persp options/subprojects/sub2/meson.options1
-rw-r--r--test cases/unit/123 persp options/subprojects/sub2/sub2.c6
-rw-r--r--test cases/unit/123 persp options/toplevel.c6
-rw-r--r--unittests/linuxliketests.py42
12 files changed, 197 insertions, 8 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index b35081a..458e014 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -260,6 +260,7 @@ class CoreData:
self.target_guids = {}
self.version = version
self.optstore = options.OptionStore()
+ self.sp_option_overrides: 'MutableKeyedOptionDictType' = {}
self.cross_files = self.__load_config_files(cmd_options, scratch_dir, 'cross')
self.compilers: PerMachine[T.Dict[str, Compiler]] = PerMachine(OrderedDict(), OrderedDict())
@@ -441,17 +442,20 @@ class CoreData:
'Default project to execute in Visual Studio',
''))
+ def get_and_clean(self, key, store):
+ v = store[key].value
+ if key.name == 'wrap_mode':
+ return WrapMode[v]
+ return v
+
def get_option(self, key: OptionKey) -> T.Union[T.List[str], str, int, bool]:
try:
- v = self.optstore.get_value(key)
- return v
+ return self.get_and_clean(key, self.sp_option_overrides)
except KeyError:
pass
try:
- v = self.optstore.get_value_object(key.as_root())
- if v.yielding:
- return v.value
+ return self.get_and_clean(key.as_root(), self.options)
except KeyError:
pass
@@ -697,6 +701,45 @@ class CoreData:
return dirty
+ def can_set_per_sb(self, keystr):
+ return True
+
+ def create_sp_options(self, A) -> bool:
+ if A is None:
+ return False
+ import copy
+ dirty = False
+ for entry in A:
+ keystr, valstr = entry.split('=', 1)
+ if ':' not in keystr:
+ raise MesonException(f'Option to add override has no subproject: {entry}')
+ if not self.can_set_per_sb(keystr):
+ raise MesonException(f'Option {keystr} can not be set per subproject.')
+ key = OptionKey.from_string(keystr)
+ if key in self.sp_option_overrides:
+ raise MesonException(f'Override {keystr} already exists.')
+ original_key = key.evolve(subproject='')
+ if original_key not in self.options:
+ raise MesonException('Tried to override a nonexisting key.')
+ new_opt = copy.deepcopy(self.options[original_key])
+ new_opt.set_value(valstr)
+ self.sp_option_overrides[key] = new_opt
+ dirty = True
+ return dirty
+
+ def remove_sp_options(self, U) -> bool:
+ dirty = False
+ if U is None:
+ return False
+ for entry in U:
+ key = OptionKey.from_string(entry)
+ if key in self.options:
+ del self.options[key]
+ dirty = True
+ else:
+ pass # Deleting a non-existing key ok, I guess?
+ return dirty
+
def set_default_options(self, default_options: T.MutableMapping[OptionKey, str], subproject: str, env: 'Environment') -> None:
from .compilers import base_options
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
index 6cb64e1..62888aa 100644
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -48,6 +48,10 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None:
help='Clear cached state (e.g. found dependencies)')
parser.add_argument('--no-pager', action='store_false', dest='pager',
help='Do not redirect output to a pager')
+ parser.add_argument('-A', action='append', dest='A',
+ help='Add a subproject option.')
+ parser.add_argument('-U', action='append', dest='U',
+ help='Remove a subproject option.')
def stringify(val: T.Any) -> str:
if isinstance(val, bool):
@@ -335,8 +339,24 @@ class Conf:
for m in mismatching:
mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}')
+def has_option_flags(options):
+ if options.cmd_line_options:
+ return True
+ if options.A:
+ return True
+ if options.D:
+ return True
+ return False
+
+def is_print_only(options):
+ if has_option_flags(options):
+ return False
+ if options.clearcache:
+ return False
+ return True
+
def run_impl(options: CMDOptions, builddir: str) -> int:
- print_only = not options.cmd_line_options and not options.clearcache
+ print_only = is_print_only(options)
c = None
try:
c = Conf(builddir)
@@ -347,8 +367,10 @@ def run_impl(options: CMDOptions, builddir: str) -> int:
return 0
save = False
- if options.cmd_line_options:
- save = c.set_options(options.cmd_line_options)
+ if has_option_flags(options):
+ save |= c.set_options(options.cmd_line_options)
+ save |= c.coredata.create_sp_options(options.A)
+ save |= c.coredata.remove_sp_options(options.U)
coredata.update_cmd_line_file(builddir, options)
if options.clearcache:
c.clear_cache()
diff --git a/test cases/unit/123 persp options/meson.build b/test cases/unit/123 persp options/meson.build
new file mode 100644
index 0000000..b751f1f
--- /dev/null
+++ b/test cases/unit/123 persp options/meson.build
@@ -0,0 +1,22 @@
+project('toplevel', 'c')
+
+round = get_option('round')
+opt = get_option('optimization')
+if round == 1
+ assert(opt == '1')
+elif round == 2
+ assert(opt == '1')
+elif round == 3
+ assert(opt == '3')
+elif round == 4
+ assert(opt == '3')
+elif round == 5
+ assert(opt == '3')
+else
+ assert(false, 'Invalid round number')
+endif
+
+executable('toplevel', 'toplevel.c')
+
+subproject('sub1')
+subproject('sub2')
diff --git a/test cases/unit/123 persp options/meson.options b/test cases/unit/123 persp options/meson.options
new file mode 100644
index 0000000..2bfd08d
--- /dev/null
+++ b/test cases/unit/123 persp options/meson.options
@@ -0,0 +1 @@
+option('round', type: 'integer', value: 1, description: 'The test round.')
diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.build b/test cases/unit/123 persp options/subprojects/sub1/meson.build
new file mode 100644
index 0000000..47cd5f4
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub1/meson.build
@@ -0,0 +1,20 @@
+project('sub1', 'c')
+
+round = get_option('round')
+opt = get_option('optimization')
+if round == 1
+ assert(opt == '1')
+elif round == 2
+ assert(opt == '1')
+elif round == 3
+ assert(opt == '1')
+elif round == 4
+ assert(opt == '1')
+elif round == 5
+ assert(opt == '2')
+else
+ assert(false, 'Invalid round number')
+endif
+
+
+executable('sub1', 'sub1.c')
diff --git a/test cases/unit/123 persp options/subprojects/sub1/meson.options b/test cases/unit/123 persp options/subprojects/sub1/meson.options
new file mode 100644
index 0000000..ba5661a
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub1/meson.options
@@ -0,0 +1 @@
+option('round', type: 'integer', value: 1, description: 'The test round.', yield: true)
diff --git a/test cases/unit/123 persp options/subprojects/sub1/sub1.c b/test cases/unit/123 persp options/subprojects/sub1/sub1.c
new file mode 100644
index 0000000..4e4b873
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub1/sub1.c
@@ -0,0 +1,6 @@
+#include<stdio.h>
+
+int main(void) {
+ printf("This is subproject 1.\n");
+ return 0;
+}
diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.build b/test cases/unit/123 persp options/subprojects/sub2/meson.build
new file mode 100644
index 0000000..774408f
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub2/meson.build
@@ -0,0 +1,19 @@
+project('sub2', 'c')
+
+round = get_option('round')
+opt = get_option('optimization')
+if round == 1
+ assert(opt == '1')
+elif round == 2
+ assert(opt == '2')
+elif round == 3
+ assert(opt == '2')
+elif round == 4
+ assert(opt == '1')
+elif round == 5
+ assert(opt == '2')
+else
+ assert(false, 'Invalid round number')
+endif
+
+executable('sub2', 'sub2.c')
diff --git a/test cases/unit/123 persp options/subprojects/sub2/meson.options b/test cases/unit/123 persp options/subprojects/sub2/meson.options
new file mode 100644
index 0000000..ba5661a
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub2/meson.options
@@ -0,0 +1 @@
+option('round', type: 'integer', value: 1, description: 'The test round.', yield: true)
diff --git a/test cases/unit/123 persp options/subprojects/sub2/sub2.c b/test cases/unit/123 persp options/subprojects/sub2/sub2.c
new file mode 100644
index 0000000..4e4b873
--- /dev/null
+++ b/test cases/unit/123 persp options/subprojects/sub2/sub2.c
@@ -0,0 +1,6 @@
+#include<stdio.h>
+
+int main(void) {
+ printf("This is subproject 1.\n");
+ return 0;
+}
diff --git a/test cases/unit/123 persp options/toplevel.c b/test cases/unit/123 persp options/toplevel.c
new file mode 100644
index 0000000..5748d6b
--- /dev/null
+++ b/test cases/unit/123 persp options/toplevel.c
@@ -0,0 +1,6 @@
+#include<stdio.h>
+
+int main(void) {
+ printf("This is the top level project.\n");
+ return 0;
+}
diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py
index 16997e3..05799e8 100644
--- a/unittests/linuxliketests.py
+++ b/unittests/linuxliketests.py
@@ -1817,3 +1817,45 @@ class LinuxlikeTests(BasePlatformTests):
self.assertIn('build t9-e1: c_LINKER t9-e1.p/main.c.o | libt9-s1.a libt9-s2.a libt9-s3.a\n', content)
self.assertIn('build t12-e1: c_LINKER t12-e1.p/main.c.o | libt12-s1.a libt12-s2.a libt12-s3.a\n', content)
self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content)
+
+ def check_has_flag(self, compdb, src, argument):
+ for i in compdb:
+ if src in i['file']:
+ self.assertIn(argument, i['command'])
+ return
+ self.assertTrue(False, f'Source {src} not found in compdb')
+
+ def test_persp_options(self):
+ testdir = os.path.join(self.unit_test_dir, '123 persp options')
+ self.init(testdir, extra_args='-Doptimization=1')
+ compdb = self.get_compdb()
+ mainsrc = 'toplevel.c'
+ sub1src = 'sub1.c'
+ sub2src = 'sub2.c'
+ self.check_has_flag(compdb, mainsrc, '-O1')
+ self.check_has_flag(compdb, sub1src, '-O1')
+ self.check_has_flag(compdb, sub2src, '-O1')
+
+ # Set subproject option to O2
+ compdb = self.get_compdb()
+ self.check_has_flag(compdb, mainsrc, '-O1')
+ self.check_has_flag(compdb, sub1src, '-O1')
+ self.check_has_flag(compdb, sub2src, '-O2')
+
+ # Set top level option to O3
+ compdb = self.get_compdb()
+ self.check_has_flag(compdb, mainsrc, '-O3')
+ self.check_has_flag(compdb, sub1src, '-O1')
+ self.check_has_flag(compdb, sub2src, '-O2')
+
+ # UnsetSet subproject
+ compdb = self.get_compdb()
+ self.check_has_flag(compdb, mainsrc, '-O3')
+ self.check_has_flag(compdb, sub1src, '-O1')
+ self.check_has_flag(compdb, sub2src, '-O1')
+
+ # Set global value
+ compdb = self.get_compdb()
+ self.check_has_flag(compdb, mainsrc, '-O3')
+ self.check_has_flag(compdb, sub1src, '-O2')
+ self.check_has_flag(compdb, sub2src, '-O2')