Repository: mesonbuild/meson

Issue:
meson 1.8 ignores buildtype defined in native-file
I ran into an issue with one of my Conan recipes, but I was able to narrow it down and reproduce it directly with Meson.

> meson.build

```
project('example', 'c',  default_options: ['buildtype=debugoptimized'])
message('Buildtype: ' + get_option('buildtype'))
```


> toolchain.ini

```[built-in options]
buildtype = 'debug'
```

When I now run `meson --native-file=toolchain.ini builddir`
Meson 1.8.0 outputs the message
> Message: Buildtype: debugoptimized

In contrast, Meson 1.7.2 shows
> Message: Buildtype: release

From my perspective, the output in version 1.8.0 is unexpected, and it seems possible that this change in behavior was not intended.


Discussion:
Can you test with Meson's master branch? (I doubt it's fixed but at the same time there were many changes so maybe we're lucky)
I just tested master - same behavior as 1.8.0 _(Message: Buildtype: debugoptimized)_



Patch:
diff --git a/mesonbuild/options.py b/mesonbuild/options.py
index ac90b2292b91..cd7d2eb6d1d3 100644
--- a/mesonbuild/options.py
+++ b/mesonbuild/options.py
@@ -1289,21 +1289,6 @@ def initialize_from_top_level_project_call(self,
         (project_default_options, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in,
                                                                                                      cmd_line_options_in,
                                                                                                      machine_file_options_in)
-        for key, valstr in machine_file_options.items():
-            # Due to backwards compatibility we ignore all build-machine options
-            # when building natively.
-            if not self.is_cross and key.is_for_build():
-                continue
-            if key.subproject:
-                self.augments[key] = valstr
-            elif key in self.options:
-                self.set_option(key, valstr, first_invocation)
-            else:
-                proj_key = key.as_root()
-                if proj_key in self.options:
-                    self.set_option(proj_key, valstr, first_invocation)
-                else:
-                    self.pending_options[key] = valstr
         for keystr, valstr in project_default_options.items():
             # Ths is complicated by the fact that a string can have two meanings:
             #
@@ -1338,6 +1323,21 @@ def initialize_from_top_level_project_call(self,
                     self.set_option(proj_key, valstr)
                 else:
                     self.pending_options[key] = valstr
+        for key, valstr in machine_file_options.items():
+            # Due to backwards compatibility we ignore all build-machine options
+            # when building natively.
+            if not self.is_cross and key.is_for_build():
+                continue
+            if key.subproject:
+                self.augments[key] = valstr
+            elif key in self.options:
+                self.set_option(key, valstr, first_invocation)
+            else:
+                proj_key = key.as_root()
+                if proj_key in self.options:
+                    self.set_option(proj_key, valstr, first_invocation)
+                else:
+                    self.pending_options[key] = valstr
         for keystr, valstr in cmd_line_options.items():
             if isinstance(keystr, str):
                 key = OptionKey.from_string(keystr)


Test patch:
diff --git a/unittests/optiontests.py b/unittests/optiontests.py
index 0bdd7dc4550a..5758a2d5c8ac 100644
--- a/unittests/optiontests.py
+++ b/unittests/optiontests.py
@@ -35,6 +35,22 @@ def test_toplevel_project(self):
         optstore.initialize_from_top_level_project_call({OptionKey('someoption'): new_value}, {}, {})
         self.assertEqual(optstore.get_value_for(k), new_value)
 
+    def test_machine_vs_project(self):
+        optstore = OptionStore(False)
+        name = 'backend'
+        default_value = 'ninja'
+        proj_value = 'xcode'
+        mfile_value = 'vs2010'
+        k = OptionKey(name)
+        prefix = UserStringOption('prefix', 'This is needed by OptionStore', '/usr')
+        optstore.add_system_option('prefix', prefix)
+        vo = UserStringOption(k.name, 'You know what this is', default_value)
+        optstore.add_system_option(k.name, vo)
+        self.assertEqual(optstore.get_value_for(k), default_value)
+        optstore.initialize_from_top_level_project_call({OptionKey(name): proj_value}, {},
+                                                        {OptionKey(name): mfile_value})
+        self.assertEqual(optstore.get_value_for(k), mfile_value)
+
     def test_subproject_system_option(self):
         """Test that subproject system options get their default value from the global
            option (e.g. "sub:b_lto" can be initialized from "b_lto")."""