Repository: conan-io/conan

Issue:
Failed to install connan 1.8.2 on ubuntu16.04
I'm installing as
```
pip --no-cache-dir install conan conan_package_tools
```
As the time of installing, the latest version was 1.8.2. And,
the output logs:
```
Installing collected packages: PyJWT, colorama, patch, monotonic, fasteners, node-semver, distro, isort, wrapt, lazy-object-proxy, typed-ast, astroid, mccabe, pylint, deprecation, t
qdm, bottle, pluginbase, conan, idna, tabulate, conan-package-tools
  Running setup.py install for patch: started
    Running setup.py install for patch: finished with status 'done'
  Running setup.py install for wrapt: started
    Running setup.py install for wrapt: finished with status 'done'
  Found existing installation: tqdm 4.26.0
    Uninstalling tqdm-4.26.0:
      Successfully uninstalled tqdm-4.26.0
  Rolling back uninstall of tqdm
Could not install packages due to an EnvironmentError: [Errno 2] No such file or directory: '/usr/local/man/man1'
```

I switched the installing to version 1.7.4, and everything was good.


Discussion:
Found a similar issue here https://github.com/tqdm/tqdm/issues/460

Maybe you dont have that *man* directory or the right user permissions
I just run into this two days ago on macOS Travis CI. It worked before but stopped working suddenly, it could be related to the 1.8.0 release, but I haven't tested that yet.

I have workaround that by doing this:

> sudo mkdir -p /usr/local/man;
sudo chown -R "${USER}:admin" /usr/local/man;

Would be still nice to know where this is coming from
I think this is actually coming from the tqdm dependency that was introduced in 1.8.0

cc/ @jgsogo 
Actually I was building an image for C++ environment including conan tools. I really don't want to install _man_ 😕

In version 1.8.2, it is required tqdm==4.20.0 strictly. Why can't it be 4.26.0?
Hi @figroc,

Seems tqdm was downgraded due to an issue with python 2, you can see the conversation starting at this comment: https://github.com/conan-io/conan/pull/3545#issuecomment-421068377

We will try to get it fixed for next release. Keep us posted if you workaround this in the meantime. Thank!
You can try to ``pip install`` in the user home directory by typping ``pip install --user conan conan_package_tools`` (or using a virtualenv). Let us know if the ``--user`` works for you.

About the ``tqdm`` update, I'm tracking the issue, I'll ping them and try to push it forward.

This is affecting pretty much every project using Conan directly or indirectly, so this should probably getting a high priority. 🙈 
Hi!

I don't think this can be tagged as a bug or even an issue, it has to do with system permissions and the way pip/python works. It's so common to come across a _permissions denied_ error when installing packages system-wide with ``pip`` and the solution is to use ``sudo``, install it per-user (with ``--user``) or use a virtualenv.

We'll open the revision required for ``tqdm`` as soon as we identify a range without known issues, but chances to find this _error_ again are 100% (@figroc would have been ok with his 4.26.0 version, but anyone without a ``tqdm`` installed could have got the error) . Right now there are several dependencies of Conan that requires elevated permissions to be installed system-wide, but usually, they are already installed so it doesn't complain.

We may change the [docs](https://docs.conan.io/en/latest/installation.html#install-with-pip-recommended) and recommend installing Conan using the ``--user`` argument (although it would be even better to use a virtualenv). The ``--user`` is a long-standing discussion in the ``pip`` community and I hope it will be the default behavior anytime soon (more info: https://github.com/pypa/pip/issues/1668).


> We may change the docs and recommend installing Conan using the --user argument (although it would be even better to use a virtualenv).

What do you think about updating docs in this way, @conan-io/barbarians?
I am willing improve the installation steps and clear the section of the docs by moving some things to FAQ for example
Also, there is a proposal for a temporary fix here https://github.com/conan-io/conan/issues/3785#issuecomment-431033910

Resolving patch:
diff --git a/conans/__init__.py b/conans/__init__.py
--- a/conans/__init__.py
+++ b/conans/__init__.py
@@ -18,5 +18,5 @@
 REVISIONS = "revisions"  # Only when enabled in config, not by default look at server_launcher.py
 SERVER_CAPABILITIES = [COMPLEX_SEARCH_CAPABILITY, ]  # Still without v2 because it is changing
 
-__version__ = '1.8.3'
+__version__ = '1.8.4'
 
diff --git a/conans/client/cmd/uploader.py b/conans/client/cmd/uploader.py
--- a/conans/client/cmd/uploader.py
+++ b/conans/client/cmd/uploader.py
@@ -1,11 +1,11 @@
+import os
 import time
-
-from conans import load
+from conans.util.files import load
+from conans.client.source import complete_recipe_sources
 from conans.errors import ConanException, NotFoundException
 from conans.model.ref import PackageReference, ConanFileReference
-from conans.util.log import logger
-from conans.client.source import complete_recipe_sources
 from conans.search.search import search_recipes, search_packages
+from conans.util.log import logger
 
 
 def _is_a_reference(ref):
@@ -158,8 +158,31 @@ def _check_recipe_date(self, conan_ref, remote):
 
         if (remote_recipe_manifest != local_manifest and
                 remote_recipe_manifest.time > local_manifest.time):
+            self._print_manifest_information(remote_recipe_manifest, local_manifest, conan_ref, remote)
             raise ConanException("Remote recipe is newer than local recipe: "
                                  "\n Remote date: %s\n Local date: %s" %
                                  (remote_recipe_manifest.time, local_manifest.time))
 
         return remote_recipe_manifest
+
+    def _print_manifest_information(self, remote_recipe_manifest, local_manifest, conan_ref, remote):
+        try:
+            self._user_io.out.info("\n%s" % ("-"*40))
+            self._user_io.out.info("Remote manifest:")
+            self._user_io.out.info(remote_recipe_manifest)
+            self._user_io.out.info("Local manifest:")
+            self._user_io.out.info(local_manifest)
+            difference = remote_recipe_manifest.difference(local_manifest)
+            if "conanfile.py" in difference:
+                contents = load(os.path.join(self._client_cache.export(conan_ref),
+                                                   "conanfile.py"))
+                endlines = "\\r\\n" if "\r\n" in contents else "\\n"
+                self._user_io.out.info("Local 'conanfile.py' using '%s' line-ends" % endlines)
+                remote_contents = self._remote_manager.get_path(conan_ref, package_id=None,
+                                                                path="conanfile.py", remote=remote)
+                endlines = "\\r\\n" if "\r\n" in remote_contents else "\\n"
+                self._user_io.out.info("Remote 'conanfile.py' using '%s' line-ends" % endlines)
+            self._user_io.out.info("\n%s" % ("-"*40))
+        except Exception as e:
+            self._user_io.out.info("Error printing information about the diff: %s" % str(e))
+
diff --git a/conans/model/manifest.py b/conans/model/manifest.py
--- a/conans/model/manifest.py
+++ b/conans/model/manifest.py
@@ -1,10 +1,10 @@
-import os
 import calendar
+import datetime
+import os
 import time
-from conans.util.files import md5sum, md5, save, load, walk
-from conans.paths import PACKAGE_TGZ_NAME, EXPORT_TGZ_NAME, CONAN_MANIFEST, EXPORT_SOURCES_TGZ_NAME
 from conans.errors import ConanException
-import datetime
+from conans.paths import PACKAGE_TGZ_NAME, EXPORT_TGZ_NAME, CONAN_MANIFEST, EXPORT_SOURCES_TGZ_NAME
+from conans.util.files import md5sum, md5, save, load, walk
 
 
 def discarded_file(filename):
@@ -39,9 +39,9 @@ def gather_files(folder):
 
 class FileTreeManifest(object):
 
-    def __init__(self, time, file_sums):
+    def __init__(self, the_time, file_sums):
         """file_sums is a dict with filepaths and md5's: {filepath/to/file.txt: md5}"""
-        self.time = time
+        self.time = the_time
         self.file_sums = file_sums
 
     def files(self):
@@ -63,14 +63,14 @@ def loads(text):
         ConanDigest
         """
         tokens = text.split("\n")
-        time = int(tokens[0])
+        the_time = int(tokens[0])
         file_sums = {}
         for md5line in tokens[1:]:
             if md5line:
                 filename, file_md5 = md5line.split(": ")
                 if not discarded_file(filename):
                     file_sums[filename] = file_md5
-        return FileTreeManifest(time, file_sums)
+        return FileTreeManifest(the_time, file_sums)
 
     @staticmethod
     def load(folder):
@@ -78,16 +78,25 @@ def load(folder):
         return FileTreeManifest.loads(text)
 
     def __repr__(self):
-        ret = ["%s" % (self.time)]
-        for filepath, file_md5 in sorted(self.file_sums.items()):
-            ret.append("%s: %s" % (filepath, file_md5))
+        ret = ["%s" % self.time]
+        for file_path, file_md5 in sorted(self.file_sums.items()):
+            ret.append("%s: %s" % (file_path, file_md5))
+        ret.append("")
+        content = "\n".join(ret)
+        return content
+
+    def __str__(self):
+        dt = datetime.datetime.utcfromtimestamp(self.time).strftime('%Y-%m-%d %H:%M:%S')
+        ret = ["Time: %s" % dt]
+        for file_path, file_md5 in sorted(self.file_sums.items()):
+            ret.append("%s, MD5: %s" % (file_path, file_md5))
         ret.append("")
         content = "\n".join(ret)
         return content
 
     def save(self, folder, filename=CONAN_MANIFEST):
         path = os.path.join(folder, filename)
-        save(path, str(self))
+        save(path, repr(self))
 
     @classmethod
     def create(cls, folder, exports_sources_folder=None):


Test patch:
diff --git a/conans/test/command/upload_test.py b/conans/test/command/upload_test.py
--- a/conans/test/command/upload_test.py
+++ b/conans/test/command/upload_test.py
@@ -1,15 +1,14 @@
+import itertools
+import os
 import unittest
-from conans.tools import environment_append
-from conans.test.utils.tools import TestClient, TestServer
-from conans.test.utils.cpp_test_files import cpp_hello_conan_files
+from conans.errors import ConanException
 from conans.model.ref import ConanFileReference, PackageReference
+from conans.paths import EXPORT_SOURCES_TGZ_NAME, PACKAGE_TGZ_NAME
+from conans.test.utils.cpp_test_files import cpp_hello_conan_files
+from conans.test.utils.tools import TestClient, TestServer
+from conans.tools import environment_append
 from conans.util.files import save, is_dirty, gzopen_without_timestamps
-import os
-import itertools
 from mock import mock
-from conans.errors import ConanException
-from conans.paths import EXPORT_SOURCES_TGZ_NAME, PACKAGE_TGZ_NAME
-
 
 conanfile = """from conans import ConanFile
 class MyPkg(ConanFile):
@@ -200,7 +199,7 @@ def upload_modified_recipe_test(self):
         client = self._client()
 
         client.save({"conanfile.py": conanfile,
-                     "hello.cpp": ""})
+                     "hello.cpp": "int i=0"})
         client.run("export . frodo/stable")
         client.run("upload Hello0/1.2.1@frodo/stable")
         self.assertIn("Uploading conanmanifest.txt", client.user_io.out)
@@ -208,9 +207,8 @@ def upload_modified_recipe_test(self):
                       client.out)
 
         client2 = self._client()
-
-        client2.save({"conanfile.py": conanfile,
-                     "hello.cpp": "//comamend"})
+        client2.save({"conanfile.py": conanfile + "\r\n#end",
+                      "hello.cpp": "int i=1"})
         client2.run("export . frodo/stable")
         ref = ConanFileReference.loads("Hello0/1.2.1@frodo/stable")
         manifest = client2.client_cache.load_manifest(ref)
@@ -224,7 +222,9 @@ def upload_modified_recipe_test(self):
         # first client tries to upload again
         error = client.run("upload Hello0/1.2.1@frodo/stable", ignore_error=True)
         self.assertTrue(error)
-        self.assertIn("ERROR: Remote recipe is newer than local recipe", client.user_io.out)
+        self.assertIn("Remote recipe is newer than local recipe", client.user_io.out)
+        self.assertIn("Local 'conanfile.py' using '\\n' line-ends", client.user_io.out)
+        self.assertIn("Remote 'conanfile.py' using '\\r\\n' line-ends", client.user_io.out)
 
     def upload_unmodified_recipe_test(self):
         client = self._client()