Repository: conan-io/conan
Pull request #16344 (2024-05-27 16:09:39)

fix --build=missing:& pattern
Changelog: Fix: Allow ``--build=missing:&`` pattern to build only the consumer if missing, but not others
Docs: Omit


Patch:
diff --git a/conans/client/graph/build_mode.py b/conans/client/graph/build_mode.py
index 5ed470988..72cf2d6b0 100644
--- a/conans/client/graph/build_mode.py
+++ b/conans/client/graph/build_mode.py
@@ -87,5 +87,5 @@ class BuildMode:
 
     def should_build_missing(self, conanfile):
         for pattern in self.build_missing_patterns:
-            if ref_matches(conanfile.ref, pattern, is_consumer=False):
+            if ref_matches(conanfile.ref, pattern, is_consumer=conanfile._conan_is_consumer):
                 return True
diff --git a/conans/model/recipe_ref.py b/conans/model/recipe_ref.py
index 01168f09f..0d86ff3fa 100644
--- a/conans/model/recipe_ref.py
+++ b/conans/model/recipe_ref.py
@@ -154,7 +154,7 @@ class RecipeReference:
         if self.channel and validation_pattern.match(self.channel) is None:
             raise ConanException(f"Invalid package channel '{self.channel}'")
 
-         # Warn if they use .+ in the name/user/channel, as it can be problematic for generators
+        # Warn if they use .+ in the name/user/channel, as it can be problematic for generators
         pattern = re.compile(r'[.+]')
         if pattern.search(self.name):
             ConanOutput().warning(f"Name containing special chars is discouraged '{self.name}'")
@@ -179,8 +179,8 @@ class RecipeReference:
             no_user_channel = True
 
         condition = ((pattern == "&" and is_consumer) or
-                      fnmatch.fnmatchcase(str(self), pattern) or
-                      fnmatch.fnmatchcase(self.repr_notime(), pattern))
+                     fnmatch.fnmatchcase(str(self), pattern) or
+                     fnmatch.fnmatchcase(self.repr_notime(), pattern))
         if no_user_channel:
             condition = condition and not self.user and not self.channel
         if negate:


Test patch:
diff --git a/test/integration/command/create_test.py b/test/integration/command/create_test.py
index 7aa9e659b..4dbb2b0b9 100644
--- a/test/integration/command/create_test.py
+++ b/test/integration/command/create_test.py
@@ -318,6 +318,12 @@ def test_create_build_missing():
     c.assert_listed_binary({"dep/1.0": ("9a4eb3c8701508aa9458b1a73d0633783ecc2270", "Missing")})
     assert "ERROR: Missing prebuilt package for 'dep/1.0'" in c.out
 
+    # The & placeholder also works
+    c.run("create pkg -s os=Linux --build=missing:&", assert_error=True)
+    c.assert_listed_binary({"pkg/1.0": ("4c0c198b627f9af3e038af4da5e6b3ae205c2435", "Build")})
+    c.assert_listed_binary({"dep/1.0": ("9a4eb3c8701508aa9458b1a73d0633783ecc2270", "Missing")})
+    assert "ERROR: Missing prebuilt package for 'dep/1.0'" in c.out
+
 
 def test_create_no_user_channel():
     """ test the --build=pattern and --build=missing:pattern syntax to build missing packages
@@ -711,22 +717,22 @@ def test_defaults_in_components():
 
     # The paths are absolute and the components have defaults
     # ".+" Check that there is a path, not only "lib"
-    assert re.search(r"BINDIRS: \['.+bin'\]", str(client.out))
-    assert re.search(r"LIBDIRS: \['.+lib'\]", str(client.out))
-    assert re.search(r"INCLUDEDIRS: \['.+include'\]", str(client.out))
+    assert re.search(r"BINDIRS: \['.+bin']", client.out)
+    assert re.search(r"LIBDIRS: \['.+lib']", client.out)
+    assert re.search(r"INCLUDEDIRS: \['.+include']", client.out)
     assert "WARN: RES DIRS: []"
-    assert re.search(r"WARN: FOO LIBDIRS: \['.+lib'\]", str(client.out))
-    assert re.search(r"WARN: FOO INCLUDEDIRS: \['.+include'\]", str(client.out))
+    assert re.search(r"WARN: FOO LIBDIRS: \['.+lib']", client.out)
+    assert re.search(r"WARN: FOO INCLUDEDIRS: \['.+include']", client.out)
     assert "WARN: FOO RESDIRS: []" in client.out
 
     # The paths are absolute and the components have defaults
     # ".+" Check that there is a path, not only "lib"
-    assert re.search("BINDIRS: \['.+bin'\]", str(client.out))
-    assert re.search("LIBDIRS: \['.+lib'\]", str(client.out))
-    assert re.search("INCLUDEDIRS: \['.+include'\]", str(client.out))
+    assert re.search(r"BINDIRS: \['.+bin']", client.out)
+    assert re.search(r"LIBDIRS: \['.+lib']", client.out)
+    assert re.search(r"INCLUDEDIRS: \['.+include']", client.out)
     assert "WARN: RES DIRS: []"
-    assert bool(re.search("WARN: FOO LIBDIRS: \['.+lib'\]", str(client.out)))
-    assert bool(re.search("WARN: FOO INCLUDEDIRS: \['.+include'\]", str(client.out)))
+    assert bool(re.search(r"WARN: FOO LIBDIRS: \['.+lib']", client.out))
+    assert bool(re.search(r"WARN: FOO INCLUDEDIRS: \['.+include']", client.out))
     assert "WARN: FOO RESDIRS: []" in client.out