Repository: conan-io/conan
Pull request #2786 (2018-04-22 22:25:10)

remove RequireResolver update from ctor
This does the same refactor than a previous PR, moving the ``update`` from a class attribute to a function argument call.


Patch:
diff --git a/conans/client/action_recorder.py b/conans/client/action_recorder.py
index bf573d9cb..d771bf075 100644
--- a/conans/client/action_recorder.py
+++ b/conans/client/action_recorder.py
@@ -7,6 +7,8 @@ from datetime import datetime
 from collections import namedtuple, OrderedDict
 
 # Install actions
+from conans.model.ref import ConanFileReference, PackageReference
+
 INSTALL_CACHE = 0
 INSTALL_DOWNLOADED = 1
 INSTALL_BUILT = 2
@@ -32,8 +34,11 @@ class ActionRecorder(object):
     def __init__(self):
         self._inst_recipes_actions = OrderedDict()
         self._inst_packages_actions = OrderedDict()
+        self._inst_recipes_develop = set()  # Recipes being created (to set dependency=False)
 
     # ###### INSTALL METHODS ############
+    def add_recipe_being_developed(self, reference):
+        self._inst_recipes_develop.add(reference)
 
     def _add_recipe_action(self, reference, action):
         if reference not in self._inst_recipes_actions:
@@ -90,6 +95,12 @@ class ActionRecorder(object):
                 ret.append((_package_ref, _package_action))
         return ret
 
+    def in_development_recipe(self, reference):
+        return reference in self._inst_recipes_develop
+
+    def get_info(self):
+        return self.get_install_info()
+
     def get_install_info(self):
         ret = {"error": self.install_errored,
                "installed": []}
@@ -98,11 +109,15 @@ class ActionRecorder(object):
             error = None if the_action.type != INSTALL_ERROR else the_action.doc
             doc = {"id": str(the_ref),
                    "downloaded": the_action.type == INSTALL_DOWNLOADED,
-                   "built": the_action.type == INSTALL_BUILT,
                    "cache": the_action.type == INSTALL_CACHE,
                    "error": error,
                    "remote": the_action.doc.get("remote", None),
                    "time": the_action.time}
+            if isinstance(the_ref, ConanFileReference):
+                doc["dependency"] = not self.in_development_recipe(the_ref)
+            else:
+                doc["built"] = the_action.type == INSTALL_BUILT
+
             if doc["remote"] is None and error:
                 doc["remote"] = error.get("remote", None)
             return doc
@@ -111,7 +126,6 @@ class ActionRecorder(object):
             # Could be a download and then an access to cache, we want the first one
             action = actions[0]
             recipe_doc = get_doc_for_ref(ref, action)
-            del recipe_doc["built"]  # Avoid confusions
             packages = self._get_installed_packages(ref)
             tmp = {"recipe": recipe_doc,
                    "packages": []}
diff --git a/conans/client/client_cache.py b/conans/client/client_cache.py
index 05aef0199..d9d43c533 100644
--- a/conans/client/client_cache.py
+++ b/conans/client/client_cache.py
@@ -13,8 +13,9 @@ from conans.model.profile import Profile
 from conans.model.ref import ConanFileReference
 from conans.model.settings import Settings
 from conans.paths import SimplePaths, CONANINFO, PUT_HEADERS
-from conans.util.files import save, load, normalize
-from conans.util.locks import SimpleLock, ReadLock, WriteLock, NoLock
+from conans.util.files import save, load, normalize, list_folder_subdirs
+from conans.util.locks import SimpleLock, ReadLock, WriteLock, NoLock, Lock
+import shutil
 
 
 CONAN_CONF = 'conan.conf'
@@ -69,6 +70,7 @@ class ClientCache(SimplePaths):
         return WriteLock(self.conan(conan_ref), conan_ref, self._output)
 
     def conanfile_lock_files(self, conan_ref):
+        # Used in ConanRemover
         if self._no_locks():
             return ()
         return WriteLock(self.conan(conan_ref), conan_ref, self._output).files
@@ -257,6 +259,18 @@ class ClientCache(SimplePaths):
                         break  # not empty
                 ref_path = os.path.dirname(ref_path)
 
+    def remove_locks(self):
+        folders = list_folder_subdirs(self._store_folder, 4)
+        for folder in folders:
+            conan_folder = os.path.join(self._store_folder, folder)
+            Lock.clean(conan_folder)
+            shutil.rmtree(os.path.join(conan_folder, "locks"), ignore_errors=True)
+
+    def remove_package_locks(self, reference):
+        conan_folder = self.conan(reference)
+        Lock.clean(conan_folder)
+        shutil.rmtree(os.path.join(conan_folder, "locks"), ignore_errors=True)
+
     def invalidate(self):
         self._conan_config = None
         self._settings = None
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
index 5dc6c3188..8dbed0463 100644
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -103,7 +103,7 @@ def _export_conanfile(conanfile_path, output, paths, conanfile, conan_ref, keep_
     source = paths.source(conan_ref, conanfile.short_paths)
     remove = False
     if is_dirty(source):
-        output.info("Source folder is dirty, forcing removal")
+        output.info("Source folder is corrupted, forcing removal")
         remove = True
     elif modified_recipe and not keep_source and os.path.exists(source):
         output.info("Package recipe modified in export, forcing source folder removal")
@@ -116,7 +116,7 @@ def _export_conanfile(conanfile_path, output, paths, conanfile, conan_ref, keep_
             rmdir(source)
         except BaseException as e:
             output.error("Unable to delete source folder. "
-                         "Will be marked as dirty for deletion")
+                         "Will be marked as corrupted for deletion")
             output.warn(str(e))
             set_dirty(source)
 
diff --git a/conans/client/cmd/uploader.py b/conans/client/cmd/uploader.py
index 52945dc22..29d42ca70 100644
--- a/conans/client/cmd/uploader.py
+++ b/conans/client/cmd/uploader.py
@@ -86,8 +86,9 @@ class CmdUpload(object):
         if all_packages:
             self._check_reference(conan_ref)
 
-            for index, package_id in enumerate(self._client_cache.conan_packages(conan_ref)):
-                total = len(self._client_cache.conan_packages(conan_ref))
+            packages = self._client_cache.conan_packages(conan_ref)
+            total = len(packages)
+            for index, package_id in enumerate(packages):
                 self.upload_package(PackageReference(conan_ref, package_id), index + 1, total,
                                     retry, retry_wait, skip_upload, integrity_check, no_overwrite)
 
diff --git a/conans/client/command.py b/conans/client/command.py
index 19513161d..01ae0b458 100644
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -54,6 +54,7 @@ class OnceArgument(argparse.Action):
             raise argparse.ArgumentError(None, msg)
         setattr(namespace, self.dest, values)
 
+
 _BUILD_FOLDER_HELP = ("Directory for the build process. Defaulted to the current directory. A "
                       "relative path to current directory can also be specified")
 _INSTALL_FOLDER_HELP = ("Directory containing the conaninfo.txt and conanbuildinfo.txt files "
@@ -220,17 +221,21 @@ class Command(object):
 
         cwd = os.getcwd()
 
+        info = None
         try:
-            self._conan.create(args.path, name, version, user, channel,
-                               args.profile, args.settings, args.options,
-                               args.env, args.test_folder, args.not_export,
-                               args.build, args.keep_source, args.keep_build, args.verify,
-                               args.manifests, args.manifests_interactive,
-                               args.remote, args.update,
-                               test_build_folder=args.test_build_folder)
+            info = self._conan.create(args.path, name, version, user, channel,
+                                      args.profile, args.settings, args.options,
+                                      args.env, args.test_folder, args.not_export,
+                                      args.build, args.keep_source, args.keep_build, args.verify,
+                                      args.manifests, args.manifests_interactive,
+                                      args.remote, args.update,
+                                      test_build_folder=args.test_build_folder)
+        except ConanException as exc:
+            info = exc.info
+            raise
         finally:
-            if args.json:
-                self._outputer.json_install(self._conan.recorder.get_install_info(), args.json, cwd)
+            if args.json and info:
+                self._outputer.json_install(info, args.json, cwd)
 
     def download(self, *args):
         """Downloads recipe and binaries to the local cache, without using settings. It works
@@ -289,34 +294,38 @@ class Command(object):
         args = parser.parse_args(*args)
         cwd = os.getcwd()
 
+        info = None
         try:
             try:
                 reference = ConanFileReference.loads(args.path_or_reference)
             except ConanException:
-                self._conan.install(path=args.path_or_reference,
-                                    settings=args.settings, options=args.options,
-                                    env=args.env,
-                                    remote=args.remote,
-                                    verify=args.verify, manifests=args.manifests,
-                                    manifests_interactive=args.manifests_interactive,
-                                    build=args.build, profile_name=args.profile,
-                                    update=args.update, generators=args.generator,
-                                    no_imports=args.no_imports,
-                                    install_folder=args.install_folder)
+                info = self._conan.install(path=args.path_or_reference,
+                                           settings=args.settings, options=args.options,
+                                           env=args.env,
+                                           remote=args.remote,
+                                           verify=args.verify, manifests=args.manifests,
+                                           manifests_interactive=args.manifests_interactive,
+                                           build=args.build, profile_name=args.profile,
+                                           update=args.update, generators=args.generator,
+                                           no_imports=args.no_imports,
+                                           install_folder=args.install_folder)
             else:
-                self._conan.install_reference(reference, settings=args.settings,
-                                              options=args.options,
-                                              env=args.env,
-                                              remote=args.remote,
-                                              verify=args.verify, manifests=args.manifests,
-                                              manifests_interactive=args.manifests_interactive,
-                                              build=args.build, profile_name=args.profile,
-                                              update=args.update,
-                                              generators=args.generator,
-                                              install_folder=args.install_folder)
+                info = self._conan.install_reference(reference, settings=args.settings,
+                                                     options=args.options,
+                                                     env=args.env,
+                                                     remote=args.remote,
+                                                     verify=args.verify, manifests=args.manifests,
+                                                     manifests_interactive=args.manifests_interactive,
+                                                     build=args.build, profile_name=args.profile,
+                                                     update=args.update,
+                                                     generators=args.generator,
+                                                     install_folder=args.install_folder)
+        except ConanException as exc:
+            info = exc.info
+            raise
         finally:
-            if args.json:
-                self._outputer.json_install(self._conan.recorder.get_install_info(), args.json, cwd)
+            if args.json and info:
+                self._outputer.json_install(info, args.json, cwd)
 
     def config(self, *args):
         """Manages Conan configuration. Edits the conan.conf or installs config files.
@@ -679,7 +688,7 @@ class Command(object):
         specified, the removal will be done by default in the local conan cache.
         """
         parser = argparse.ArgumentParser(description=self.remove.__doc__, prog="conan remove")
-        parser.add_argument('pattern_or_reference', help=_PATTERN_OR_REFERENCE_HELP)
+        parser.add_argument('pattern_or_reference', nargs="?", help=_PATTERN_OR_REFERENCE_HELP)
         parser.add_argument('-b', '--builds', nargs="*", action=Extender,
                             help=("By default, remove all the build folders or select one, "
                                   "specifying the package ID"))
@@ -694,6 +703,8 @@ class Command(object):
                             help='Will remove from the specified remote')
         parser.add_argument('-s', '--src', default=False, action="store_true",
                             help='Remove source folders')
+        parser.add_argument("-l", "--locks", default=False, action="store_true",
+                            help="Remove locks")
         args = parser.parse_args(*args)
         reference = self._check_query_parameter_and_get_reference(args.pattern_or_reference,
                                                                   args.query)
@@ -704,6 +715,16 @@ class Command(object):
         if args.builds is not None and args.query:
             raise ConanException("'-q' and '-b' parameters can't be used at the same time")
 
+        if args.locks:
+            if args.pattern_or_reference:
+                raise ConanException("Specifying a pattern is not supported when removing locks")
+            self._client_cache.remove_locks()
+            self._user_io.out.info("Cache locks removed")
+            return
+        else:
+            if not args.pattern_or_reference:
+                raise ConanException('Please specify a pattern to be removed ("*" for all)')
+
         return self._conan.remove(pattern=reference or args.pattern_or_reference, query=args.query,
                                   packages=args.packages, builds=args.builds, src=args.src,
                                   force=args.force, remote=args.remote, outdated=args.outdated)
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
index ac0cc3448..96727d3f7 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -64,17 +64,26 @@ def api_method(f):
     def wrapper(*args, **kwargs):
         the_self = args[0]
         try:
+            curdir = os.getcwd()
             log_command(f.__name__, kwargs)
+            the_self._init_manager()
             with tools.environment_append(the_self._client_cache.conan_config.env_vars):
                 # Patch the globals in tools
-                return f(*args, **kwargs)
+                ret = f(*args, **kwargs)
+                if ret is None:  # FIXME: Probably each method should manage its return
+                    return the_self._recorder.get_info()
+                return ret
         except Exception as exc:
             msg = exception_message_safe(exc)
             try:
                 log_exception(exc, msg)
             except:
                 pass
+            if isinstance(exc, ConanException):
+                exc.info = the_self._recorder.get_info()
             raise
+        finally:
+            os.chdir(curdir)
 
     return wrapper
 
@@ -144,11 +153,21 @@ class ConanAPIV1(object):
     @staticmethod
     def factory(interactive=None):
         """Factory"""
-
-        use_color = get_env("CONAN_COLOR_DISPLAY", 1)
-        if use_color and hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
+        # Respect color env setting or check tty if unset
+        color_set = "CONAN_COLOR_DISPLAY" in os.environ
+        if ((color_set and get_env("CONAN_COLOR_DISPLAY", 1))
+                or (not color_set
+                    and hasattr(sys.stdout, "isatty")
+                    and sys.stdout.isatty())):
+            # in PyCharm disable convert/strip
+            if get_env("PYCHARM_HOSTED"):
+                convert = False
+                strip = False
+            else:
+                convert = None
+                strip = None
             import colorama
-            colorama.init()
+            colorama.init(convert=convert, strip=strip)
             color = True
         else:
             color = False
@@ -197,13 +216,22 @@ class ConanAPIV1(object):
         self._user_io = user_io
         self._runner = runner
         self._remote_manager = remote_manager
-        self.recorder = ActionRecorder()
+        self._search_manager = search_manager
+        self._settings_preprocessor = _settings_preprocessor
         self._registry = RemoteRegistry(self._client_cache.registry, self._user_io.out)
-        self._manager = ConanManager(client_cache, user_io, runner, remote_manager, search_manager,
-                                     _settings_preprocessor, self.recorder, self._registry)
+        self._recorder = None
+        self._manager = None
+
         if not interactive:
             self._user_io.disable_input()
 
+    def _init_manager(self):
+        """Every api call gets a new recorder and new manager"""
+        self._recorder = ActionRecorder()
+        self._manager = ConanManager(self._client_cache, self._user_io, self._runner,
+                                     self._remote_manager, self._search_manager,
+                                     self._settings_preprocessor, self._recorder, self._registry)
+
     @api_method
     def new(self, name, header=False, pure_c=False, test=False, exports_sources=False, bare=False,
             cwd=None, visual_versions=None, linux_gcc_versions=None, linux_clang_versions=None,
@@ -309,6 +337,7 @@ class ConanAPIV1(object):
                                          "or it doesn't have a conanfile.py" % tf)
 
         test_conanfile_path = get_test_conanfile_path(test_folder)
+        self._recorder.add_recipe_being_developed(reference)
 
         if test_conanfile_path:
             pt = PackageTester(self._manager, self._user_io)
diff --git a/conans/client/file_copier.py b/conans/client/file_copier.py
index 507733362..acaf77363 100644
--- a/conans/client/file_copier.py
+++ b/conans/client/file_copier.py
@@ -6,18 +6,23 @@ from collections import defaultdict
 from conans import tools
 
 
-def report_copied_files(copied, output, warn=False):
+def report_copied_files(copied, output):
     ext_files = defaultdict(list)
     for f in copied:
         _, ext = os.path.splitext(f)
         ext_files[ext].append(os.path.basename(f))
 
-    for ext, files in ext_files.items():
-        files_str = (": " + ", ".join(files)) if len(files) < 5 else ""
-        output.info("Copied %d '%s' files%s" % (len(files), ext, files_str))
+    if not ext_files:
+        return False
 
-    if warn and not ext_files:
-        output.warn("No files copied!")
+    for ext, files in ext_files.items():
+        files_str = (", ".join(files)) if len(files) < 5 else ""
+        file_or_files = "file" if len(files) == 1 else "files"
+        if not ext:
+            output.info("Copied %d %s: %s" % (len(files), file_or_files, files_str))
+        else:
+            output.info("Copied %d '%s' %s: %s" % (len(files), ext, file_or_files, files_str))
+    return True
 
 
 class FileCopier(object):
@@ -42,8 +47,8 @@ class FileCopier(object):
         if excluded:
             self._excluded.append(excluded)
 
-    def report(self, output, warn=False):
-        report_copied_files(self._copied, output, warn)
+    def report(self, output):
+        return report_copied_files(self._copied, output)
 
     def __call__(self, pattern, dst="", src="", keep_path=True, links=False, symlinks=None,
                  excludes=None, ignore_case=False):
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 4fb1889d7..4131ae325 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -48,13 +48,13 @@ class DepsGraphBuilder(object):
         logger.debug("Deps-builder: Propagate info %s" % (time.time() - t1))
         return dep_graph
 
-    def _resolve_deps(self, node, aliased):
+    def _resolve_deps(self, node, aliased, update):
         # Resolve possible version ranges of the current node requirements
         # new_reqs is a shallow copy of what is propagated upstream, so changes done by the
         # RequireResolver are also done in new_reqs, and then propagated!
         conanfile, conanref = node.conanfile, node.conan_ref
         for _, require in conanfile.requires.items():
-            self._resolver.resolve(require, conanref)
+            self._resolver.resolve(require, conanref, update)
 
         # After resolving ranges,
         for req in conanfile.requires.values():
@@ -86,7 +86,7 @@ class DepsGraphBuilder(object):
         # basic node configuration
         new_reqs, new_options = self._config_node(node, down_reqs, down_ref, down_options)
 
-        self._resolve_deps(node, aliased)
+        self._resolve_deps(node, aliased, update)
 
         # Expand each one of the current requirements
         for name, require in node.conanfile.requires.items():
diff --git a/conans/client/graph/require_resolver.py b/conans/client/graph/require_resolver.py
index e59029363..e0629a5d7 100644
--- a/conans/client/graph/require_resolver.py
+++ b/conans/client/graph/require_resolver.py
@@ -22,13 +22,12 @@ def satisfying(list_versions, versionexpr, output):
 
 class RequireResolver(object):
 
-    def __init__(self, output, local_search, remote_search, update):
+    def __init__(self, output, local_search, remote_search):
         self._output = output
         self._local_search = local_search
         self._remote_search = remote_search
-        self._update = update
 
-    def resolve(self, require, base_conanref):
+    def resolve(self, require, base_conanref, update):
         version_range = require.version_range
         if version_range is None:
             return
@@ -50,7 +49,7 @@ class RequireResolver(object):
         # The search pattern must be a string
         search_ref = str(ConanFileReference(ref.name, "*", ref.user, ref.channel))
 
-        if self._update:
+        if update:
             searches = (self._resolve_remote, self._resolve_local)
         else:
             searches = (self._resolve_local, self._resolve_remote)
@@ -82,7 +81,7 @@ class RequireResolver(object):
         if remote_found:
             return self._resolve_version(version_range, remote_found)
 
-    def _resolve_version(self, version_range, local_found):
-        versions = {ref.version: ref for ref in local_found}
+    def _resolve_version(self, version_range, refs_found):
+        versions = {ref.version: ref for ref in refs_found}
         result = satisfying(versions, version_range, self._output)
         return versions.get(result)
diff --git a/conans/client/installer.py b/conans/client/installer.py
index 9d153fffd..e45c815e3 100644
--- a/conans/client/installer.py
+++ b/conans/client/installer.py
@@ -9,7 +9,8 @@ from conans.model.conan_file import get_env_context_manager
 from conans.model.env_info import EnvInfo
 from conans.model.user_info import UserInfo
 from conans.paths import CONANINFO, BUILD_INFO, RUN_LOG_NAME
-from conans.util.files import save, rmdir, mkdir, make_read_only
+from conans.util.files import save, rmdir, mkdir, make_read_only, is_dirty,\
+    set_dirty, clean_dirty
 from conans.model.ref import PackageReference
 from conans.util.log import logger
 from conans.errors import (ConanException, conanfile_exception_formatter,
@@ -268,8 +269,10 @@ class ConanInstaller(object):
                 package_reference = PackageReference(conan_ref, package_id)
                 check_outdated = self._build_mode.outdated
 
+                package_folder = self._client_cache.package(package_reference,
+                                                            short_paths=conanfile.short_paths)
                 if self._remote_proxy.package_available(package_reference,
-                                                        conanfile.short_paths,
+                                                        package_folder,
                                                         check_outdated):
                     skip_nodes.add(node)
 
@@ -308,15 +311,18 @@ class ConanInstaller(object):
             package_folder = self._client_cache.package(package_ref,
                                                         conan_file.short_paths)
 
-            if build_needed and (conan_ref, package_id) not in self._built_packages:
-                self._build_package(node, package_id, package_ref, output,
-                                    keep_build, profile_build_requires, flat, deps_graph, update)
-            else:
-                self._get_existing_package(conan_file, package_ref, output, package_folder, update)
-                self._propagate_info(node, flat, deps_graph)
+            with self._client_cache.package_lock(package_ref):
+                set_dirty(package_folder)
+                if build_needed and (conan_ref, package_id) not in self._built_packages:
+                    self._build_package(node, package_id, package_ref, output,
+                                        keep_build, profile_build_requires, flat, deps_graph, update)
+                else:
+                    self._get_existing_package(conan_file, package_ref, output, package_folder, update)
+                    self._propagate_info(node, flat, deps_graph)
 
-            # Call the info method
-            self._call_package_info(conan_file, package_folder)
+                # Call the info method
+                self._call_package_info(conan_file, package_folder)
+                clean_dirty(package_folder)
 
         # Finally, propagate information to root node (conan_ref=None)
         self._propagate_info(root_node, flat, deps_graph)
@@ -360,30 +366,28 @@ class ConanInstaller(object):
                 builder.prepare_build()
 
         with self._client_cache.conanfile_read_lock(conan_ref):
-            with self._client_cache.package_lock(builder.build_reference):
-                try:
-                    if not skip_build:
-                        builder.build()
-                    builder.package()
-                except ConanException as exc:
-                    self._recorder.package_install_error(package_ref, INSTALL_ERROR_BUILDING,
-                                                         str(exc), remote=None)
-                    raise exc
-                else:
-                    self._remote_proxy.handle_package_manifest(package_ref)
+            try:
+                if not skip_build:
+                    builder.build()
+                builder.package()
+            except ConanException as exc:
+                self._recorder.package_install_error(package_ref, INSTALL_ERROR_BUILDING,
+                                                     str(exc), remote=None)
+                raise exc
+            else:
+                self._remote_proxy.handle_package_manifest(package_ref)
 
-                    # Log build
-                    self._log_built_package(builder.build_folder, package_ref, time.time() - t1)
-                    self._built_packages.add((conan_ref, package_id))
+                # Log build
+                self._log_built_package(builder.build_folder, package_ref, time.time() - t1)
+                self._built_packages.add((conan_ref, package_id))
 
     def _get_existing_package(self, conan_file, package_reference, output, package_folder, update):
-        with self._client_cache.package_lock(package_reference):
-            installed = get_package(conan_file, package_reference, package_folder, output,
-                                    self._recorder, self._remote_proxy, update=update)
-            self._remote_proxy.handle_package_manifest(package_reference)
-            if installed:
-                _handle_system_requirements(conan_file, package_reference,
-                                            self._client_cache, output)
+        installed = get_package(conan_file, package_reference, package_folder, output,
+                                self._recorder, self._remote_proxy, update=update)
+        self._remote_proxy.handle_package_manifest(package_reference)
+        if installed:
+            _handle_system_requirements(conan_file, package_reference,
+                                        self._client_cache, output)
 
     def _log_built_package(self, build_folder, package_ref, duration):
         log_file = os.path.join(build_folder, RUN_LOG_NAME)
@@ -452,12 +456,19 @@ class ConanInstaller(object):
                 # Avoid processing twice the same package reference
                 if package_reference not in package_references:
                     package_references.add(package_reference)
+                    package_folder = self._client_cache.package(package_reference,
+                                                                short_paths=conan_file.short_paths)
+
+                    with self._client_cache.package_lock(package_reference):
+                        if is_dirty(package_folder):
+                            output = ScopedOutput(str(conan_ref), self._out)
+                            output.warn("Package is corrupted, removing folder: %s" % package_folder)
+                            rmdir(package_folder)
                     check_outdated = self._build_mode.outdated
                     if self._build_mode.forced(conan_file, conan_ref):
                         build_node = True
                     else:
-                        available = self._remote_proxy.package_available(package_reference,
-                                                                         conan_file.short_paths,
+                        available = self._remote_proxy.package_available(package_reference, package_folder,
                                                                          check_outdated)
                         build_node = not available
 
diff --git a/conans/client/loader.py b/conans/client/loader.py
index b436a1970..ed5a96671 100644
--- a/conans/client/loader.py
+++ b/conans/client/loader.py
@@ -59,7 +59,7 @@ class ConanFileLoader(object):
 
             if consumer:
                 self._user_options.descope_options(result.name)
-                result.options.initialize_upstream(self._user_options)
+                result.options.initialize_upstream(self._user_options, local=local)
                 self._user_options.clear_unscoped_options()
             else:
                 result.in_local_cache = True
diff --git a/conans/client/manager.py b/conans/client/manager.py
index 8a6d0e8e9..72909a9b3 100644
--- a/conans/client/manager.py
+++ b/conans/client/manager.py
@@ -181,7 +181,7 @@ class ConanManager(object):
         if install_folder and existing_info_files(install_folder):
             _load_deps_info(install_folder, conanfile, required=True)
 
-        graph_builder = self._get_graph_builder(loader, False, remote_proxy)
+        graph_builder = self._get_graph_builder(loader, remote_proxy)
         deps_graph = graph_builder.load_graph(conanfile, check_updates=False, update=False)
 
         # this is a bit tricky, but works. The loading of a cache package makes the referenced
@@ -262,16 +262,16 @@ class ConanManager(object):
         conanfile._user = inject_require.user
         conanfile._channel = inject_require.channel
 
-    def _get_graph_builder(self, loader, update, remote_proxy):
+    def _get_graph_builder(self, loader, remote_proxy):
         local_search = self._search_manager
-        resolver = RequireResolver(self._user_io.out, local_search, remote_proxy, update=update)
+        resolver = RequireResolver(self._user_io.out, local_search, remote_proxy)
         graph_builder = DepsGraphBuilder(remote_proxy, self._user_io.out, loader, resolver)
         return graph_builder
 
     def _get_deps_graph(self, reference, profile, remote_proxy, check_updates, update):
         loader = self.get_loader(profile)
         conanfile = self._load_install_conanfile(loader, reference)
-        graph_builder = self._get_graph_builder(loader, False, remote_proxy)
+        graph_builder = self._get_graph_builder(loader, remote_proxy)
         deps_graph = graph_builder.load_graph(conanfile, check_updates, update)
         return deps_graph, graph_builder, conanfile
 
@@ -358,7 +358,7 @@ class ConanManager(object):
         conanfile = self._load_install_conanfile(loader, reference)
         if inject_require:
             self._inject_require(conanfile, inject_require)
-        graph_builder = self._get_graph_builder(loader, update, remote_proxy)
+        graph_builder = self._get_graph_builder(loader, remote_proxy)
         deps_graph = graph_builder.load_graph(conanfile, False, update)
 
         if not isinstance(reference, ConanFileReference):
diff --git a/conans/client/packager.py b/conans/client/packager.py
index ab3b092cc..d8d0ff513 100644
--- a/conans/client/packager.py
+++ b/conans/client/packager.py
@@ -15,11 +15,14 @@ def export_pkg(conanfile, src_package_folder, package_folder, output):
     mkdir(package_folder)
 
     output.info("Exporting to cache existing package from user folder")
-    output.info("Package folder %s" % (package_folder))
+    output.info("Package folder %s" % package_folder)
 
     copier = FileCopier(src_package_folder, package_folder)
     copier("*", symlinks=True)
-    copier.report(output, warn=True)
+
+    copy_done = copier.report(output)
+    if not copy_done:
+        output.warn("No files copied from package folder!")
 
     save(os.path.join(package_folder, CONANINFO), conanfile.info.dumps())
     digest = FileTreeManifest.create(package_folder)
@@ -29,14 +32,14 @@ def export_pkg(conanfile, src_package_folder, package_folder, output):
 
 def create_package(conanfile, source_folder, build_folder, package_folder, install_folder,
                    output, local=False, copy_info=False):
-    """ copies built artifacts, libs, headers, data, etc from build_folder to
+    """ copies built artifacts, libs, headers, data, etc. from build_folder to
     package folder
     """
     mkdir(package_folder)
 
     # Make the copy of all the patterns
     output.info("Generating the package")
-    output.info("Package folder %s" % (package_folder))
+    output.info("Package folder %s" % package_folder)
 
     try:
         package_output = ScopedOutput("%s package()" % output.scope, output)
@@ -46,7 +49,7 @@ def create_package(conanfile, source_folder, build_folder, package_folder, insta
         conanfile.install_folder = install_folder
         conanfile.build_folder = build_folder
 
-        def recipe_has(conanfile, attribute):
+        def recipe_has(attribute):
             return attribute in conanfile.__class__.__dict__
 
         if source_folder != build_folder:
@@ -54,15 +57,17 @@ def create_package(conanfile, source_folder, build_folder, package_folder, insta
             with conanfile_exception_formatter(str(conanfile), "package"):
                 with tools.chdir(source_folder):
                     conanfile.package()
-            warn = recipe_has(conanfile, "package")
-            conanfile.copy.report(package_output, warn=warn)
+            copy_done = conanfile.copy.report(package_output)
+            if not copy_done and recipe_has("package"):
+                output.warn("No files copied from source folder!")
 
         conanfile.copy = FileCopier(build_folder, package_folder)
         with tools.chdir(build_folder):
             with conanfile_exception_formatter(str(conanfile), "package"):
                 conanfile.package()
-        warn = recipe_has(conanfile, "build") and recipe_has(conanfile, "package")
-        conanfile.copy.report(package_output, warn=warn)
+        copy_done = conanfile.copy.report(package_output)
+        if not copy_done and recipe_has("build") and recipe_has("package"):
+            output.warn("No files copied from build folder!")
     except Exception as e:
         if not local:
             os.chdir(build_folder)
diff --git a/conans/client/proxy.py b/conans/client/proxy.py
index 474c65681..2125617e1 100644
--- a/conans/client/proxy.py
+++ b/conans/client/proxy.py
@@ -36,15 +36,13 @@ class ConanProxy(object):
     def registry(self):
         return self._registry
 
-    def package_available(self, package_ref, short_paths, check_outdated):
+    def package_available(self, package_ref, package_folder, check_outdated):
         """
         Returns True if there is a local or remote package available (and up to date if check_outdated).
         It wont download the package, just check its hash
         """
 
         output = ScopedOutput(str(package_ref.conan), self._out)
-        package_folder = self._client_cache.package(package_ref, short_paths=short_paths)
-
         remote_info = None
         # No package in local cache
         if not os.path.exists(package_folder):
diff --git a/conans/client/remote_manager.py b/conans/client/remote_manager.py
index c9bf3e0b6..f8e6913aa 100644
--- a/conans/client/remote_manager.py
+++ b/conans/client/remote_manager.py
@@ -10,7 +10,7 @@ from conans.errors import ConanException, ConanConnectionError, NotFoundExceptio
 from conans.model.manifest import gather_files
 from conans.paths import PACKAGE_TGZ_NAME, CONANINFO, CONAN_MANIFEST, CONANFILE, EXPORT_TGZ_NAME, \
     rm_conandir, EXPORT_SOURCES_TGZ_NAME, EXPORT_SOURCES_DIR_OLD
-from conans.util.files import gzopen_without_timestamps
+from conans.util.files import gzopen_without_timestamps, is_dirty
 from conans.util.files import tar_extract, rmdir, exception_message_safe, mkdir
 from conans.util.files import touch_folder
 from conans.util.log import logger
@@ -92,6 +92,11 @@ class RemoteManager(object):
         t1 = time.time()
         # existing package, will use short paths if defined
         package_folder = self._client_cache.package(package_reference, short_paths=None)
+        if is_dirty(package_folder):
+            raise ConanException("Package %s is corrupted, aborting upload.\n"
+                                 "Remove it with 'conan remove %s -p=%s'" % (package_reference,
+                                                                             package_reference.conan,
+                                                                             package_reference.package_id))
         # Get all the files in that directory
         files, symlinks = gather_files(package_folder)
 
diff --git a/conans/client/remover.py b/conans/client/remover.py
index 0a20a674e..bb34d2e81 100644
--- a/conans/client/remover.py
+++ b/conans/client/remover.py
@@ -23,7 +23,7 @@ class DiskRemover(object):
 
     def _remove_file(self, path, conan_ref, msg=""):
         try:
-            logger.debug("Removing folder %s" % path)
+            logger.debug("Removing file %s" % path)
             if os.path.exists(path):
                 os.remove(path)
         except OSError:
@@ -62,6 +62,7 @@ class DiskRemover(object):
     def remove_packages(self, conan_ref, ids_filter=None):
         if not ids_filter:  # Remove all
             path = self._paths.packages(conan_ref)
+            # Necessary for short_paths removal
             for package in self._paths.conan_packages(conan_ref):
                 self._remove(os.path.join(path, package), conan_ref, "package folder:%s" % package)
             self._remove(path, conan_ref, "packages")
@@ -69,7 +70,9 @@ class DiskRemover(object):
         else:
             for id_ in ids_filter:  # remove just the specified packages
                 package_ref = PackageReference(conan_ref, id_)
-                self._remove(self._paths.package(package_ref), conan_ref, "package:%s" % id_)
+                pkg_folder = self._paths.package(package_ref)
+                self._remove(pkg_folder, conan_ref, "package:%s" % id_)
+                self._remove_file(pkg_folder + ".dirty", conan_ref, "dirty flag")
                 self._remove_file(self._paths.system_reqs_package(package_ref),
                                   conan_ref, "%s/%s" % (id_, SYSTEM_REQS))
 
@@ -91,6 +94,8 @@ class ConanRemover(object):
             self._remote_proxy.remove_packages(reference, package_ids)
 
     def _local_remove(self, reference, src, build_ids, package_ids):
+        # Make sure to clean the locks too
+        self._client_cache.remove_package_locks(reference)
         remover = DiskRemover(self._client_cache)
         if src:
             remover.remove_src(reference)
diff --git a/conans/client/rest/rest_client.py b/conans/client/rest/rest_client.py
index 3bd29ee57..37d44da25 100644
--- a/conans/client/rest/rest_client.py
+++ b/conans/client/rest/rest_client.py
@@ -169,7 +169,7 @@ class RestApiClient(object):
         if not new and not deleted and modified in (["conanmanifest.txt"], []):
             return False
 
-        if no_overwrite:
+        if no_overwrite and remote_snapshot:
             if no_overwrite in ("all", "recipe"):
                 raise ConanException("Local recipe is different from the remote recipe. "
                                      "Forbbiden overwrite")
@@ -205,7 +205,7 @@ class RestApiClient(object):
         if not new and not deleted and modified in (["conanmanifest.txt"], []):
             return False
 
-        if no_overwrite:
+        if no_overwrite and remote_snapshot:
             if no_overwrite in ("all"):
                 raise ConanException("Local package is different from the remote package. "
                                      "Forbbiden overwrite")
diff --git a/conans/client/source.py b/conans/client/source.py
index a8352f148..84012443f 100644
--- a/conans/client/source.py
+++ b/conans/client/source.py
@@ -46,7 +46,7 @@ def config_source(export_folder, export_source_folder, src_folder,
         output.warn("Forced removal of source folder")
         remove_source()
     elif is_dirty(src_folder):
-        output.warn("Trying to remove dirty source folder")
+        output.warn("Trying to remove corrupted source folder")
         remove_source()
     elif conan_file.build_policy_always:
         output.warn("Detected build_policy 'always', trying to remove source folder")
@@ -82,7 +82,7 @@ def config_source(export_folder, export_source_folder, src_folder,
             os.chdir(export_folder)
             # in case source() fails (user error, typically), remove the src_folder
             # and raise to interrupt any other processes (build, package)
-            output.warn("Trying to remove dirty source folder")
+            output.warn("Trying to remove corrupted source folder")
             remove_source(raise_error=False)
             if isinstance(e, ConanExceptionInUserConanfileMethod):
                 raise e
diff --git a/conans/client/tools/oss.py b/conans/client/tools/oss.py
index d0713c0e5..014224365 100644
--- a/conans/client/tools/oss.py
+++ b/conans/client/tools/oss.py
@@ -262,7 +262,10 @@ class OSInfo(object):
     @staticmethod
     def detect_windows_subsystem():
         from conans.client.tools.win import CYGWIN, MSYS2, MSYS, WSL
-        output = OSInfo.uname()
+        try:
+            output = OSInfo.uname()
+        except ConanException:
+            return None
         if not output:
             return None
         if "cygwin" in output:
diff --git a/conans/errors.py b/conans/errors.py
index 256f20a3f..e5b73a087 100644
--- a/conans/errors.py
+++ b/conans/errors.py
@@ -68,7 +68,9 @@ class ConanException(Exception):
     """
          Generic conans exception
     """
-    pass
+    def __init__(self, *args, **kwargs):
+        self.info = None
+        super(ConanException, self).__init__(*args, **kwargs)
 
 
 class NoRemoteAvailable(ConanException):
diff --git a/conans/model/options.py b/conans/model/options.py
index 2cb950d89..8ca3310bc 100644
--- a/conans/model/options.py
+++ b/conans/model/options.py
@@ -306,6 +306,9 @@ class PackageOption(object):
     def __str__(self):
         return str(self._value)
 
+    def __int__(self):
+        return int(self._value)
+
     def _check_option_value(self, value):
         """ checks that the provided value is allowed by current restrictions
         """
@@ -427,6 +430,14 @@ class PackageOptions(object):
             self._ensure_exists(name)
             self._data[name].value = value
 
+    def set_local(self, values):
+        # For local commands, to restore state from conaninfo it is necessary to remove
+        for k in list(self._data):
+            try:
+                self._data[k].value = values._dict[k]
+            except KeyError:
+                self._data.pop(k)
+
     def propagate_upstream(self, package_values, down_ref, own_ref, pattern_options):
         """
         :param: package_values: PackageOptionValues({"shared": "True"}
@@ -547,13 +558,16 @@ class Options(object):
                 pkg_values = self._deps_package_values.setdefault(name, PackageOptionValues())
                 pkg_values.propagate_upstream(option_values, down_ref, own_ref, name)
 
-    def initialize_upstream(self, user_values):
+    def initialize_upstream(self, user_values, local=False):
         """ used to propagate from downstream the options to the upper requirements
         """
         if user_values is not None:
             assert isinstance(user_values, OptionsValues)
             # This values setter implements an update, not an overwrite
-            self._package_options.values = user_values._package_values
+            if local:
+                self._package_options.set_local(user_values._package_values)
+            else:
+                self._package_options.values = user_values._package_values
             for package_name, package_values in user_values._reqs_options.items():
                 pkg_values = self._deps_package_values.setdefault(package_name, PackageOptionValues())
                 pkg_values.update(package_values)
diff --git a/conans/util/locks.py b/conans/util/locks.py
index b90c8d7d4..49b440aee 100644
--- a/conans/util/locks.py
+++ b/conans/util/locks.py
@@ -2,6 +2,7 @@ import fasteners
 from conans.util.log import logger
 import time
 from conans.util.files import save, load
+import os
 
 
 class NoLock(object):
@@ -31,6 +32,13 @@ WRITE_BUSY_DELAY = 0.25
 
 class Lock(object):
 
+    @staticmethod
+    def clean(folder):
+        if os.path.exists(folder + ".count"):
+            os.remove(folder + ".count")
+        if os.path.exists(folder + ".count.lock"):
+            os.remove(folder + ".count.lock")
+
     def __init__(self, folder, locked_item, output):
         self._count_file = folder + ".count"
         self._count_lock_file = folder + ".count.lock"
@@ -47,8 +55,7 @@ class Lock(object):
             self._first_lock = False
             self._output.info("%s is locked by another concurrent conan process, wait..."
                               % str(self._locked_item))
-            self._output.info("If not the case, quit, and do 'conan remove %s -f'"
-                              % str(self._locked_item))
+            self._output.info("If not the case, quit, and do 'conan remove --locks'")
 
     def _readers(self):
         try:


Test patch:
diff --git a/conans/test/build_helpers/msbuild_test.py b/conans/test/build_helpers/msbuild_test.py
index e6c7a70cf..635680b36 100644
--- a/conans/test/build_helpers/msbuild_test.py
+++ b/conans/test/build_helpers/msbuild_test.py
@@ -45,7 +45,7 @@ class HelloConan(ConanFile):
         self.assertTrue(error)
         client.run('create . Hello/1.2.1@lasote/stable -s cppstd=17 '
                    '-s compiler="Visual Studio" -s compiler.version=14')
-        self.assertIn("Copied 1 '.exe' files: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.user_io.out)
 
         files = get_vs_project_files()
         files[CONANFILE] = conan_build_vs
@@ -64,18 +64,18 @@ class HelloConan(ConanFile):
         client.run("export . lasote/stable")
         client.run("install Hello/1.2.1@lasote/stable --build -s arch=x86_64")
         self.assertIn("Release|x64", client.user_io.out)
-        self.assertIn("Copied 1 '.exe' files: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.user_io.out)
 
         # Try with x86
         client.save(files, clean_first=True)
         client.run("export . lasote/stable")
         client.run("install Hello/1.2.1@lasote/stable --build -s arch=x86")
         self.assertIn("Release|x86", client.user_io.out)
-        self.assertIn("Copied 1 '.exe' files: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.user_io.out)
 
         # Try with x86 debug
         client.save(files, clean_first=True)
         client.run("export . lasote/stable")
         client.run("install Hello/1.2.1@lasote/stable --build -s arch=x86 -s build_type=Debug")
         self.assertIn("Debug|x86", client.user_io.out)
-        self.assertIn("Copied 1 '.exe' files: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.user_io.out)
diff --git a/conans/test/command/create_test.py b/conans/test/command/create_test.py
index 92424ba61..708972641 100644
--- a/conans/test/command/create_test.py
+++ b/conans/test/command/create_test.py
@@ -66,7 +66,7 @@ class MyPkg(ConanFile):
         self.assertIn("Pkg/0.1@lasote/testing: mysource!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mybuild!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mypackage!!", client.out)
-        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' files: header.h", client.out)
+        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' file: header.h", client.out)
         # keep the source
         client.save({"conanfile.py": conanfile + " "})
         client.run("create . Pkg/0.1@lasote/testing --keep-source")
@@ -74,14 +74,14 @@ class MyPkg(ConanFile):
         self.assertNotIn("Pkg/0.1@lasote/testing: mysource!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mybuild!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mypackage!!", client.out)
-        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' files: header.h", client.out)
+        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' file: header.h", client.out)
         # keep build
         client.run("create . Pkg/0.1@lasote/testing --keep-build")
         self.assertIn("Pkg/0.1@lasote/testing: Won't be built as specified by --keep-build", client.out)
         self.assertNotIn("Pkg/0.1@lasote/testing: mysource!!", client.out)
         self.assertNotIn("Pkg/0.1@lasote/testing: mybuild!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mypackage!!", client.out)
-        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' files: header.h", client.out)
+        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' file: header.h", client.out)
 
         # Changes in the recipe again
         client.save({"conanfile.py": conanfile})
@@ -92,7 +92,7 @@ class MyPkg(ConanFile):
         self.assertNotIn("Pkg/0.1@lasote/testing: mysource!!", client.out)
         self.assertNotIn("Pkg/0.1@lasote/testing: mybuild!!", client.out)
         self.assertIn("Pkg/0.1@lasote/testing: mypackage!!", client.out)
-        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' files: header.h", client.out)
+        self.assertIn("Pkg/0.1@lasote/testing package(): Copied 1 '.h' file: header.h", client.out)
 
     def keep_build_error_test(self):
         client = TestClient()
diff --git a/conans/test/command/export_dirty_test.py b/conans/test/command/export_dirty_test.py
index f2847b18e..da6db911c 100644
--- a/conans/test/command/export_dirty_test.py
+++ b/conans/test/command/export_dirty_test.py
@@ -32,7 +32,7 @@ class ExportDirtyTest(unittest.TestCase):
         self.client.save(files)
         self.client.run("export . lasote/stable")
         self.assertIn("ERROR: Unable to delete source folder. "
-                      "Will be marked as dirty for deletion",
+                      "Will be marked as corrupted for deletion",
                       self.client.user_io.out)
 
         err = self.client.run("install Hello0/0.1@lasote/stable --build", ignore_error=True)
@@ -46,7 +46,7 @@ class ExportDirtyTest(unittest.TestCase):
             return
         self.f.close()
         self.client.run("export . lasote/stable")
-        self.assertIn("Source folder is dirty, forcing removal", self.client.user_io.out)
+        self.assertIn("Source folder is corrupted, forcing removal", self.client.user_io.out)
         err = self.client.run("install Hello0/0.1@lasote/stable --build")
         self.assertFalse(err)
 
@@ -59,4 +59,4 @@ class ExportDirtyTest(unittest.TestCase):
         self.f.close()
         err = self.client.run("install Hello0/0.1@lasote/stable --build")
         self.assertFalse(err)
-        self.assertIn("WARN: Trying to remove dirty source folder", self.client.user_io.out)
+        self.assertIn("WARN: Trying to remove corrupted source folder", self.client.user_io.out)
diff --git a/conans/test/command/export_pkg_test.py b/conans/test/command/export_pkg_test.py
index fdeee26dc..eb665e691 100644
--- a/conans/test/command/export_pkg_test.py
+++ b/conans/test/command/export_pkg_test.py
@@ -33,7 +33,8 @@ class HelloPythonConan(ConanFile):
                       client.out)
 
         client.run("export-pkg . Hello/0.1@lasote/stable -pf=pkg")
-        self.assertIn("Hello/0.1@lasote/stable: WARN: No files copied!", client.out)
+        self.assertIn("Hello/0.1@lasote/stable: WARN: No files copied from package folder!",
+                      client.out)
 
     def test_package_folder(self):
         # https://github.com/conan-io/conan/issues/2350
@@ -51,7 +52,7 @@ class HelloPythonConan(ConanFile):
 
         client.run("export-pkg . Hello/0.1@lasote/stable -pf=pkg -pr=profile")
         self.assertNotIn("PACKAGE NOT CALLED", client.out)
-        self.assertIn("Hello/0.1@lasote/stable: Copied 1 '.h' files: myfile.h", client.out)
+        self.assertIn("Hello/0.1@lasote/stable: Copied 1 '.h' file: myfile.h", client.out)
         ref = ConanFileReference.loads("Hello/0.1@lasote/stable")
         pkg_folder = client.client_cache.packages(ref)
         folders = os.listdir(pkg_folder)
@@ -227,10 +228,10 @@ class TestConan(ConanFile):
         client.run("export-pkg . Hello/0.1@lasote/stable %s" % settings)
         self.assertIn("Hello/0.1@lasote/stable: A new conanfile.py version was exported",
                       client.out)
-        self.assertNotIn("Hello/0.1@lasote/stable package(): WARN: No files copied!",
+        self.assertNotIn("Hello/0.1@lasote/stable package(): WARN: No files copied",
                          client.out)  # --bare include a now mandatory package() method!
 
-        self.assertIn("Copied 1 '.a' files: libmycoollib.a", client.out)
+        self.assertIn("Copied 1 '.a' file: libmycoollib.a", client.out)
         self._consume(client, settings + " . -g cmake")
 
         cmakeinfo = load(os.path.join(client.current_folder, "conanbuildinfo.cmake"))
@@ -336,7 +337,7 @@ class TestConan(ConanFile):
         # Partial reference is ok
         client.save({CONANFILE: conanfile, "file.txt": "txt contents"})
         client.run("export-pkg . conan/stable")
-        self.assertIn("Hello/0.1@conan/stable package(): Copied 1 '.txt' files: file.txt", client.out)
+        self.assertIn("Hello/0.1@conan/stable package(): Copied 1 '.txt' file: file.txt", client.out)
 
         # Specify different name or version is not working
         error = client.run("export-pkg . lib/1.0@conan/stable -f", ignore_error=True)
@@ -360,7 +361,7 @@ class TestConan(ConanFile):
         # Partial reference is ok
         client.save({CONANFILE: conanfile, "file.txt": "txt contents"})
         client.run("export-pkg . anyname/1.222@conan/stable")
-        self.assertIn("anyname/1.222@conan/stable package(): Copied 1 '.txt' files: file.txt", client.out)
+        self.assertIn("anyname/1.222@conan/stable package(): Copied 1 '.txt' file: file.txt", client.out)
 
     def test_with_deps(self):
         client = TestClient()
diff --git a/conans/test/command/json_output_test.py b/conans/test/command/json_output_test.py
index a9f64b87a..0cf3ba030 100644
--- a/conans/test/command/json_output_test.py
+++ b/conans/test/command/json_output_test.py
@@ -23,6 +23,7 @@ class JsonOutputTest(unittest.TestCase):
         my_json = json.loads(load(os.path.join(self.client.current_folder, "myfile.json")))
         self.assertFalse(my_json["error"])
         self.assertEquals(my_json["installed"][0]["recipe"]["id"], "CC/1.0@private_user/channel")
+        self.assertFalse(my_json["installed"][0]["recipe"]["dependency"])
         self.assertTrue(my_json["installed"][0]["recipe"]["cache"])
         self.assertIsNone(my_json["installed"][0]["recipe"]["remote"])
         self.assertTrue(my_json["installed"][0]["packages"][0]["built"])
@@ -37,6 +38,7 @@ class JsonOutputTest(unittest.TestCase):
         self.assertIn("T", the_time_str) # Weak validation of the ISO 8601
         self.assertFalse(my_json["error"])
         self.assertEquals(my_json["installed"][0]["recipe"]["id"], "CC/1.0@private_user/channel")
+        self.assertTrue(my_json["installed"][0]["recipe"]["dependency"])
         self.assertFalse(my_json["installed"][0]["recipe"]["cache"])
         self.assertTrue(my_json["installed"][0]["recipe"]["downloaded"])
         self.assertIsNotNone(my_json["installed"][0]["recipe"]["remote"])
@@ -164,6 +166,10 @@ AA*: CC/1.0@private_user/channel
         my_json = load(os.path.join(self.client.current_folder, "myfile.json"))
         my_json = json.loads(my_json)
 
+        self.assertTrue(my_json["installed"][0]["recipe"]["dependency"])
+        self.assertTrue(my_json["installed"][1]["recipe"]["dependency"])
+        self.assertTrue(my_json["installed"][2]["recipe"]["dependency"])
+
         # Installed the build require CC with two options
         self.assertEquals(len(my_json["installed"][2]["packages"]), 2)
         self.assertEquals(my_json["installed"][2]["recipe"]["id"], "CC/1.0@private_user/channel")
diff --git a/conans/test/command/package_test.py b/conans/test/command/package_test.py
index 98f96aef6..2106dc342 100644
--- a/conans/test/command/package_test.py
+++ b/conans/test/command/package_test.py
@@ -19,17 +19,16 @@ class PackageLocalCommandTest(unittest.TestCase):
             the_client.run("new lib/1.0 -s")
 
             # don't need build method
-            tools.replace_in_file(os.path.join(client.current_folder,
-                                               "conanfile.py"),
-                                               "def build",
-                                               "def skip_build")
+            tools.replace_in_file(os.path.join(client.current_folder, "conanfile.py"),
+                                  "def build",
+                                  "def skip_build")
             the_client.run("install . --install-folder build")
             mkdir(os.path.join(client.current_folder, "build2"))
 
         # In current dir subdir
         prepare_for_package(client)
         client.run("package . --build-folder build2 --install-folder build --package-folder=subdir")
-        self.assertNotIn("package(): WARN: No files copied!", client.out)
+        self.assertNotIn("package(): WARN: No files copied", client.out)
         self.assertTrue(os.path.exists(os.path.join(client.current_folder, "subdir")))
 
         # In current dir subdir with conanfile path
@@ -40,7 +39,7 @@ class PackageLocalCommandTest(unittest.TestCase):
         # Default path
         prepare_for_package(client)
         client.run("package . --build-folder build")
-        self.assertNotIn("package(): WARN: No files copied!", client.out)
+        self.assertNotIn("package(): WARN: No files copied", client.out)
         self.assertTrue(os.path.exists(os.path.join(client.current_folder, "build", "package")))
 
         # Default path with conanfile path
@@ -52,7 +51,7 @@ class PackageLocalCommandTest(unittest.TestCase):
         prepare_for_package(client)
         pf = os.path.join(client.current_folder, "mypackage/two")
         client.run("package . --build-folder build --package-folder='%s'" % pf)
-        self.assertNotIn("package(): WARN: No files copied!", client.out)
+        self.assertNotIn("package(): WARN: No files copied", client.out)
         self.assertTrue(os.path.exists(os.path.join(client.current_folder, "mypackage", "two")))
 
         # Abs path with conanfile path
@@ -162,10 +161,12 @@ class MyConan(ConanFile):
     def package(self):
         self.copy(pattern="*.h", dst="include", src="include")
         self.copy(pattern="*.lib")
+        self.copy(pattern="myapp", src="bin", dst="bin")
 """
 
         client.save({"src/include/file.h": "foo",
                      "build/lib/mypkg.lib": "mylib",
+                     "build/bin/myapp": "",
                      CONANFILE: conanfile_template})
         conanfile_folder = client.current_folder
         path = conanfile_folder
@@ -185,9 +186,62 @@ class MyConan(ConanFile):
             client.run('package "{0}" --build-folder="{1}/build" '
                        '--package-folder="{2}" --source-folder="{1}/src"'.
                        format(path, conanfile_folder, package_folder))
+        self.assertNotIn("package(): Copied 1 \'\' file", client.out)
+        self.assertIn("package(): Copied 1 file: myapp", client.out)
         content = load(os.path.join(package_folder, "include/file.h"))
         self.assertEqual(content, "foo")
         self.assertEqual(sorted(os.listdir(package_folder)),
-                         sorted(["include", "lib", "conaninfo.txt", "conanmanifest.txt"]))
+                         sorted(["include", "lib", "bin", "conaninfo.txt", "conanmanifest.txt"]))
         self.assertEqual(os.listdir(os.path.join(package_folder, "include")), ["file.h"])
         self.assertEqual(os.listdir(os.path.join(package_folder, "lib")), ["mypkg.lib"])
+        self.assertEqual(os.listdir(os.path.join(package_folder, "bin")), ["myapp"])
+
+    def no_files_copied_local_package_test(self):
+        # https://github.com/conan-io/conan/issues/2753
+        client = TestClient()
+        conanfile = """
+from conans import ConanFile
+
+class MyConan(ConanFile):
+    def build(self):
+        pass
+    def package(self):
+        self.copy(pattern="*.lib")
+"""
+        client.save({"source/include/file.h": "foo",
+                     "build/bin/library.lib": "",
+                     CONANFILE: conanfile})
+        client.run("install . --install-folder=install")
+        client.run('package . --source-folder=source --install-folder=install --build-folder=build')
+        self.assertIn("No files copied from source folder!", client.out)
+        self.assertIn("Copied 1 '.lib' file: library.lib", client.out)
+
+        conanfile = """
+from conans import ConanFile
+
+class MyConan(ConanFile):
+    def build(self):
+        pass
+    def package(self):
+        self.copy(pattern="*.h")
+"""
+        client.save({CONANFILE: conanfile})
+        client.run('package . --source-folder=source --install-folder=install --build-folder=build')
+        self.assertIn("No files copied from build folder!", client.out)
+        self.assertIn("Copied 1 '.h' file: file.h", client.out)
+
+        conanfile = """
+from conans import ConanFile
+
+class MyConan(ConanFile):
+    def build(self):
+        pass
+    def package(self):
+        self.copy(pattern="*.fake")
+"""
+        client.save({CONANFILE: conanfile})
+        client.run('package . --source-folder=source --install-folder=install --build-folder=build')
+        self.assertIn("No files copied from source folder!", client.out)
+        self.assertIn("No files copied from build folder!", client.out)
+        self.assertNotIn("Copied 1 '.h' file: file.h", client.out)
+        self.assertNotIn("Copied 1 '.lib' file: library.lib", client.out)
diff --git a/conans/test/command/remove_test.py b/conans/test/command/remove_test.py
index 9516dfb89..6d312b2bb 100644
--- a/conans/test/command/remove_test.py
+++ b/conans/test/command/remove_test.py
@@ -254,7 +254,7 @@ class RemoveTest(unittest.TestCase):
                             remote_folders={"H1": None, "H2": None, "B": [1, 2], "O": [1, 2]},
                             build_folders={"H1": [1, 2], "H2": [1, 2], "B": [1, 2], "O": [1, 2]},
                             src_folders={"H1": True, "H2": True, "B": True, "O": True})
-        remote_folder = os.path.join(self.server_folder, ".conan_server/data") 
+        remote_folder = os.path.join(self.server_folder, ".conan_server/data")
         folders = os.listdir(remote_folder)
         six.assertCountEqual(self, ["Other", "Bye"], folders)
 
diff --git a/conans/test/command/upload_test.py b/conans/test/command/upload_test.py
index e4af91c23..b985945ac 100644
--- a/conans/test/command/upload_test.py
+++ b/conans/test/command/upload_test.py
@@ -225,7 +225,11 @@ class MyPkg(ConanFile):
                      "hello.h": "",
                      "hello.cpp": ""})
         client.run("create . frodo/stable")
-        client.run("upload Hello0/1.2.1@frodo/stable --all")
+
+        # First time upload
+        client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite")
+        self.assertNotIn("Forbbiden overwrite", client.out)
+        self.assertIn("Uploading Hello0/1.2.1@frodo/stable", client.out)
 
         # CASE: Upload again
         client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite")
@@ -287,7 +291,11 @@ class MyPkg(ConanFile):
                      "hello.h": "",
                      "hello.cpp": ""})
         client.run("create . frodo/stable")
-        client.run("upload Hello0/1.2.1@frodo/stable --all")
+
+        # First time upload
+        client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe")
+        self.assertNotIn("Forbbiden overwrite", client.out)
+        self.assertIn("Uploading Hello0/1.2.1@frodo/stable", client.out)
 
         # Upload again
         client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe")
diff --git a/conans/test/conan_api/curdir_kept_test.py b/conans/test/conan_api/curdir_kept_test.py
new file mode 100644
index 000000000..9020e9c9b
--- /dev/null
+++ b/conans/test/conan_api/curdir_kept_test.py
@@ -0,0 +1,24 @@
+import unittest
+import os
+
+from conans import tools
+from conans.client.conan_api import ConanAPIV1
+from conans.test.utils.test_files import temp_folder
+
+
+class CurdirKeptTest(unittest.TestCase):
+
+    def curdir_test(self):
+        tmp_folder = temp_folder(tools.mkdir_tmp())
+        conanfile = """from conans import ConanFile
+class Pkg(ConanFile):
+    name = "lib"
+    version = "1.0"
+"""
+        tools.save(os.path.join(tmp_folder, "conanfile.py"), conanfile)
+        with tools.chdir(tmp_folder):
+            api, _, _ = ConanAPIV1.factory()
+            api.create(".", name="lib", version="1.0", user="user", channel="channel")
+            self.assertEquals(tmp_folder, os.getcwd())
+            api.create(".", name="lib", version="1.0", user="user", channel="channel2")
+            self.assertEquals(tmp_folder, os.getcwd())
diff --git a/conans/test/download_test.py b/conans/test/download_test.py
index 1a64df5ef..6b59b17dd 100644
--- a/conans/test/download_test.py
+++ b/conans/test/download_test.py
@@ -6,7 +6,6 @@ from conans.errors import NotFoundException, ConanException
 from conans.model.ref import ConanFileReference, PackageReference
 from conans.test.utils.tools import TestClient, TestServer
 from conans.client.remote_registry import RemoteRegistry
-from conans.client import remote_registry
 
 
 myconan1 = """
@@ -50,11 +49,12 @@ class DownloadTest(unittest.TestCase):
         registry = RemoteRegistry(client2.client_cache.registry, client2.out)
         installer = ConanProxy(client2.paths, client2.user_io, client2.remote_manager,
                                "default", recorder=ActionRecorder(), registry=registry)
+        package_folder = client2.client_cache.package(package_ref)
 
         with self.assertRaises(NotFoundException):
             installer.get_recipe(conan_ref, False, False)
 
-        self.assertFalse(installer.package_available(package_ref, False, True))
+        self.assertFalse(installer.package_available(package_ref, package_folder, True))
 
         class BuggyRequester2(BuggyRequester):
             def get(self, *args, **kwargs):
@@ -73,7 +73,7 @@ class DownloadTest(unittest.TestCase):
             pass
 
         try:
-            installer.package_available(package_ref, False, True)
+            installer.package_available(package_ref, package_folder, True)
         except NotFoundException:
             self.assertFalse(True)  # Shouldn't capture here
         except ConanException:
diff --git a/conans/test/functional/conan_trace_file_test.py b/conans/test/functional/conan_trace_file_test.py
index e1e738c18..a533f5ae3 100644
--- a/conans/test/functional/conan_trace_file_test.py
+++ b/conans/test/functional/conan_trace_file_test.py
@@ -55,21 +55,21 @@ class HelloConan(ConanFile):
             return log_file_packaged, client.user_io.out
 
         log_file_packaged, output = _install_a_package(False, True)
-        self.assertIn("Copied 1 '.log' files: conan_run.log", output)
+        self.assertIn("Copied 1 '.log' file: conan_run.log", output)
         self.assertTrue(os.path.exists(log_file_packaged))
         contents = load(log_file_packaged)
         self.assertIn("Simulating cmake...", contents)
         self.assertNotIn("----Running------%s> echo" % os.linesep, contents)
 
         log_file_packaged, output = _install_a_package(True, True)
-        self.assertIn("Copied 1 '.log' files: conan_run.log", output)
+        self.assertIn("Copied 1 '.log' file: conan_run.log", output)
         self.assertTrue(os.path.exists(log_file_packaged))
         contents = load(log_file_packaged)
         self.assertIn("Simulating cmake...", contents)
         self.assertIn("----Running------%s> echo" % os.linesep, contents)
 
         log_file_packaged, output = _install_a_package(False, False)
-        self.assertNotIn("Copied 1 '.log' files: conan_run.log", output)
+        self.assertNotIn("Copied 1 '.log' file: conan_run.log", output)
         self.assertFalse(os.path.exists(log_file_packaged))
 
     def test_trace_actions(self):
diff --git a/conans/test/functional/deploy_test.py b/conans/test/functional/deploy_test.py
index 7d668928a..9419725aa 100644
--- a/conans/test/functional/deploy_test.py
+++ b/conans/test/functional/deploy_test.py
@@ -55,9 +55,9 @@ class Pkg(ConanFile):
             client.current_folder = temp_folder()
             client.run("install Pkg/0.1@user/testing --install-folder=%s" % folder)
 
-            self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.dll' files: mylib.dll",
+            self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.dll' file: mylib.dll",
                           client.out)
-            self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.exe' files: myapp.exe",
+            self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.exe' file: myapp.exe",
                           client.out)
             deploy_manifest = FileTreeManifest.loads(load(os.path.join(client.current_folder,
                                                                        folder,
diff --git a/conans/test/functional/imports_tests.py b/conans/test/functional/imports_tests.py
index 8ab8dbd57..4a5d9038b 100644
--- a/conans/test/functional/imports_tests.py
+++ b/conans/test/functional/imports_tests.py
@@ -51,7 +51,7 @@ class TestConan(ConanFile):
 """
         client.save({"conanfile.py": conanfile}, clean_first=True)
         client.run("create . Pkg/0.1@user/testing --build=missing")
-        self.assertIn("Pkg/0.1@user/testing package(): Copied 1 '.md' files: LICENSE.md",
+        self.assertIn("Pkg/0.1@user/testing package(): Copied 1 '.md' file: LICENSE.md",
                       client.out)
         pkg_ref = PackageReference(ConanFileReference.loads("Pkg/0.1@user/testing"),
                                    "e6f2dac07251ad9958120a7f7c324366fb3b6f2a")
diff --git a/conans/test/functional/package_integrity_test.py b/conans/test/functional/package_integrity_test.py
new file mode 100644
index 000000000..75ec4dd6c
--- /dev/null
+++ b/conans/test/functional/package_integrity_test.py
@@ -0,0 +1,51 @@
+import os
+import unittest
+
+from conans.test.utils.tools import TestClient, TestServer
+from conans.model.ref import ConanFileReference, PackageReference
+from conans.test.utils.conanfile import TestConanFile
+from conans.util.files import save, set_dirty
+
+
+class PackageIngrityTest(unittest.TestCase):
+
+    def remove_locks_test(self):
+        client = TestClient()
+        client.save({"conanfile.py": str(TestConanFile())})
+        client.run("create . lasote/testing")
+        ref = ConanFileReference.loads("Hello/0.1@lasote/testing")
+        conan_folder = client.client_cache.conan(ref)
+        self.assertIn("locks", os.listdir(conan_folder))
+        self.assertTrue(os.path.exists(conan_folder + ".count"))
+        self.assertTrue(os.path.exists(conan_folder + ".count.lock"))
+        error = client.run("remove * --locks", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("ERROR: Specifying a pattern is not supported", client.out)
+        error = client.run("remove", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn('ERROR: Please specify a pattern to be removed ("*" for all)', client.out)
+        client.run("remove --locks")
+        self.assertNotIn("locks", os.listdir(conan_folder))
+        self.assertFalse(os.path.exists(conan_folder + ".count"))
+        self.assertFalse(os.path.exists(conan_folder + ".count.lock"))
+
+    def upload_dirty_test(self):
+        test_server = TestServer([], users={"lasote": "mypass"})
+        client = TestClient(servers={"default": test_server},
+                            users={"default": [("lasote", "mypass")]})
+        client.save({"conanfile.py": str(TestConanFile())})
+        client.run("export . lasote/testing")
+        ref = ConanFileReference.loads("Hello/0.1@lasote/testing")
+        pkg_ref = PackageReference(ref, "12345")
+        package_folder = client.client_cache.package(pkg_ref)
+        save(os.path.join(package_folder, "conaninfo.txt"), "")
+        save(os.path.join(package_folder, "conanmanifest.txt"), "")
+        set_dirty(package_folder)
+
+        error = client.run("upload * --all --confirm", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("ERROR: Package Hello/0.1@lasote/testing:12345 is corrupted, aborting upload", client.out)
+        self.assertIn("Remove it with 'conan remove Hello/0.1@lasote/testing -p=12345'", client.out)
+
+        client.run("remove Hello/0.1@lasote/testing -p=12345 -f")
+        client.run("upload * --all --confirm")
diff --git a/conans/test/integration/complete_test.py b/conans/test/integration/complete_test.py
index c2a0789d7..b4a599e74 100644
--- a/conans/test/integration/complete_test.py
+++ b/conans/test/integration/complete_test.py
@@ -26,7 +26,7 @@ class CompleteFlowTest(unittest.TestCase):
         self.client.run("export . lasote/stable")
         self.client.run("install %s --build missing" % str(conan_reference))
 
-        self.assertIn("Hello0/0.1@lasote/stable package(): Copied 1 '.h' files: helloHello0.h",
+        self.assertIn("Hello0/0.1@lasote/stable package(): Copied 1 '.h' file: helloHello0.h",
                       self.client.user_io.out)
         # Check compilation ok
         package_ids = self.client.paths.conan_packages(conan_reference)
diff --git a/conans/test/model/options_test.py b/conans/test/model/options_test.py
index bee18f0d5..c672bf204 100644
--- a/conans/test/model/options_test.py
+++ b/conans/test/model/options_test.py
@@ -18,6 +18,9 @@ class OptionsTest(unittest.TestCase):
         package_options.values = values
         self.sut = Options(package_options)
 
+    def test_int(self):
+        self.assertEqual(3, int(self.sut.optimized))
+
     def test_in(self):
         package_options = PackageOptions.loads("{static: [True, False]}")
         sut = Options(package_options)
diff --git a/conans/test/model/transitive_reqs_test.py b/conans/test/model/transitive_reqs_test.py
index 7780e2560..ee4560b7d 100644
--- a/conans/test/model/transitive_reqs_test.py
+++ b/conans/test/model/transitive_reqs_test.py
@@ -120,7 +120,7 @@ def _get_edges(graph):
 
 
 class MockRequireResolver(object):
-    def resolve(self, rquire, conanref):  # @UnusedVariable
+    def resolve(self, rquire, conanref, update):  # @UnusedVariable
         return
 
 
diff --git a/conans/test/model/version_ranges_test.py b/conans/test/model/version_ranges_test.py
index 71012b623..81dd6ff71 100644
--- a/conans/test/model/version_ranges_test.py
+++ b/conans/test/model/version_ranges_test.py
@@ -1,4 +1,5 @@
 import unittest
+
 from conans.test.utils.tools import TestBufferConanOutput
 from conans.paths import CONANFILE
 import os
@@ -171,7 +172,7 @@ class VersionRangesTest(unittest.TestCase):
         self.loader = ConanFileLoader(None, Settings.loads(""), Profile())
         self.retriever = Retriever(self.loader, self.output)
         self.remote_search = MockSearchRemote()
-        self.resolver = RequireResolver(self.output, self.retriever, self.remote_search, update=False)
+        self.resolver = RequireResolver(self.output, self.retriever, self.remote_search)
         self.builder = DepsGraphBuilder(self.retriever, self.output, self.loader, self.resolver)
 
         for v in ["0.1", "0.2", "0.3", "1.1", "1.1.2", "1.2.1", "2.1", "2.2.1"]:
diff --git a/conans/test/remove_subsetting_test.py b/conans/test/remove_subsetting_test.py
index da67fb02e..7fdf188bd 100644
--- a/conans/test/remove_subsetting_test.py
+++ b/conans/test/remove_subsetting_test.py
@@ -8,6 +8,7 @@ class RemoveSubsettingTest(unittest.TestCase):
 
     def remove_options_test(self):
         # https://github.com/conan-io/conan/issues/2327
+        # https://github.com/conan-io/conan/issues/2781
         client = TestClient()
         conanfile = """from conans import ConanFile
 class Pkg(ConanFile):
@@ -15,13 +16,19 @@ class Pkg(ConanFile):
     default_options = "opt1=True", "opt2=False"
     def config_options(self):
         del self.options.opt2
+    def build(self):
+        assert "opt2" not in self.options
+        self.options.opt2
 """
         client.save({"conanfile.py": conanfile})
         build_folder = os.path.join(client.current_folder, "build")
         mkdir(build_folder)
         client.current_folder = build_folder
         client.run("install ..")
-        client.run("build ..")
+        error = client.run("build ..", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("ConanException: 'options.opt2' doesn't exist", client.out)
+        self.assertIn("Possible options are ['opt1']", client.out)
 
     def remove_setting_test(self):
         # https://github.com/conan-io/conan/issues/2327
diff --git a/conans/test/util/action_recorder_test.py b/conans/test/util/action_recorder_test.py
index 0a93a1570..ac85817b5 100644
--- a/conans/test/util/action_recorder_test.py
+++ b/conans/test/util/action_recorder_test.py
@@ -19,9 +19,11 @@ class ActionRecorderTest(unittest.TestCase):
     def incomplete_process_test(self):
         tracer = ActionRecorder()
         tracer.recipe_install_error(self.ref1, INSTALL_ERROR_NETWORK, "SSL wtf", "http://drl.com")
-        install_info = tracer.get_install_info()
+        tracer.add_recipe_being_developed(self.ref1)
+        install_info = tracer.get_info()
         self.assertTrue(install_info["error"])
         self.assertEquals(install_info["installed"][0]["packages"], [])
+        self.assertEquals(install_info["installed"][0]["recipe"]["dependency"], False)
 
     def double_actions_test(self):
         tracer = ActionRecorder()
@@ -30,7 +32,7 @@ class ActionRecorderTest(unittest.TestCase):
         tracer.package_downloaded(self.ref_p1, "http://drl.com")
         tracer.package_fetched_from_cache(self.ref_p1)
 
-        install_info = tracer.get_install_info()
+        install_info = tracer.get_info()
         self.assertFalse(install_info["error"])
 
         first_installed = install_info["installed"][0]
@@ -55,12 +57,15 @@ class ActionRecorderTest(unittest.TestCase):
 
         tracer.recipe_fetched_from_cache(self.ref3)
         tracer.package_built(self.ref_p3)
+        tracer.add_recipe_being_developed(self.ref1)
 
-        install_info = tracer.get_install_info()
+        install_info = tracer.get_info()
         self.assertTrue(install_info["error"])
 
         first_installed = install_info["installed"][0]
+
         self.assertTrue(first_installed["recipe"]["cache"])
+        self.assertFalse(first_installed["recipe"]["dependency"])
         self.assertFalse(first_installed["recipe"]["downloaded"])
         self.assertIsNone(first_installed["recipe"]["error"])
         self.assertEquals(str(first_installed["recipe"]["id"]), "lib1/1.0@conan/stable")
@@ -73,6 +78,7 @@ class ActionRecorderTest(unittest.TestCase):
 
         second_installed = install_info["installed"][1]
         self.assertFalse(second_installed["recipe"]["cache"])
+        self.assertTrue(second_installed["recipe"]["dependency"])
         self.assertTrue(second_installed["recipe"]["downloaded"])
         self.assertIsNone(second_installed["recipe"]["error"])
         self.assertEquals(str(second_installed["recipe"]["id"]), "lib2/1.0@conan/stable")
@@ -85,6 +91,7 @@ class ActionRecorderTest(unittest.TestCase):
         self.assertEquals(str(second_installed["packages"][0]["id"]), "2")
 
         third_installed = install_info["installed"][2]
+        self.assertTrue(third_installed["recipe"]["dependency"])
         self.assertFalse(third_installed["packages"][0]["cache"])
         self.assertFalse(third_installed["packages"][0]["error"])
         self.assertTrue(third_installed["packages"][0]["built"])
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
index e860a4692..78da180b2 100644
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -685,3 +685,10 @@ class MyConan(ConanFile):
         tools.download("https://httpbin.org/basic-auth/user/passwd", dest,
                        headers={"Authorization": "Basic dXNlcjpwYXNzd2Q="}, overwrite=True)
 
+    def detect_windows_subsystem_test(self):
+        # Dont raise test
+        result = tools.os_info.detect_windows_subsystem()
+        if not tools.os_info.bash_path or platform.system() != "Windows":
+            self.assertEqual(None, result)
+        else:
+            self.assertEqual(str, type(result))