Repository: conan-io/conan

Issue:
Inverted arguments order when calling 'conan install'
Hi, while experimenting, I've seen that this fails:
`conan install -o boost:shared=True --build missing ..`
whereas this succeeds:
`conan install -o boost:shared=True .. --build missing`

This is unexpected, as the documentation states the very opposite, saying that the path should be last, after all options if any:
```
usage: conan install [-h] [-g GENERATOR] [-if INSTALL_FOLDER] [-m [MANIFESTS]]
                     [-mi [MANIFESTS_INTERACTIVE]] [-v [VERIFY]]
                     [--no-imports] [-u] [-pr PROFILE] [-r REMOTE]
                     [-o OPTIONS] [-s SETTINGS] [-e ENV]
                     [-b [BUILD [BUILD ...]]]
                     path
```

I'm using conan 1.0.4 on Ubuntu 14.04.

- [x] I've read the [CONTRIBUTING guide](https://raw.githubusercontent.com/conan-io/conan/develop/.github/CONTRIBUTING.md).
- [X] I've specified the Conan version, operating system version and any tool that can be relevant.
- [X] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.




Discussion:
That output is automatically generated by python-argparse. Try to use ``--build=missing`` with equal, so the parser knows better. Otherwise, just use the path always in first position, that is the conan suggested approach in all docs.
For completeness, the reason for this behavior is that the `build` command line argument is specified with [`nargs='*'`](https://github.com/conan-io/conan/blob/develop/conans/client/command.py#L1213). It therefore automatically absorbs all subsequent arguments including the path.

According to the example in the [documentation](http://docs.conan.io/en/latest/reference/commands/consumer/install.html?highlight=build#build-options), the intended use seems to be to specify `-b` or `--build` before every pattern similar to options and settings. If this interpretation is correct, the current behavior can also be considered a bug. On the first glance, using something like `nargs='?'` could be a solution.
> On the first glance, using something like nargs='?' could be a solution.

@niosHD: I don't think so, because `conan install -o boost:shared=True --build ..` is allowed (`--build` without a value, so `..` would be seen as the value). You have `--build=never` but yet someone thought that saving 7 keystrokes with `--build` instead of `--build=always` would be nice, at the price of consistency.

As a result, the command help is also ambiguous:

> Default behavior: If you don't specify anything, it will be similar to --build=never, but package recipes can override it and decide to build with "build_policy"

What does "if you don't specify anything" means in that context? If you pass `--build` without a value? If you don't use a `--build=xxxx` option? Depending on how the user understands the sentence, the expected result is the _complete opposite_.

Another problem I see in the current implementation is that in the `--build=pattern` notation, `pattern` is in the same space as `never`, `outdated` and `missing`, which may lead to collisions. You can't have packages called `never`,`missing` or `outdated`, nor any other keyword possibly added in the future (like `always`). This would have been better served by a `--build-only=pattern` separate option (option name could certainly be improved, but you get the idea). Sure this won't happen frequently, but the point is that although you control the values of the `--build` option, _you don't control the way people want to name their applications_. Otherwise you'll see ugly hacks like `--build='missin?'

I'm pretty sure you won't consider it as it can seen as breaking the API just after 1.0, but IMHO, if possible:
 - `--build` alone should be deprecated
 - `--build=always` would replace it
 - `--build=pattern` should be deprecated
 - `--build-only=pattern` would replace it

For what it's worth, git allows `--` to signal end of options and beginning of mandatory arguments.
@liberforce I fully agree with your analysis of my suggestion and your broader view on the peculiarities of the `--build` argument. I also do not really like the fact that patterns and keywords are mixed. However, since we can not break the API anymore, I think we have to live with it for now.

Still, if accepting `--build [<pattern> [<pattern> ...]]` is considered a bug and even though it does not fix the standalone `--build` case, I think that switching to `nargs='?'` would be an improvement for users that always provide an argument to build. Anyway, I do not feel strongly about this and just wanted to suggest an (subjective) improvement.
Thanks all for the feedback.

Yes, that is right, I wish we had had the time before 1.0 to review this, so we could have fixed it, but right now we cannot break, I agree on @niosHD fixing about the ``nargs``, this can be done

I'd suggest as best practice for pattern to use ``--build=pkgname*``, what will avoid collisions with the identifiers, and will put explicitly that it is a pattern, and in fact, can match different packages names like ``pkgname1``, ``pkgname_other``, etc.
> I'd suggest as best practice for pattern to use --build=pkgname*, what will avoid collisions with the identifiers

Not sure this is a best practice either, as trying to match packages with (for example) a short name may match other packages. Again, you have no control on what packages the user builds, so this will work for you but not necessarily if someone has (for example) the gtk library and an application that uses gtk and is called gtk-something, and that's a really common naming scheme.

Again, explicit is better than implicit, so when using patterns, one should use the pattern that matches exactly what it's meant to match, and not more.
> For what it's worth, git allows -- to signal end of options and beginning of mandatory arguments.

While looking for a fix to this issue I found that using the separator ``--`` for positional arguments as @liberforce said is allowed in argparse and works well with:

```
$ conan install -o boost:shared=True --build missing -- ..
```

The proposed solution by @niosHD and similiar do not work as `--build` can have an argument or not and in the case of writing ``--build .``, the dot will be parsed as the optional argument of `--build` and not the `path` positional argument.

The best way to solve this would be to force `--build` to have always one argument, but we cannot brake the command line API.

I would see if I can make the help message show positional arguments should be on first place.
True, but as [I already mentioned](https://github.com/conan-io/conan/issues/2520#issuecomment-368527305), switching to `nargs='?'` would still improve the current situation for people which do apply the argument. Given that we can make that change without breaking backward compatibility, I think it is a worthwhile improvement.

Anyway, thank you for working on this!


Resolving patch:
diff --git a/conans/client/command.py b/conans/client/command.py
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -35,10 +35,13 @@ def __call__(self, parser, namespace, values, option_strings=None):  # @UnusedVa
             # share this destination.
             parser.set_defaults(**{self.dest: None})
 
-        try:
-            dest.extend(values)
-        except ValueError:
+        if isinstance(values, str):
             dest.append(values)
+        elif values:
+            try:
+                dest.extend(values)
+            except ValueError:
+                dest.append(values)
 
 
 class OnceArgument(argparse.Action):
@@ -1232,7 +1235,7 @@ def _add_common_install_arguments(parser, build_help):
                              '-e CXX=/usr/bin/clang++',
                         nargs=1, action=Extender)
     if build_help:
-        parser.add_argument("-b", "--build", action=Extender, nargs="*", help=build_help)
+        parser.add_argument("-b", "--build", action=Extender, nargs="?", help=build_help)
 
 
 _help_build_policies = '''Optional, use it to choose if you want to build from sources:


Test patch:
diff --git a/conans/test/command/install_test.py b/conans/test/command/install_test.py
--- a/conans/test/command/install_test.py
+++ b/conans/test/command/install_test.py
@@ -453,3 +453,42 @@ class Pkg(ConanFile):
         self.assertTrue(error)
         self.assertIn("ERROR: Unable to find 'Hello/0.1@lasote/stable' in remotes",
                       client.out)
+
+    def install_argument_order_test(self):
+        # https://github.com/conan-io/conan/issues/2520
+
+        conanfile_boost = """from conans import ConanFile
+class BoostConan(ConanFile):
+    name = "boost"
+    version = "0.1"
+    options = {"shared": [True, False]}
+    default_options = "shared=True"
+"""
+        conanfile = """from conans import ConanFile
+class TestConan(ConanFile):
+    name = "Hello"
+    version = "0.1"
+    requires = "boost/0.1@conan/stable"
+"""
+        client = TestClient()
+        client.save({"conanfile.py": conanfile,
+                     "conanfile_boost.py": conanfile_boost})
+        client.run("create conanfile_boost.py conan/stable")
+        client.run("install . -o boost:shared=True --build=missing")
+        output_0 = "%s" % client.out
+        client.run("install . -o boost:shared=True --build missing")
+        output_1 = "%s" % client.out
+        client.run("install -o boost:shared=True . --build missing")
+        output_2 = "%s" % client.out
+        client.run("install -o boost:shared=True --build missing .")
+        output_3 = "%s" % client.out
+        self.assertNotIn("ERROR", output_3)
+        self.assertEqual(output_0, output_1)
+        self.assertEqual(output_1, output_2)
+        self.assertEqual(output_2, output_3)
+
+        client.run("install -o boost:shared=True --build boost . --build missing")
+        output_4 = "%s" % client.out
+        client.run("install -o boost:shared=True --build missing --build boost .")
+        output_5 = "%s" % client.out
+        self.assertEqual(output_4, output_5)