Repository: conan-io/conan

Issue:
"conan export" fails with `short_paths` and `exports` when recipe and conan are on different drives
"conan export" fails with `short_paths` and `exports` when recipe and conan use different drives

 * OS: Windows 10 x64
 * conan version: 1.20.5 
 * python version: 3.7.3 x64

Steps to reproduce:

I have a recipe that enables `short_paths` and `exports` a folder with several files alongside the recipe file. The recipe and the files are on drive `C:`, and my conan data folder is configured to be on drive `D:` (which is slower but larger than `C:`). When I export this recipe by running `conan export`, the command always fails with `ERROR: path is on mount D:, start on mount C:`. When I move the recipe to drive `D:` the same export command succeedes. Expected behavior: the export command should succeed regardless of the drive.

The output of the failed `conan export` command:

	Exporting package recipe
	Traceback (most recent call last):
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\command.py", line 1938, in run
	    method(args[0][1:])
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\command.py", line 995, in export
	    ignore_dirty=args.ignore_dirty)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\conan_api.py", line 81, in wrapper
	    return f(api, *args, **kwargs)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\conan_api.py", line 753, in export
	    graph_lock=graph_lock, ignore_dirty=ignore_dirty)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\cmd\export.py", line 120, in cmd_export
	    export_recipe(conanfile, origin_folder, package_layout.export())
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\cmd\export.py", line 431, in export_recipe
	    copier(pattern, links=True, excludes=excluded_exports)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\file_copier.py", line 81, in __call__
	    keep_path, excluded_folders=excluded)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\file_copier.py", line 100, in _copy
	    self.link_folders(src, dst, link_folders)
	  File "C:\Programs\Python37_x64\lib\site-packages\conans\client\file_copier.py", line 178, in link_folders
	    link = os.path.relpath(link, os.path.dirname(src_link))
	  File "C:\Programs\Python37_x64\lib\ntpath.py", line 562, in relpath
	    path_drive, start_drive))
	ValueError: path is on mount 'D:', start on mount 'C:'
	
	ERROR: path is on mount 'D:', start on mount 'C:'
	


Discussion:
Hi @guban 

Doing a quick inspection to the code and your error trace, it seems that the problem is not in the drive, but on the fact that you have symlinks, and in Windows those symlinks cannot be relativized between different drives.

Do you really intend to have symlinks in Windows? In the first place, having symlinks in the exported files is typically not a good idea, it makes things more complicated and error prone. In all platforms, but specially in Windows. What would be your use case?
Indeed, in a subfolder next to `conanfile.py` there is a directory symlink that points to drive `D:` (it took some time to find it as I created that symlink long ago for purposes not related to conan). The thing is, I do not export that symlink (and, of course, I do not intend to do so).

Here is a minimal example that reproduces the bug:

File `c:/work/conanfile.py`:

	from conans import ConanFile
	class cccConan(ConanFile):
	    name = "ccc"
	    settings = "os", "compiler", "build_type", "arch"
	    exports = 'a.txt'	

File `c:/work/a.txt` is a tiny text file; its content is irrelevant.

Folder `c:/work/my_link` is a symlink pointing to folder `d:/work`.

I export this recipe using this command:

	conan export . 1.0.0@me/repr

With this, I get the `path is on mount 'D:', start on mount 'C:'` error. This error disappears if I remove the line `exports = 'a.txt'` from the recipe, or if I remove the symlink, or if I change the symlink to point to a folder on drive `C:`.

The bug is reproducible without `short_paths=True`. Most likely, the fact that my conan storage is on drive `D:` is irrelevant, too.

Yes, I think you are right, the ``short_paths`` is irrelevant here, it is all about the symlinks.

I have been inspecting the code, and I am still not sure how to improve this without a horrible hack for this Windows specific behavior.

I think there is a workaround excluding that folder from the exports, like:
```python
exports = 'a.txt', '!my_link'
```
With this you don't need to remove your exports, or your symlink.  Please tell me if this helps.

I would also like to suggest to have a look to the "SCM" feature, to use git as the origin of sources instead of exporting the sources together with the recipe.


Resolving patch:
diff --git a/conans/client/file_copier.py b/conans/client/file_copier.py
--- a/conans/client/file_copier.py
+++ b/conans/client/file_copier.py
@@ -3,6 +3,7 @@
 import shutil
 from collections import defaultdict
 
+from conans.errors import ConanException
 from conans.util.files import mkdir, walk
 
 
@@ -175,7 +176,12 @@ def link_folders(src, dst, linked_folders):
             link = os.readlink(src_link)
             # Absoluted path symlinks are a problem, convert it to relative
             if os.path.isabs(link):
-                link = os.path.relpath(link, os.path.dirname(src_link))
+                try:
+                    link = os.path.relpath(link, os.path.dirname(src_link))
+                except ValueError as e:
+                    # https://github.com/conan-io/conan/issues/6197 fails if Windows and other Drive
+                    raise ConanException("Symlink '%s' pointing to '%s' couldn't be made relative:"
+                                         " %s" % (src_link, link, str(e)))
 
             dst_link = os.path.join(dst, linked_folder)
             try:


Test patch: