Repository: mesonbuild/meson

Issue:
The extract_objects() method warns unconditionally
Starting from 0.36.0, this code:

    solver_objects = libemeus.extract_objects(solver_sources)

where `solver_sources` is an array with a subset of sources used by a library, generates a warning when running Meson:

````
Single object files can not be extracted in Unity builds. You can only extract all the object files at once.
````

This happens regardless of whether I pass `--unity` or not, and even if I wrap the `extract_objects()` call under a:

    if not meson.is_unity()
    ...
    endif

This does not happen with Meson < 0.36.0, and being able to extract a subset of objects is useful when writing tests for a specific internal API.


Discussion:
It causes a warning, and not an exception? Could you show us the code that's causing this? There's a test for exactly this, so it should work: https://github.com/mesonbuild/meson/blob/master/test%20cases/common/25%20object%20extraction/meson.build#L9
Sorry, I wasn't being accurate. Running meson I get:

```
Meson encountered an error in file src/meson.build, line 96, column 0:
Single object files can not be extracted in Unity builds. You can only extract all the object files at once.
```

and the execution terminates.

The code that causes the issue is in Emeus, prior to this commit: https://github.com/ebassi/emeus/commit/868265038e5461dff8ccf23ae377c3751d857a8a

I had to switch to an internal static library in order to keep working on it.
I see the problem now. The test case is faulty. I'll fix this and we should get the fix into a 0.36.1 release.

Resolving patch:
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -279,13 +279,9 @@ def determine_ext_objs(self, extobj, proj_dir_to_build_root):
         result = []
         targetdir = self.get_target_private_dir(extobj.target)
         # With unity builds, there's just one object that contains all the
-        # sources, so if we want all the objects, just return that.
+        # sources, and we only support extracting all the objects in this mode,
+        # so just return that.
         if self.environment.coredata.get_builtin_option('unity'):
-            if not extobj.unity_compatible:
-                # This should never happen
-                msg = 'BUG: Meson must not allow extracting single objects ' \
-                      'in Unity builds'
-                raise AssertionError(msg)
             comp = get_compiler_for_source(extobj.target.compilers.values(),
                                            extobj.srclist[0])
             # The unity object name uses the full absolute path of the source file
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -198,10 +198,11 @@ class ExtractedObjects():
     '''
     Holds a list of sources for which the objects must be extracted
     '''
-    def __init__(self, target, srclist):
+    def __init__(self, target, srclist, is_unity):
         self.target = target
         self.srclist = srclist
-        self.check_unity_compatible()
+        if is_unity:
+            self.check_unity_compatible()
 
     def check_unity_compatible(self):
         # Figure out if the extracted object list is compatible with a Unity
@@ -211,11 +212,9 @@ def check_unity_compatible(self):
         # from each unified source file.
         # If the list of sources for which we want objects is the same as the
         # list of sources that go into each unified build, we're good.
-        self.unity_compatible = False
         srclist_set = set(self.srclist)
         # Objects for all the sources are required, so we're compatible
         if srclist_set == set(self.target.sources):
-            self.unity_compatible = True
             return
         # Check if the srclist is a subset (of the target's sources) that is
         # going to form a unified source file and a single object
@@ -223,7 +222,6 @@ def check_unity_compatible(self):
                                           self.target.sources)
         for srcs in compsrcs.values():
             if srclist_set == set(srcs):
-                self.unity_compatible = True
                 return
         msg = 'Single object files can not be extracted in Unity builds. ' \
               'You can only extract all the object files at once.'
@@ -273,6 +271,7 @@ def __init__(self, name, subdir, subproject, is_cross, sources, objects, environ
         self.subdir = subdir
         self.subproject = subproject # Can not be calculated from subdir as subproject dirname can be changed per project.
         self.is_cross = is_cross
+        self.is_unity = environment.coredata.get_builtin_option('unity')
         self.environment = environment
         self.sources = []
         self.compilers = {}
@@ -458,10 +457,10 @@ def extract_objects(self, srclist):
             if src not in self.sources:
                 raise MesonException('Tried to extract unknown source %s.' % src)
             obj_src.append(src)
-        return ExtractedObjects(self, obj_src)
+        return ExtractedObjects(self, obj_src, self.is_unity)
 
     def extract_all_objects(self):
-        return ExtractedObjects(self, self.sources)
+        return ExtractedObjects(self, self.sources, self.is_unity)
 
     def get_all_link_deps(self):
         return self.get_transitive_link_deps()


Test patch:
diff --git a/test cases/common/25 object extraction/lib2.c b/test cases/common/25 object extraction/lib2.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/25 object extraction/lib2.c	
@@ -0,0 +1,3 @@
+int retval() {
+  return 43;
+}
diff --git a/test cases/common/25 object extraction/meson.build b/test cases/common/25 object extraction/meson.build
--- a/test cases/common/25 object extraction/meson.build	
+++ b/test cases/common/25 object extraction/meson.build	
@@ -4,7 +4,7 @@ if meson.is_unity()
   message('Skipping extraction test because this is a Unity build.')
 else
   lib1 = shared_library('somelib', 'src/lib.c')
-  lib2 = shared_library('somelib2', 'lib.c')
+  lib2 = shared_library('somelib2', 'lib.c', 'lib2.c')
 
   obj1 = lib1.extract_objects('src/lib.c')
   obj2 = lib2.extract_objects(['lib.c'])