1import os
2import shutil
3
4from conans.client.source import retrieve_exports_sources
5from conans.errors import ConanException
6from conans.model.ref import ConanFileReference, PackageReference
7from conans.util.files import rmdir
8
9
10def _prepare_sources(cache, ref, remote_manager, loader, remotes):
11    conan_file_path = cache.package_layout(ref).conanfile()
12    conanfile = loader.load_basic(conan_file_path)
13    retrieve_exports_sources(remote_manager, cache, conanfile, ref, remotes)
14    return conanfile.short_paths
15
16
17def cmd_copy(ref, user_channel, package_ids, cache, user_io, remote_manager, loader, remotes,
18             force=False):
19    """
20    param package_ids: Falsey=do not copy binaries. True=All existing. []=list of ids
21    """
22    # It is important to get the revision early, so "retrieve_exports_sources" can
23    # get the right revision sources, not latest
24    layout = cache.package_layout(ref)
25    src_metadata = layout.load_metadata()
26    ref = ref.copy_with_rev(src_metadata.recipe.revision)
27    short_paths = _prepare_sources(cache, ref, remote_manager, loader, remotes)
28    package_ids = layout.package_ids() if package_ids is True else (package_ids or [])
29    package_copy(ref, user_channel, package_ids, cache, user_io, short_paths, force)
30
31
32def package_copy(src_ref, user_channel, package_ids, cache, user_io, short_paths=False,
33                 force=False):
34
35    ref = "%s/%s@%s" % (src_ref.name, src_ref.version, user_channel)
36    if ref.count('@') > 1:
37        raise ConanException("Destination must contain user/channel only.")
38
39    dest_ref = ConanFileReference.loads(ref)
40    # Generate metadata
41    src_layout = cache.package_layout(src_ref, short_paths)
42    src_metadata = src_layout.load_metadata()
43    dst_layout = cache.package_layout(dest_ref, short_paths)
44
45    # Copy export
46    export_origin = src_layout.export()
47    if not os.path.exists(export_origin):
48        raise ConanException("'%s' doesn't exist" % str(src_ref))
49    export_dest = dst_layout.export()
50    if os.path.exists(export_dest):
51        if not force and not user_io.request_boolean("'%s' already exist. Override?"
52                                                     % str(dest_ref)):
53            return
54        rmdir(export_dest)
55    shutil.copytree(export_origin, export_dest, symlinks=True)
56    user_io.out.info("Copied %s to %s" % (str(src_ref), str(dest_ref)))
57
58    export_sources_origin = src_layout.export_sources()
59    export_sources_dest = dst_layout.export_sources()
60    if os.path.exists(export_sources_dest):
61        rmdir(export_sources_dest)
62    shutil.copytree(export_sources_origin, export_sources_dest, symlinks=True)
63    user_io.out.info("Copied sources %s to %s" % (str(src_ref), str(dest_ref)))
64
65    # Copy packages
66    package_revisions = {}  # To be stored in the metadata
67    for package_id in package_ids:
68        pref_origin = PackageReference(src_ref, package_id)
69        pref_dest = PackageReference(dest_ref, package_id)
70        package_path_origin = src_layout.package(pref_origin)
71        if dst_layout.package_id_exists(package_id):
72            if not force and not user_io.request_boolean("Package '%s' already exist."
73                                                         " Override?" % str(package_id)):
74                continue
75            dst_layout.package_remove(pref_dest)
76        package_path_dest = dst_layout.package(pref_dest)
77        package_revisions[package_id] = (src_metadata.packages[package_id].revision,
78                                         src_metadata.recipe.revision)
79        shutil.copytree(package_path_origin, package_path_dest, symlinks=True)
80        user_io.out.info("Copied %s to %s" % (str(package_id), str(dest_ref)))
81
82        # The downloaded .tgz files can also be copied
83        src_download = src_layout.download_package(pref_origin)
84        dst_download = dst_layout.download_package(pref_dest)
85        rmdir(dst_download)
86        if os.path.isdir(src_download):
87            shutil.copytree(src_download, dst_download)
88
89    # The downloaded export and sources .tgz files can also be copied
90    src_download = src_layout.download_export()
91    dst_download = dst_layout.download_export()
92    rmdir(dst_download)
93    if os.path.isdir(src_download):
94        shutil.copytree(src_download, dst_download)
95
96    # Generate the metadata
97    with dst_layout.update_metadata() as metadata:
98        metadata.recipe.revision = src_metadata.recipe.revision
99        for package_id, (revision, recipe_revision) in package_revisions.items():
100            metadata.packages[package_id].revision = revision
101            metadata.packages[package_id].recipe_revision = recipe_revision
102