From 10d94a12b85ba19dc2bcfc6296632eaf74f5d4f0 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 16 Feb 2021 10:48:20 -0800 Subject: [PATCH] Environment: Fix passing envrionment variables CPPFLAGS and CFLAGS Or other language flags that use CPPFLAGS (like CXXFLAGS). The problem here is actually rather simple, `dict.setdefault()` doesn't work like I thought it did, I thought it created a weak entry, but it actually is equivalent to: ```python if k not in dict: dict[k] = v ``` Instead we'll use an intermediate dictionary (a default dictionary actually, since that makes things a little cleaner) and then add the keys from that dict to self.options as applicable. Test case written by Jussi, Fix by Dylan Co-authored-by: Jussi Pakkanen Fixes: #8361 Fixes: #8345 --- mesonbuild/environment.py | 19 ++++++++++++------- mesonbuild/mesonlib/universal.py | 1 - run_unittests.py | 8 ++++++++ .../unit/90 multiple envvars/meson.build | 4 ++++ test cases/unit/90 multiple envvars/prog.c | 18 ++++++++++++++++++ test cases/unit/90 multiple envvars/prog.cpp | 18 ++++++++++++++++++ 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 test cases/unit/90 multiple envvars/meson.build create mode 100644 test cases/unit/90 multiple envvars/prog.c create mode 100644 test cases/unit/90 multiple envvars/prog.cpp diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index b12728b39472..7e9118ea9208 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -788,6 +788,8 @@ def _set_default_options_from_env(self) -> None: ] ) + env_opts: T.DefaultDict[OptionKey, T.List[str]] = collections.defaultdict(list) + for (evar, keyname), for_machine in itertools.product(opts, MachineChoice): p_env = _get_env_var(for_machine, self.is_cross_build(), evar) if p_env is not None: @@ -805,7 +807,7 @@ def _set_default_options_from_env(self) -> None: p_list = list(mesonlib.OrderedSet(p_env.split(':'))) else: p_list = split_args(p_env) - p_list = [e for e in p_list if e] # filter out any empty eelemnts + p_list = [e for e in p_list if e] # filter out any empty elements # Take env vars only on first invocation, if the env changes when # reconfiguring it gets ignored. @@ -816,18 +818,21 @@ def _set_default_options_from_env(self) -> None: key = OptionKey('link_args', machine=for_machine, lang='c') # needs a language to initialize properly for lang in compilers.compilers.LANGUAGES_USING_LDFLAGS: key = key.evolve(lang=lang) - v = mesonlib.listify(self.options.get(key, [])) - self.options.setdefault(key, v + p_list) + env_opts[key].extend(p_list) elif keyname == 'cppflags': key = OptionKey('args', machine=for_machine, lang='c') for lang in compilers.compilers.LANGUAGES_USING_CPPFLAGS: key = key.evolve(lang=lang) - v = mesonlib.listify(self.options.get(key, [])) - self.options.setdefault(key, v + p_list) + env_opts[key].extend(p_list) else: key = OptionKey.from_string(keyname).evolve(machine=for_machine) - v = mesonlib.listify(self.options.get(key, [])) - self.options.setdefault(key, v + p_list) + env_opts[key].extend(p_list) + + # Only store options that are not already in self.options, + # otherwise we'd override the machine files + for k, v in env_opts.items(): + if k not in self.options: + self.options[k] = v def _set_default_binaries_from_env(self) -> None: """Set default binaries from the environment. diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 7deb24a11e08..19e329d56d4c 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -2023,7 +2023,6 @@ def from_string(cls, raw: str) -> 'OptionKey': This takes strings like `mysubproject:build.myoption` and Creates an OptionKey out of them. """ - try: subproject, raw2 = raw.split(':') except ValueError: diff --git a/run_unittests.py b/run_unittests.py index 0bd6d4bf9c61..3aa1cb31f58e 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -5476,6 +5476,14 @@ def test_version_file(self): projinfo = self.introspect('--projectinfo') self.assertEqual(projinfo['version'], '1.0.0') + def test_cflags_cppflags(self): + envs = {'CPPFLAGS': '-DCPPFLAG', + 'CFLAGS': '-DCFLAG', + 'CXXFLAGS': '-DCXXFLAG'} + srcdir = os.path.join(self.unit_test_dir, '90 multiple envvars') + self.init(srcdir, override_envvars=envs) + self.build() + class FailureTests(BasePlatformTests): ''' diff --git a/test cases/unit/90 multiple envvars/meson.build b/test cases/unit/90 multiple envvars/meson.build new file mode 100644 index 000000000000..ac57611c8cb8 --- /dev/null +++ b/test cases/unit/90 multiple envvars/meson.build @@ -0,0 +1,4 @@ +project('multienv', 'c', 'cpp') + +executable('cexe', 'prog.c') +executable('cppexe', 'prog.cpp') diff --git a/test cases/unit/90 multiple envvars/prog.c b/test cases/unit/90 multiple envvars/prog.c new file mode 100644 index 000000000000..38d3c123bcf0 --- /dev/null +++ b/test cases/unit/90 multiple envvars/prog.c @@ -0,0 +1,18 @@ +#include + +#ifndef CPPFLAG +#error CPPFLAG not set +#endif + +#ifndef CFLAG +#error CFLAGS not set +#endif + +#ifdef CXXFLAG +#error CXXFLAG is set +#endif + +int main(int argc, char **argv) { + printf("%d %s\n", argc, argv[0]); + return 0; +} diff --git a/test cases/unit/90 multiple envvars/prog.cpp b/test cases/unit/90 multiple envvars/prog.cpp new file mode 100644 index 000000000000..61ccf3a458a4 --- /dev/null +++ b/test cases/unit/90 multiple envvars/prog.cpp @@ -0,0 +1,18 @@ +#include + +#ifndef CPPFLAG +#error CPPFLAG not set +#endif + +#ifdef CFLAG +#error CFLAG is set +#endif + +#ifndef CXXFLAG +#error CXXFLAG not set +#endif + +int main(int argc, char **argv) { + printf("%d %s\n", argc, argv[0]); + return 0; +}