Repository: conan-io/conan

Issue:
`--build <pattern>` seems not to work
When calling `conan help install` it states the following regarding the --build parameter:

```
--build=[pattern] Build always these packages from source, but never build the others. [...] 'pattern' is a fnmatch file pattern of a package name.
```

I would like to install a package and rebuild all of my own packages with it. All of my packages are within the `@monsdar/release` channel. The following seems not to work though: `conan install ... --build *monsdar*`. Conan gives me the message `ERROR: No package matching '*monsdar*' pattern`.

Do I do something wrong? Or is this feature not working correctly?

This happens with Conan 1.13.0 installed via Pip. Python 3.6.1 on a Windows 10 Professional with 64bit.


Discussion:
Note: Just saw what I was originally looking is already there but not working... So I rewrote this issue.
I think you forgot the equals sign.

The parameter should look like this:
```bat
conan install ... --build="*monsdar*"
```

Quotes are optional.
Does this work for you then? In my case the following does not work:
* `conan install ... --build *monsdar*`
* `conan install ... --build=*monsdar*`
* `conan install ... --build "*monsdar*"`
* `conan install ... --build="*monsdar*"`
* `conan install ... --build '*monsdar*'`
* `conan install ... --build='*monsdar*'`
Sorry, my mistake I have been testing it with package names instead of package references.
The ```build=pattern``` only works with package names e.g. ```boost```, ```boost*```, ... not ```boost/1.69.0@test/testing``` or ```*@test/*```.

As the [documentation](https://docs.conan.io/en/latest/reference/commands/consumer/install.html#conan-install) states for the ```conan install``` command:

> 'pattern' is a fnmatch file pattern of a package name.

And what happen when a package has the name `monsdar` but also there are packages using `monsdar` as namespace? For example:

    monsdar/1.0.0@company/stable
    foobar/0.1.0@monsdar/stable

The [Build Mode](https://github.com/conan-io/conan/blob/b3856467cab48aaf8aa873221d8b4c1bbd232917/conans/client/graph/build_mode.py#L6) can accept full reference, partial package name or regular modes (build, outdated,..). You could add the full reference multiple times, I know this is annoying when you have dozen of packages, but the namespace is not part of package id. 

@memsharded Should we support namespace as build mode, like `--build "*@user/channel"` ?


Then I will correct my answer regarding the full reference.

Specifying full references works according to the _source code_. :)
If it's not working, it's a possible counter feature, not a bug :joy: :bug: 
@uilianries: In case my pattern matches multiple types of packages I'd need to define my pattern better. An working example for that could be the following: 
* `conan install ... --build */*@monsdar/release`
* `conan install ... --build */*@monsdar/*`
* `conan install ... --build *@monsdar*`

I can see that this could a a valid usecase. Having a way to define the build-packages like that would make life much easier.
Well I see no limitation on implement this and I think you are not the first one who requests such feature. @memsharded @lasote WDYT ?

Resolving patch:
diff --git a/conans/client/command.py b/conans/client/command.py
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -1665,7 +1665,7 @@ def _add_common_install_arguments(parser, build_help):
                        when missing binary package.
     --build=[pattern]  Build always these packages from source, but never build the others.
                        Allows multiple --build parameters. 'pattern' is a fnmatch file pattern
-                       of a package name.
+                       of a package reference.
 
     Default behavior: If you don't specify anything, it will be similar to '--build=never', but
     package recipes can override it with their 'build_policy' attribute in the conanfile.py.
diff --git a/conans/client/graph/build_mode.py b/conans/client/graph/build_mode.py
--- a/conans/client/graph/build_mode.py
+++ b/conans/client/graph/build_mode.py
@@ -53,7 +53,9 @@ def forced(self, conan_file, ref):
 
         # Patterns to match, if package matches pattern, build is forced
         for pattern in self.patterns:
-            if fnmatch.fnmatch(ref.name, pattern) or repr(ref) == pattern:
+            is_matching_name = fnmatch.fnmatch(ref.name, pattern)
+            is_matching_ref = fnmatch.fnmatch(repr(ref), pattern)
+            if is_matching_name or is_matching_ref:
                 try:
                     self._unused_patterns.remove(pattern)
                 except ValueError:


Test patch:
diff --git a/conans/test/unittests/client/graph/build_mode_test.py b/conans/test/unittests/client/graph/build_mode_test.py
--- a/conans/test/unittests/client/graph/build_mode_test.py
+++ b/conans/test/unittests/client/graph/build_mode_test.py
@@ -72,3 +72,47 @@ def test_allowed(self):
 
         build_mode = BuildMode([], self.output)
         self.assertFalse(build_mode.allowed(self.conanfile))
+
+    def test_pattern_matching(self):
+        build_mode = BuildMode(["Boost*"], self.output)
+        reference = ConanFileReference.loads("Boost/1.69.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("Boost_Addons/1.0.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("MyBoost/1.0@user/stable")
+        self.assertFalse(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/Boost@user/stable")
+        self.assertFalse(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0@Boost/stable")
+        self.assertFalse(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0@user/Boost")
+        self.assertFalse(build_mode.forced(self.conanfile, reference))
+
+        build_mode = BuildMode(["foo/*@user/stable"], self.output)
+        reference = ConanFileReference.loads("foo/1.0.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0.0-abcdefg@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+
+        build_mode = BuildMode(["*@user/stable"], self.output)
+        reference = ConanFileReference.loads("foo/1.0.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("bar/1.0@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0.0-abcdefg@user/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("foo/1.0.0@NewUser/stable")
+        self.assertFalse(build_mode.forced(self.conanfile, reference))
+
+        build_mode = BuildMode(["*Tool"], self.output)
+        reference = ConanFileReference.loads("Tool/0.1@lasote/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("PythonTool/0.1@lasote/stable")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+        reference = ConanFileReference.loads("SomeTool/1.2@user/channel")
+        self.assertTrue(build_mode.forced(self.conanfile, reference))
+
+        build_mode.report_matches()
+        self.assertEqual("", self.output)