Repository: conan-io/conan

Issue:
Optimization on SCM ('scm_folder.txt' pointing to local folder) could lead to unexpected package
When the SCM feature is used, there is an optimization to avoid the git commands `git commit`,  `git push` in order to test new sources for `conan create` command: Conan creates a file 'scm_folder.txt' inside the cache that stores the path to the local sources and copies them from it instead of cloning the repository.

This optimization also applies for `conan install <...> --build=ref/v1@user/channel`, so this command will copy the sources from the local folder in order to build `ref` (but those sources could correspond to a different version, package,... to the one supposed to be at the moment when the recipe was created in the cache).



Discussion:

Patch:
diff --git a/conans/client/build/cmake.py b/conans/client/build/cmake.py
index bc73e7184..9bfcd123b 100644
--- a/conans/client/build/cmake.py
+++ b/conans/client/build/cmake.py
@@ -14,13 +14,14 @@ from conans.model.version import Version
 from conans.client.tools.oss import cpu_count, args_to_string
 from conans.util.config_parser import get_bool_from_text
 from conans.util.files import mkdir, get_abs_path, walk, decode_text
+from conans.util.env_reader import get_env
 
 
 class CMake(object):
 
     def __init__(self, conanfile, generator=None, cmake_system_name=True,
                  parallel=True, build_type=None, toolset=None, make_program=None,
-                 set_cmake_flags=False):
+                 set_cmake_flags=False, msbuild_verbosity=None, cmake_program=None):
         """
         :param conanfile: Conanfile instance
         :param generator: Generator name to use or none to autodetect
@@ -32,6 +33,8 @@ class CMake(object):
                 applies only to certain generators (e.g. Visual Studio)
         :param set_cmake_flags: whether or not to set CMake flags like CMAKE_CXX_FLAGS, CMAKE_C_FLAGS, etc.
                it's vital to set for certain projects (e.g. using CMAKE_SIZEOF_VOID_P or CMAKE_LIBRARY_ARCHITECTURE)
+        :param msbuild_verbosity: verbosity level for MSBuild (in case of Visual Studio generator)
+        :param cmake_program: Path to the custom cmake executable
         """
         if not isinstance(conanfile, ConanFile):
             raise ConanException("First argument of CMake() has to be ConanFile. Use CMake(self)")
@@ -39,6 +42,7 @@ class CMake(object):
         self._conanfile = conanfile
         self._settings = conanfile.settings
         self._build_type = build_type or conanfile.settings.get_safe("build_type")
+        self._cmake_program = os.getenv("CONAN_CMAKE_PROGRAM") or cmake_program or "cmake"
 
         self.generator = generator or get_generator(conanfile.settings)
         self.parallel = parallel
@@ -55,6 +59,7 @@ class CMake(object):
         self.definitions = builder.get_definitions()
         self.toolset = toolset or get_toolset(self._settings)
         self.build_dir = None
+        self.msbuild_verbosity = msbuild_verbosity or get_env("CONAN_MSBUILD_VERBOSITY", "minimal")
 
     @property
     def build_folder(self):
@@ -184,7 +189,7 @@ class CMake(object):
             pkg_env = {"PKG_CONFIG_PATH": self._conanfile.install_folder} if set_env else {}
 
         with tools.environment_append(pkg_env):
-            command = "cd %s && cmake %s" % (args_to_string([self.build_dir]), arg_list)
+            command = "cd %s && %s %s" % (args_to_string([self.build_dir]), self._cmake_program, arg_list)
             if platform.system() == "Windows" and self.generator == "MinGW Makefiles":
                 with tools.remove_from_path("sh"):
                     self._run(command)
@@ -202,8 +207,8 @@ class CMake(object):
         if target is not None:
             args = ["--target", target] + args
 
+        compiler_version = self._settings.get_safe("compiler.version")
         if self.generator and self.parallel:
-            compiler_version = self._settings.get_safe("compiler.version")
             if "Makefiles" in self.generator and "NMake" not in self.generator:
                 if "--" not in args:
                     args.append("--")
@@ -215,12 +220,19 @@ class CMake(object):
                 # Parallel for building projects in the solution
                 args.append("/m:%i" % cpu_count(output=self._conanfile.output))
 
+        if self.generator and self.msbuild_verbosity:
+            if "Visual Studio" in self.generator and \
+                    compiler_version and Version(compiler_version) >= "10":
+                if "--" not in args:
+                    args.append("--")
+                args.append("/verbosity:%s" % self.msbuild_verbosity)
+
         arg_list = join_arguments([
             args_to_string([build_dir]),
             self.build_config,
             args_to_string(args)
         ])
-        command = "cmake --build %s" % arg_list
+        command = "%s --build %s" % (self._cmake_program, arg_list)
         self._run(command)
 
     def install(self, args=None, build_dir=None):
@@ -233,12 +245,17 @@ class CMake(object):
                                  "defined" % cmake_install_prefix_var_name)
         self._build(args=args, build_dir=build_dir, target="install")
 
-    def test(self, args=None, build_dir=None, target=None):
+    def test(self, args=None, build_dir=None, target=None, output_on_failure=False):
         if not self._conanfile.should_test:
             return
         if not target:
             target = "RUN_TESTS" if self.is_multi_configuration else "test"
-        self._build(args=args, build_dir=build_dir, target=target)
+
+        env = {'CTEST_OUTPUT_ON_FAILURE': '1' if output_on_failure else '0'}
+        if self.parallel:
+            env['CTEST_PARALLEL_LEVEL'] = str(cpu_count(self._conanfile.output))
+        with tools.environment_append(env):
+            self._build(args=args, build_dir=build_dir, target=target)
 
     @property
     def verbose(self):
diff --git a/conans/client/build/msbuild.py b/conans/client/build/msbuild.py
index b5eb39f03..394c9cc90 100644
--- a/conans/client/build/msbuild.py
+++ b/conans/client/build/msbuild.py
@@ -30,14 +30,48 @@ class MSBuild(object):
     def build(self, project_file, targets=None, upgrade_project=True, build_type=None, arch=None,
               parallel=True, force_vcvars=False, toolset=None, platforms=None, use_env=True,
               vcvars_ver=None, winsdk_version=None, properties=None, output_binary_log=None,
-              property_file_name=None):
+              property_file_name=None, verbosity=None, definitions=None):
+        """
+        :param project_file: Path to the .sln file.
+        :param targets: List of targets to build.
+        :param upgrade_project: Will call devenv to upgrade the solution to your current Visual Studio.
+        :param build_type: Use a custom build type name instead of the default settings.build_type one.
+        :param arch: Use a custom architecture name instead of the settings.arch one.
+        It will be used to build the /p:Configuration= parameter of MSBuild.
+        It can be used as the key of the platforms parameter. E.g. arch="x86", platforms={"x86": "i386"}
+        :param parallel: Will use the configured number of cores in the conan.conf file or tools.cpu_count():
+        In the solution: Building the solution with the projects in parallel. (/m: parameter).
+        CL compiler: Building the sources in parallel. (/MP: compiler flag)
+        :param force_vcvars: Will ignore if the environment is already set for a different Visual Studio version.
+        :param toolset: Specify a toolset. Will append a /p:PlatformToolset option.
+        :param platforms: Dictionary with the mapping of archs/platforms from Conan naming to another one.
+        It is useful for Visual Studio solutions that have a different naming in architectures.
+        Example: platforms={"x86":"Win32"} (Visual solution uses "Win32" instead of "x86").
+        This dictionary will update the default one:
+        msvc_arch = {'x86': 'x86', 'x86_64': 'x64', 'armv7': 'ARM', 'armv8': 'ARM64'}
+        :param use_env: Applies the argument /p:UseEnv=true to the MSBuild call.
+        :param vcvars_ver: Specifies the Visual Studio compiler toolset to use.
+        :param winsdk_version: Specifies the version of the Windows SDK to use.
+        :param properties: Dictionary with new properties, for each element in the dictionary {name: value}
+        it will append a /p:name="value" option.
+        :param output_binary_log: If set to True then MSBuild will output a binary log file called msbuild.binlog in
+        the working directory. It can also be used to set the name of log file like this
+        output_binary_log="my_log.binlog".
+        This parameter is only supported starting from MSBuild version 15.3 and onwards.
+        :param property_file_name: When None it will generate a file named conan_build.props.
+        You can specify a different name for the generated properties file.
+        :param verbosity: Specifies verbosity level (/verbosity: parameter)
+        :param definitions: Dictionary with additional compiler definitions to be applied during the build.
+        Use value of None to set compiler definition with no value.
+        :return: status code of the MSBuild command invocation
+        """
 
         property_file_name = property_file_name or "conan_build.props"
         self.build_env.parallel = parallel
 
         with tools.environment_append(self.build_env.vars):
             # Path for custom properties file
-            props_file_contents = self._get_props_file_contents()
+            props_file_contents = self._get_props_file_contents(definitions)
             save(property_file_name, props_file_contents)
             vcvars = vcvars_command(self._conanfile.settings, force=force_vcvars,
                                     vcvars_ver=vcvars_ver, winsdk_version=winsdk_version)
@@ -45,13 +79,14 @@ class MSBuild(object):
                                        targets=targets, upgrade_project=upgrade_project,
                                        build_type=build_type, arch=arch, parallel=parallel,
                                        toolset=toolset, platforms=platforms,
-                                       use_env=use_env, properties=properties, output_binary_log=output_binary_log)
+                                       use_env=use_env, properties=properties, output_binary_log=output_binary_log,
+                                       verbosity=verbosity)
             command = "%s && %s" % (vcvars, command)
             return self._conanfile.run(command)
 
     def get_command(self, project_file, props_file_path=None, targets=None, upgrade_project=True,
                     build_type=None, arch=None, parallel=True, toolset=None, platforms=None,
-                    use_env=False, properties=None, output_binary_log=None):
+                    use_env=False, properties=None, output_binary_log=None, verbosity=None):
 
         targets = targets or []
         properties = properties or {}
@@ -64,6 +99,7 @@ class MSBuild(object):
 
         build_type = build_type or self._settings.get_safe("build_type")
         arch = arch or self._settings.get_safe("arch")
+        verbosity = verbosity or get_env("CONAN_MSBUILD_VERBOSITY", "minimal")
         if not build_type:
             raise ConanException("Cannot build_sln_command, build_type not defined")
         if not arch:
@@ -116,6 +152,9 @@ class MSBuild(object):
         if toolset:
             command.append('/p:PlatformToolset="%s"' % toolset)
 
+        if verbosity:
+            command.append('/verbosity:%s' % verbosity)
+
         if props_file_path:
             command.append('/p:ForceImportBeforeCppTargets="%s"' % props_file_path)
 
@@ -124,7 +163,11 @@ class MSBuild(object):
 
         return " ".join(command)
 
-    def _get_props_file_contents(self):
+    def _get_props_file_contents(self, definitions=None):
+
+        def format_macro(name, value):
+            return "%s=%s" % (name, value) if value else name
+
         # how to specify runtime in command line:
         # https://stackoverflow.com/questions/38840332/msbuild-overrides-properties-while-building-vc-project
         runtime_library = {"MT": "MultiThreaded",
@@ -140,23 +183,31 @@ class MSBuild(object):
             flags = vs_build_type_flags(self._settings, with_flags=False)
             flags.append(vs_std_cpp(self._settings))
 
-        flags_str = " ".join(list(filter(None, flags))) # Removes empty and None elements
+        if definitions:
+            definitions = ";".join([format_macro(name, definitions[name]) for name in definitions])
+
+        flags_str = " ".join(list(filter(None, flags)))  # Removes empty and None elements
         additional_node = "<AdditionalOptions>" \
                           "{} %(AdditionalOptions)" \
                           "</AdditionalOptions>".format(flags_str) if flags_str else ""
         runtime_node = "<RuntimeLibrary>" \
                        "{}" \
                        "</RuntimeLibrary>".format(runtime_library) if runtime_library else ""
+        definitions_node = "<PreprocessorDefinitions>" \
+                           "{};%(PreprocessorDefinitions)" \
+                           "</PreprocessorDefinitions>".format(definitions) if definitions else ""
         template = """<?xml version="1.0" encoding="utf-8"?>
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemDefinitionGroup>
     <ClCompile>
       {runtime_node}
       {additional_node}
+      {definitions_node}
     </ClCompile>
   </ItemDefinitionGroup>
 </Project>""".format(**{"runtime_node": runtime_node,
-                        "additional_node": additional_node})
+                        "additional_node": additional_node,
+                        "definitions_node": definitions_node})
         return template
 
     @staticmethod
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 581a480ea..8d8c75ec8 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -98,13 +98,14 @@ request_timeout = 60                  # environment CONAN_REQUEST_TIMEOUT (secon
 # recipe_linter = False               # environment CONAN_RECIPE_LINTER
 # read_only_cache = True              # environment CONAN_READ_ONLY_CACHE
 # pylintrc = path/to/pylintrc_file    # environment CONAN_PYLINTRC
-# cache_no_locks = True               # Disable locking mechanism of local cache
+# cache_no_locks = True               # environment CONAN_CACHE_NO_LOCKS
 # user_home_short = your_path         # environment CONAN_USER_HOME_SHORT
 # use_always_short_paths = False      # environment CONAN_USE_ALWAYS_SHORT_PATHS
 # skip_vs_projects_upgrade = False    # environment CONAN_SKIP_VS_PROJECTS_UPGRADE
 # non_interactive = False             # environment CONAN_NON_INTERACTIVE
 
 # conan_make_program = make           # environment CONAN_MAKE_PROGRAM (overrides the make program used in AutoToolsBuildEnvironment.make)
+# conan_cmake_program = cmake         # environment CONAN_CMAKE_PROGRAM (overrides the make program used in CMake.cmake_program)
 
 # cmake_generator                     # environment CONAN_CMAKE_GENERATOR
 # http://www.vtk.org/Wiki/CMake_Cross_Compiling
@@ -117,6 +118,8 @@ request_timeout = 60                  # environment CONAN_REQUEST_TIMEOUT (secon
 # cmake_find_root_path_mode_library   # environment CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
 # cmake_find_root_path_mode_include   # environment CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
 
+# msbuild_verbosity = minimal         # environment CONAN_MSBUILD_VERBOSITY
+
 # cpu_count = 1             # environment CONAN_CPU_COUNT
 
 # Change the default location for building test packages to a temporary folder
@@ -168,6 +171,7 @@ class ConanClientConfigParser(ConfigParser, object):
                "CONAN_COMPRESSION_LEVEL": self._env_c("general.compression_level", "CONAN_COMPRESSION_LEVEL", "9"),
                "CONAN_NON_INTERACTIVE": self._env_c("general.non_interactive", "CONAN_NON_INTERACTIVE", "False"),
                "CONAN_PYLINTRC": self._env_c("general.pylintrc", "CONAN_PYLINTRC", None),
+               "CONAN_CACHE_NO_LOCKS": self._env_c("general.cache_no_locks", "CONAN_CACHE_NO_LOCKS", "False"),
                "CONAN_PYLINT_WERR": self._env_c("general.pylint_werr", "CONAN_PYLINT_WERR", None),
                "CONAN_SYSREQUIRES_SUDO": self._env_c("general.sysrequires_sudo", "CONAN_SYSREQUIRES_SUDO", "False"),
                "CONAN_SYSREQUIRES_MODE": self._env_c("general.sysrequires_mode", "CONAN_SYSREQUIRES_MODE", "enabled"),
@@ -202,9 +206,13 @@ class ConanClientConfigParser(ConfigParser, object):
 
                "CONAN_BASH_PATH": self._env_c("general.bash_path", "CONAN_BASH_PATH", None),
                "CONAN_MAKE_PROGRAM": self._env_c("general.conan_make_program", "CONAN_MAKE_PROGRAM", None),
+               "CONAN_CMAKE_PROGRAM": self._env_c("general.conan_cmake_program", "CONAN_CMAKE_PROGRAM", None),
                "CONAN_TEMP_TEST_FOLDER": self._env_c("general.temp_test_folder", "CONAN_TEMP_TEST_FOLDER", "False"),
                "CONAN_SKIP_VS_PROJECTS_UPGRADE": self._env_c("general.skip_vs_projects_upgrade", "CONAN_SKIP_VS_PROJECTS_UPGRADE", "False"),
                "CONAN_HOOKS": self._env_c("hooks", "CONAN_HOOKS", None),
+               "CONAN_MSBUILD_VERBOSITY": self._env_c("general.msbuild_verbosity",
+                                                      "CONAN_MSBUILD_VERBOSITY",
+                                                      None)
                }
 
         # Filter None values
@@ -315,7 +323,7 @@ class ConanClientConfigParser(ConfigParser, object):
     @property
     def cache_no_locks(self):
         try:
-            return self.get_item("general.cache_no_locks")
+            return get_env("CONAN_CACHE_NO_LOCKS", False)
         except ConanException:
             return False
 
diff --git a/conans/client/conf/detect.py b/conans/client/conf/detect.py
index 612d8dbf3..ccc5734e2 100644
--- a/conans/client/conf/detect.py
+++ b/conans/client/conf/detect.py
@@ -5,6 +5,7 @@ from subprocess import PIPE, Popen, STDOUT
 
 from conans.client.output import Color
 from conans.client.tools.win import latest_visual_studio_version_installed
+from conans.client.tools import OSInfo
 from conans.model.version import Version
 
 
@@ -168,16 +169,11 @@ adjusting 'compiler.libcxx=libstdc++11'
 
 
 def detected_os():
-    result = platform.system()
-    if result == "Darwin":
+    if OSInfo().is_macos:
         return "Macos"
-    if result.startswith("CYGWIN"):
+    if OSInfo().is_windows:
         return "Windows"
-    if result.startswith("MINGW32_NT") or result.startswith("MINGW64_NT"):
-        return "Windows"
-    if result.startswith("MSYS_NT"):
-        return "Windows"
-    return result
+    return platform.system()
 
 
 def _detect_os_arch(result, output):
diff --git a/conans/client/generators/cmake_common.py b/conans/client/generators/cmake_common.py
index 1f31b9ced..01d58a36b 100644
--- a/conans/client/generators/cmake_common.py
+++ b/conans/client/generators/cmake_common.py
@@ -397,6 +397,10 @@ function(check_compiler_version)
 endfunction()
 
 function(conan_check_compiler)
+    if(CONAN_DISABLE_CHECK_COMPILER)
+        conan_message(STATUS "WARN: Disabled conan compiler checks")
+        return()
+    endif()
     if(NOT DEFINED CMAKE_CXX_COMPILER_ID)
         if(DEFINED CMAKE_C_COMPILER_ID)
             conan_message(STATUS "This project seems to be plain C, using '${CMAKE_C_COMPILER_ID}' compiler")
@@ -406,10 +410,6 @@ function(conan_check_compiler)
             message(FATAL_ERROR "This project seems to be plain C, but no compiler defined")
         endif()
     endif()
-    if(CONAN_DISABLE_CHECK_COMPILER)
-        conan_message(STATUS "WARN: Disabled conan compiler checks")
-        return()
-    endif()
     if(NOT CMAKE_CXX_COMPILER_ID AND NOT CMAKE_C_COMPILER_ID)
         # This use case happens when compiler is not identified by CMake, but the compilers are there and work
         conan_message(STATUS "*** WARN: CMake was not able to identify a C or C++ compiler ***")
diff --git a/conans/client/graph/build_mode.py b/conans/client/graph/build_mode.py
index 280607990..e4f9f9fe4 100644
--- a/conans/client/graph/build_mode.py
+++ b/conans/client/graph/build_mode.py
@@ -39,7 +39,7 @@ class BuildMode(object):
                 raise ConanException("--build=never not compatible with other options")
         self._unused_patterns = list(self.patterns)
 
-    def forced(self, conan_file, reference):
+    def forced(self, conan_file, ref):
         if self.never:
             return False
         if self.all:
@@ -50,7 +50,7 @@ class BuildMode(object):
                                    "build_policy='always'")
             return True
 
-        ref = reference.name
+        ref = ref.name
         # Patterns to match, if package matches pattern, build is forced
         for pattern in self.patterns:
             if fnmatch.fnmatch(ref, pattern):
diff --git a/conans/client/rest/rest_client_common.py b/conans/client/rest/rest_client_common.py
index a6631e288..8cace86ca 100644
--- a/conans/client/rest/rest_client_common.py
+++ b/conans/client/rest/rest_client_common.py
@@ -156,7 +156,16 @@ class RestCommonMethods(object):
             response.charset = "utf-8"  # To be able to access ret.text (ret.content are bytes)
             raise get_exception_from_error(response.status_code)(response.text)
 
-        result = json.loads(decode_text(response.content))
+        content = decode_text(response.content)
+        content_type = response.headers.get("Content-Type")
+        if content_type != 'application/json':
+            raise ConanException("%s\n\nResponse from remote is not json, but '%s'"
+                                 % (content, content_type))
+
+        try:  # This can fail, if some proxy returns 200 and an html message
+            result = json.loads(content)
+        except Exception:
+            raise ConanException("Remote responded with broken json: %s" % content)
         if not isinstance(result, dict):
             raise ConanException("Unexpected server response %s" % result)
         return result
diff --git a/conans/client/rest/uploader_downloader.py b/conans/client/rest/uploader_downloader.py
index 39af8fa7f..c67c03cec 100644
--- a/conans/client/rest/uploader_downloader.py
+++ b/conans/client/rest/uploader_downloader.py
@@ -19,8 +19,11 @@ class Uploader(object):
         self.verify = verify
 
     def upload(self, url, abs_path, auth=None, dedup=False, retry=1, retry_wait=0, headers=None):
+        # Send always the header with the Sha1
+        headers = headers or {}
+        headers["X-Checksum-Sha1"] = sha1sum(abs_path)
         if dedup:
-            dedup_headers = {"X-Checksum-Deploy": "true", "X-Checksum-Sha1": sha1sum(abs_path)}
+            dedup_headers = {"X-Checksum-Deploy": "true"}
             if headers:
                 dedup_headers.update(headers)
             response = self.requester.put(url, data="", verify=self.verify, headers=dedup_headers,
@@ -28,7 +31,6 @@ class Uploader(object):
             if response.status_code == 201:  # Artifactory returns 201 if the file is there
                 return response
 
-        headers = headers or {}
         self.output.info("")
         # Actual transfer of the real content
         it = load_in_chunks(abs_path, self.chunk_size)
diff --git a/conans/client/source.py b/conans/client/source.py
index 751b138fc..f9d2ac13a 100644
--- a/conans/client/source.py
+++ b/conans/client/source.py
@@ -196,7 +196,7 @@ def _run_scm(conanfile, src_folder, local_sources_path, output, cache):
     else:
         # In user space, if revision="auto", then copy
         captured = scm_data.capture_origin or scm_data.capture_revision
-    local_sources_path = local_sources_path if captured else None
+    local_sources_path = local_sources_path if captured and conanfile.develop else None
 
     if local_sources_path:
         excluded = SCM(scm_data, local_sources_path, output).excluded_files
diff --git a/conans/client/tools/oss.py b/conans/client/tools/oss.py
index 909025be8..2c7106e70 100644
--- a/conans/client/tools/oss.py
+++ b/conans/client/tools/oss.py
@@ -85,24 +85,21 @@ class OSInfo(object):
     """
 
     def __init__(self):
+        system = platform.system()
         self.os_version = None
         self.os_version_name = None
-        self.is_linux = platform.system() == "Linux"
+        self.is_linux = system == "Linux"
         self.linux_distro = None
-        self.is_windows = platform.system() == "Windows"
-        self.is_macos = platform.system() == "Darwin"
-        self.is_freebsd = platform.system() == "FreeBSD"
-        self.is_solaris = platform.system() == "SunOS"
+        self.is_msys = system.startswith("MING") or system.startswith("MSYS_NT")
+        self.is_cygwin = system.startswith("CYGWIN_NT")
+        self.is_windows = system == "Windows" or self.is_msys or self.is_cygwin
+        self.is_macos = system == "Darwin"
+        self.is_freebsd = system == "FreeBSD"
+        self.is_solaris = system == "SunOS"
         self.is_posix = os.pathsep == ':'
 
         if self.is_linux:
-            import distro
-            self.linux_distro = distro.id()
-            self.os_version = Version(distro.version())
-            version_name = distro.codename()
-            self.os_version_name = version_name if version_name != "n/a" else ""
-            if not self.os_version_name and self.linux_distro == "debian":
-                self.os_version_name = self.get_debian_version_name(self.os_version)
+            self._get_linux_distro_info()
         elif self.is_windows:
             self.os_version = self.get_win_os_version()
             self.os_version_name = self.get_win_version_name(self.os_version)
@@ -116,6 +113,15 @@ class OSInfo(object):
             self.os_version = Version(platform.release())
             self.os_version_name = self.get_solaris_version_name(self.os_version)
 
+    def _get_linux_distro_info(self):
+        import distro
+        self.linux_distro = distro.id()
+        self.os_version = Version(distro.version())
+        version_name = distro.codename()
+        self.os_version_name = version_name if version_name != "n/a" else ""
+        if not self.os_version_name and self.linux_distro == "debian":
+            self.os_version_name = self.get_debian_version_name(self.os_version)
+
     @property
     def with_apt(self):
         return self.is_linux and self.linux_distro in \
@@ -143,7 +149,7 @@ class OSInfo(object):
         if "opensuse" in self.linux_distro or "sles" in self.linux_distro:
             return True
         return False
-    
+
     @staticmethod
     def get_win_os_version():
         """
@@ -264,7 +270,7 @@ class OSInfo(object):
     @staticmethod
     def uname(options=None):
         options = " %s" % options if options else ""
-        if platform.system() != "Windows":
+        if not OSInfo().is_windows:
             raise ConanException("Command only for Windows operating system")
         custom_bash_path = OSInfo.bash_path()
         if not custom_bash_path:
@@ -291,7 +297,7 @@ class OSInfo(object):
         if "cygwin" in output:
             return CYGWIN
         elif "msys" in output or "mingw" in output:
-            output = OSInfo.uname("-or")
+            output = OSInfo.uname("-r")
             if output.startswith("2"):
                 return MSYS2
             elif output.startswith("1"):
@@ -305,7 +311,6 @@ class OSInfo(object):
 
 
 def cross_building(settings, self_os=None, self_arch=None):
-
     ret = get_cross_building_settings(settings, self_os, self_arch)
     build_os, build_arch, host_os, host_arch = ret
 
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
index d73f566c2..7ecb7a7b1 100644
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -120,7 +120,7 @@ def msvc_build_command(settings, sln_path, targets=None, upgrade_project=True, b
 @deprecation.deprecated(deprecated_in="1.2", removed_in="2.0",
                         details="Use the MSBuild() build helper instead")
 def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, build_type=None,
-                      arch=None, parallel=True, toolset=None, platforms=None, output=None):
+                      arch=None, parallel=True, toolset=None, platforms=None, output=None, verbosity=None, definitions=None):
     """
     Use example:
         build_command = build_sln_command(self.settings, "myfile.sln", targets=["SDL2_image"])
@@ -133,7 +133,7 @@ def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, bu
     tmp._output = output
 
     # Generate the properties file
-    props_file_contents = tmp._get_props_file_contents()
+    props_file_contents = tmp._get_props_file_contents(definitions)
     tmp_path = os.path.join(mkdir_tmp(), ".conan_properties")
     save(tmp_path, props_file_contents)
 
@@ -141,7 +141,8 @@ def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, bu
     command = tmp.get_command(sln_path, tmp_path,
                               targets=targets, upgrade_project=upgrade_project,
                               build_type=build_type, arch=arch, parallel=parallel,
-                              toolset=toolset, platforms=platforms, use_env=False)
+                              toolset=toolset, platforms=platforms, use_env=False,
+                              verbosity=verbosity)
 
     return command
 
diff --git a/conans/server/rest/multipart_encoder.py b/conans/server/rest/multipart_encoder.py
index c2d3e0f8f..6a2dbdc76 100644
--- a/conans/server/rest/multipart_encoder.py
+++ b/conans/server/rest/multipart_encoder.py
@@ -77,7 +77,7 @@ def read_in_chunks(file_object, chunk_size=1024):
 #             yield buf1
 #         else:
         yield buf1
-        #yield buf2
+        # yield buf2
 
 
 if __name__ == "__main__":
diff --git a/conans/server/rest/server.py b/conans/server/rest/server.py
index 2819202c4..5e726df32 100644
--- a/conans/server/rest/server.py
+++ b/conans/server/rest/server.py
@@ -1,6 +1,5 @@
 import bottle
 
-from conans.model.version import Version
 from conans.server.rest.api_v1 import ApiV1
 from conans.server.rest.api_v2 import ApiV2
 
diff --git a/conans/server/service/mime.py b/conans/server/service/mime.py
index 0e7a4ee89..4a503fb41 100644
--- a/conans/server/service/mime.py
+++ b/conans/server/service/mime.py
@@ -6,4 +6,4 @@ def get_mime_type(filepath):
     else:
         mimetype = "auto"
 
-    return mimetype
\ No newline at end of file
+    return mimetype


Test patch:
diff --git a/conans/test/functional/build_helpers/msbuild_test.py b/conans/test/functional/build_helpers/msbuild_test.py
index fff9c8007..b6ca7181e 100644
--- a/conans/test/functional/build_helpers/msbuild_test.py
+++ b/conans/test/functional/build_helpers/msbuild_test.py
@@ -28,7 +28,7 @@ class HelloConan(ConanFile):
 
     def build(self):
         msbuild = MSBuild(self)
-        msbuild.build("MyProject.sln")
+        msbuild.build("MyProject.sln", verbosity="normal")
 
     def package(self):
         self.copy(pattern="*.exe")
@@ -45,7 +45,7 @@ class HelloConan(ConanFile):
                    'compiler="Visual Studio" -s compiler.version=14', assert_error=True)
         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' file: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.out)
 
         files = get_vs_project_files()
         files[CONANFILE] = conan_build_vs
@@ -57,41 +57,41 @@ class HelloConan(ConanFile):
         client.save(files, clean_first=True)
         client.run("create . Hello/1.2.1@lasote/stable --build")
         self.assertNotIn("devenv", client.user_io.out)
-        self.assertIn("Skipped sln project upgrade", client.user_io.out)
+        self.assertIn("Skipped sln project upgrade", client.out)
 
         # Try with x86_64
         client.save(files)
         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' file: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.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' file: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.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' file: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.out)
 
         # Try with a custom property file name
-        files[CONANFILE] = conan_build_vs.replace('msbuild.build("MyProject.sln")',
-                                                  'msbuild.build("MyProject.sln", '
-                                                  'property_file_name="myprops.props")')
+        files[CONANFILE] = conan_build_vs.replace(
+                'msbuild.build("MyProject.sln", verbosity="normal")',
+                'msbuild.build("MyProject.sln", verbosity="normal", property_file_name="mp.props")')
         client.save(files, clean_first=True)
         client.run("create . 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' file: MyProject.exe", client.user_io.out)
+        self.assertIn("Copied 1 '.exe' file: MyProject.exe", client.out)
         full_ref = "Hello/1.2.1@lasote/stable:b786e9ece960c3a76378ca4d5b0d0e922f4cedc1"
         pref = PackageReference.loads(full_ref)
         build_folder = client.cache.build(pref)
-        self.assertTrue(os.path.exists(os.path.join(build_folder, "myprops.props")))
+        self.assertTrue(os.path.exists(os.path.join(build_folder, "mp.props")))
 
     @unittest.skipUnless(platform.system() == "Windows", "Requires MSBuild")
     def reuse_msbuild_object_test(self):
diff --git a/conans/test/functional/command/remote_verify_ssl_test.py b/conans/test/functional/command/remote_verify_ssl_test.py
index 0ce3500be..f6f842408 100644
--- a/conans/test/functional/command/remote_verify_ssl_test.py
+++ b/conans/test/functional/command/remote_verify_ssl_test.py
@@ -7,6 +7,7 @@ from conans.test.utils.tools import TestClient
 resp = Response()
 resp._content = b'{"results": []}'
 resp.status_code = 200
+resp.headers = {"Content-Type": "application/json"}
 
 
 class RequesterMockTrue(object):
@@ -54,4 +55,3 @@ class VerifySSLTest(unittest.TestCase):
         self.client = TestClient(requester_class=RequesterMockFalse)
         self.client.run("remote add myremote https://localhost False")
         self.client.run("search op* -r myremote")
-
diff --git a/conans/test/functional/old/cmake_skip_rpath_test.py b/conans/test/functional/generators/cmake_skip_rpath_test.py
similarity index 100%
rename from conans/test/functional/old/cmake_skip_rpath_test.py
rename to conans/test/functional/generators/cmake_skip_rpath_test.py
diff --git a/conans/test/functional/generators/cmake_test.py b/conans/test/functional/generators/cmake_test.py
index 7d8c5cfdc..1e19c5c37 100644
--- a/conans/test/functional/generators/cmake_test.py
+++ b/conans/test/functional/generators/cmake_test.py
@@ -9,6 +9,32 @@ from conans.test.utils.tools import TestClient
 
 class CMakeGeneratorTest(unittest.TestCase):
 
+    def test_no_check_compiler(self):
+        # https://github.com/conan-io/conan/issues/4268
+        file_content = '''from conans import ConanFile, CMake
+
+class ConanFileToolsTest(ConanFile):
+    generators = "cmake"
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+    '''
+
+        cmakelists = '''cmake_minimum_required(VERSION 2.8)
+PROJECT(conanzlib LANGUAGES NONE)
+set(CONAN_DISABLE_CHECK_COMPILER TRUE)
+
+include(conanbuildinfo.cmake)
+CONAN_BASIC_SETUP()
+'''
+        client = TestClient()
+        client.save({"conanfile.py": file_content,
+                     "CMakeLists.txt": cmakelists})
+
+        client.run('install .')
+        client.run('build .')
+
     @attr('slow')
     def no_output_test(self):
         client = TestClient()
diff --git a/conans/test/functional/old/download_retries_test.py b/conans/test/functional/old/download_retries_test.py
index 216ea5189..5be5feb21 100644
--- a/conans/test/functional/old/download_retries_test.py
+++ b/conans/test/functional/old/download_retries_test.py
@@ -38,7 +38,7 @@ class MyConanfile(ConanFile):
             ok = None
             status_code = None
             charset = None
-            headers = {}
+            headers = {"Content-Type": "application/json"}
 
             def __init__(self, ok, status_code):
                 self.ok = ok
diff --git a/conans/test/functional/old/libcxx_setting_test.py b/conans/test/functional/old/libcxx_setting_test.py
index ed8ebc4a0..20f020379 100644
--- a/conans/test/functional/old/libcxx_setting_test.py
+++ b/conans/test/functional/old/libcxx_setting_test.py
@@ -39,15 +39,9 @@ endforeach()
 '''
 
 
-def nowintest(func):
-    if platform.system() == "Windows":
-        func.__test__ = False
-    return func
-
-
 class LibcxxSettingTest(unittest.TestCase):
 
-    @nowintest
+    @unittest.skipIf(platform.system() == "Windows", "Not in Windows")
     def test_declared_stdlib_and_passed(self):
         client = TestClient()
         client.save({"conanfile.py": file_content,
diff --git a/conans/test/functional/remote/server_error_test.py b/conans/test/functional/remote/server_error_test.py
new file mode 100644
index 000000000..9ae0b47de
--- /dev/null
+++ b/conans/test/functional/remote/server_error_test.py
@@ -0,0 +1,63 @@
+import unittest
+
+
+from conans.test.utils.tools import TestClient, TestServer
+from collections import namedtuple
+
+
+class Error200NoJson(unittest.TestCase):
+
+    def test_error_no_json(self):
+        class RequesterMock(object):
+            def __init__(self, *args, **kwargs):
+                pass
+
+            def get(self, *args, **kwargs):  # @UnusedVariable
+                # Response must be binary, it is decoded in RestClientCommon
+                return namedtuple("Response", "status_code headers content")(200, {}, b'<>')
+
+        # https://github.com/conan-io/conan/issues/3432
+        client = TestClient(servers={"default": TestServer()},
+                            requester_class=RequesterMock,
+                            users={"default": [("lasote", "mypass")]})
+
+        client.run("install pkg/ref@user/testing", assert_error=True)
+        self.assertIn("ERROR: <>", client.out)
+        self.assertIn("Response from remote is not json, but 'None'", client.out)
+
+    def test_error_broken_json(self):
+        class RequesterMock(object):
+            def __init__(self, *args, **kwargs):
+                pass
+
+            def get(self, *args, **kwargs):  # @UnusedVariable
+                # Response must be binary, it is decoded in RestClientCommon
+                headers = {"Content-Type": "application/json"}
+                return namedtuple("Response", "status_code headers content")(200, headers, b'<>')
+
+        # https://github.com/conan-io/conan/issues/3432
+        client = TestClient(servers={"default": TestServer()},
+                            requester_class=RequesterMock,
+                            users={"default": [("lasote", "mypass")]})
+
+        client.run("install pkg/ref@user/testing", assert_error=True)
+        self.assertIn("Remote responded with broken json: <>", client.out)
+
+    def test_error_json(self):
+        class RequesterMock(object):
+            def __init__(self, *args, **kwargs):
+                pass
+
+            def get(self, *args, **kwargs):  # @UnusedVariable
+                # Response must be binary, it is decoded in RestClientCommon
+                headers = {"Content-Type": "application/json"}
+                return namedtuple("Response", "status_code headers content")(200, headers,
+                                                                             b'[1, 2, 3]')
+
+        # https://github.com/conan-io/conan/issues/3432
+        client = TestClient(servers={"default": TestServer()},
+                            requester_class=RequesterMock,
+                            users={"default": [("lasote", "mypass")]})
+
+        client.run("install pkg/ref@user/testing", assert_error=True)
+        self.assertIn("ERROR: Unexpected server response [1, 2, 3]", client.out)
diff --git a/conans/test/functional/scm/test_local_modified.py b/conans/test/functional/scm/test_local_modified.py
new file mode 100644
index 000000000..99c4a0e4c
--- /dev/null
+++ b/conans/test/functional/scm/test_local_modified.py
@@ -0,0 +1,57 @@
+# coding=utf-8
+
+import textwrap
+import unittest
+
+from conans.model.ref import ConanFileReference
+from conans.test.utils.tools import TestClient, create_local_git_repo
+
+
+class SCMFolderObsoleteTest(unittest.TestCase):
+    conanfile = textwrap.dedent("""\
+        from conans import ConanFile, tools
+
+        class Pkg(ConanFile):
+            scm = {"type": "git",
+                   "url": "auto",
+                   "revision": "auto"}
+
+            def build(self):
+                content = tools.load("file.txt")
+                self.output.info(">>>> I'm {}/{}@{}/{}".format(self.name, self.version,
+                                                               self.user, self.channel))
+                self.output.info(">>>> content: {} ".format(content))
+        """)
+
+    def setUp(self):
+        self.reference = "pkg/v1@user/channel"
+        self.t = TestClient(path_with_spaces=False)
+
+        # Create pkg/v1
+        url, _ = create_local_git_repo(files={'conanfile.py': self.conanfile,
+                                              'file.txt': self.reference},
+                                       folder=self.t.current_folder)
+        self.t.runner('git remote add origin {}'.format(url), cwd=self.t.current_folder)
+        self.t.run("create . {}".format(self.reference))
+        self.assertIn(">>>> I'm {}".format(self.reference), self.t.out)
+        self.assertIn(">>>> content: {}".format(self.reference), self.t.out)
+
+        # Change something in the local folder
+        self.new_content = "Updated in the local folder!"
+        self.t.save({'file.txt': self.new_content})
+
+    def test_create_workflow(self):
+        """ Use the 'create' command, local changes are reflected in the cache """
+        self.t.run("create . {}".format(self.reference))
+        self.assertIn(">>>> I'm {}".format(self.reference), self.t.out)
+        self.assertIn(">>>> content: {}".format(self.new_content), self.t.out)
+
+    def test_install_workflow(self):
+        """ Using the install command, it won't be taken into account """
+        t2 = TestClient(base_folder=self.t.base_folder)
+        t2.save({'conanfile.txt': "[requires]\n{}".format(self.reference)})
+        ref = ConanFileReference.loads(self.reference)
+        t2.run("install . --build={}".format(ref.name))
+        self.assertNotIn(self.new_content, t2.out)
+        self.assertIn(">>>> I'm {}".format(self.reference), self.t.out)
+        self.assertIn(">>>> content: {}".format(self.reference), self.t.out)
diff --git a/conans/test/unittests/client/build/cmake_test.py b/conans/test/unittests/client/build/cmake_test.py
index f2ff1b0c4..635607dcb 100644
--- a/conans/test/unittests/client/build/cmake_test.py
+++ b/conans/test/unittests/client/build/cmake_test.py
@@ -1160,3 +1160,74 @@ build_type: [ Release]
             vcvars_mock.__exit__ = mock.MagicMock(return_value=None)
             cmake.build()
             self.assertTrue(vcvars_mock.called, "vcvars weren't called")
+
+    def test_cmake_program(self):
+        conanfile = ConanFileMock()
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "14"
+        conanfile.settings = settings
+
+        cmake = CMake(conanfile, parallel=False)
+        cmake.build()
+        self.assertEqual("cmake --build %s" % CMakeTest.scape("."), conanfile.command)
+
+        cmake = CMake(conanfile, cmake_program="use_another_cmake", parallel=False)
+        cmake.build()
+        self.assertEqual("use_another_cmake --build %s" % CMakeTest.scape("."), conanfile.command)
+
+        with tools.environment_append({"CONAN_CMAKE_PROGRAM": "my_custom_cmake"}):
+            cmake = CMake(conanfile, parallel=False)
+            cmake.build()
+            self.assertEqual("my_custom_cmake --build %s"  % CMakeTest.scape("."), conanfile.command)
+
+        with tools.environment_append({"CONAN_CMAKE_PROGRAM": "cmake_from_environment_has_priority"}):
+            cmake = CMake(conanfile, cmake_program="but_not_cmake_from_the_ctor", parallel=False)
+            cmake.build()
+            self.assertEqual("cmake_from_environment_has_priority --build %s" % CMakeTest.scape("."), conanfile.command)
+
+    def test_msbuild_verbosity(self):
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "10"
+        settings.compiler.runtime = "MDd"
+        settings.arch = "x86"
+        settings.build_type = None
+
+        conan_file = ConanFileMock()
+        conan_file.settings = settings
+
+        cmake = CMake(conan_file)
+        cmake.build()
+        self.assertIn("/verbosity:minimal", conan_file.command)
+
+        cmake = CMake(conan_file, msbuild_verbosity="quiet")
+        cmake.build()
+        self.assertIn("/verbosity:quiet", conan_file.command)
+
+        with tools.environment_append({"CONAN_MSBUILD_VERBOSITY": "detailed"}):
+            cmake = CMake(conan_file)
+            cmake.build()
+            self.assertIn("/verbosity:detailed", conan_file.command)
+
+    def test_ctest_variables(self):
+        conanfile = ConanFileMock()
+        settings = Settings.loads(default_settings_yml)
+        settings.os = "Windows"
+        settings.compiler = "Visual Studio"
+        settings.compiler.version = "14"
+        conanfile.settings = settings
+
+        cmake = CMake(conanfile, parallel=False, generator="NMake Makefiles")
+        cmake.test()
+        self.assertEquals(conanfile.captured_env["CTEST_OUTPUT_ON_FAILURE"], "0")
+        self.assertNotIn("CTEST_PARALLEL_LEVEL", conanfile.captured_env)
+
+        with tools.environment_append({"CONAN_CPU_COUNT": "666"}):
+            cmake = CMake(conanfile, parallel=True, generator="NMake Makefiles")
+            cmake.test(output_on_failure=True)
+            self.assertEquals(conanfile.captured_env["CTEST_OUTPUT_ON_FAILURE"], "1")
+            self.assertEquals(conanfile.captured_env["CTEST_PARALLEL_LEVEL"], "666")
+
diff --git a/conans/test/unittests/client/build/msbuild_test.py b/conans/test/unittests/client/build/msbuild_test.py
index 76f417d48..126139866 100644
--- a/conans/test/unittests/client/build/msbuild_test.py
+++ b/conans/test/unittests/client/build/msbuild_test.py
@@ -145,3 +145,58 @@ class MSBuildTest(unittest.TestCase):
                                                                       "MyProp2": "MyValue2"})
         self.assertIn('/p:MyProp1="MyValue1"', command)
         self.assertIn('/p:MyProp2="MyValue2"', command)
+
+    def definitions_test(self):
+        settings = MockSettings({"build_type": "Debug",
+                                 "compiler": "Visual Studio",
+                                 "arch": "x86_64",
+                                 "compiler.runtime": "MDd"})
+        conanfile = MockConanfile(settings)
+        msbuild = MSBuild(conanfile)
+        template = msbuild._get_props_file_contents(definitions={'_WIN32_WINNT': "0x0501"})
+
+        self.assertIn("<PreprocessorDefinitions>"
+                      "_WIN32_WINNT=0x0501;"
+                      "%(PreprocessorDefinitions)</PreprocessorDefinitions>", template)
+
+    def definitions_no_value_test(self):
+        settings = MockSettings({"build_type": "Debug",
+                                 "compiler": "Visual Studio",
+                                 "arch": "x86_64",
+                                 "compiler.runtime": "MDd"})
+        conanfile = MockConanfile(settings)
+        msbuild = MSBuild(conanfile)
+        template = msbuild._get_props_file_contents(definitions={'_DEBUG': None})
+
+        self.assertIn("<PreprocessorDefinitions>"
+                      "_DEBUG;"
+                      "%(PreprocessorDefinitions)</PreprocessorDefinitions>", template)
+
+    def verbosity_default_test(self):
+        settings = MockSettings({"build_type": "Debug",
+                                 "compiler": "Visual Studio",
+                                 "arch": "x86_64"})
+        conanfile = MockConanfile(settings)
+        msbuild = MSBuild(conanfile)
+        command = msbuild.get_command("projecshould_flags_testt_file.sln")
+        self.assertIn('/verbosity:minimal', command)
+
+    def verbosity_env_test(self):
+        settings = MockSettings({"build_type": "Debug",
+                                 "compiler": "Visual Studio",
+                                 "arch": "x86_64"})
+        with tools.environment_append({"CONAN_MSBUILD_VERBOSITY": "detailed"}):
+            conanfile = MockConanfile(settings)
+            msbuild = MSBuild(conanfile)
+            command = msbuild.get_command("projecshould_flags_testt_file.sln")
+            self.assertIn('/verbosity:detailed', command)
+
+    def verbosity_explicit_test(self):
+        settings = MockSettings({"build_type": "Debug",
+                                 "compiler": "Visual Studio",
+                                 "arch": "x86_64"})
+        conanfile = MockConanfile(settings)
+        msbuild = MSBuild(conanfile)
+        command = msbuild.get_command("projecshould_flags_testt_file.sln", verbosity="quiet")
+        self.assertIn('/verbosity:quiet', command)
+
diff --git a/conans/test/unittests/client/conf/detect/__init__.py b/conans/test/unittests/client/conf/detect/__init__.py
new file mode 100644
index 000000000..992ec4875
--- /dev/null
+++ b/conans/test/unittests/client/conf/detect/__init__.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/conans/test/unittests/client/conf/detect/detected_os_test.py b/conans/test/unittests/client/conf/detect/detected_os_test.py
new file mode 100644
index 000000000..e194f7cfb
--- /dev/null
+++ b/conans/test/unittests/client/conf/detect/detected_os_test.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
+
+import unittest
+from mock import mock
+from conans.client.conf.detect import detected_os
+from conans.client.tools import OSInfo
+
+
+class DetectedOSTest(unittest.TestCase):
+    def test_windows(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='Windows')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertEqual(detected_os(), "Windows")
+
+    def test_cygwin(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='CYGWIN_NT-10.0')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertEqual(detected_os(), "Windows")
+
+    def test_msys(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='MSYS_NT-10.0')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertEqual(detected_os(), "Windows")
+
+    def test_mingw32(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='MINGW32_NT-10.0')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertEqual(detected_os(), "Windows")
+
+    def test_mingw64(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='MINGW64_NT-10.0')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertEqual(detected_os(), "Windows")
+
+    def test_darwin(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='Darwin')):
+            self.assertEqual(detected_os(), "Macos")
+
+    def test_linux(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='Linux')):
+            with mock.patch.object(OSInfo, '_get_linux_distro_info'):
+                self.assertEqual(detected_os(), "Linux")
+
+    def test_freebsd(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='FreeBSD')):
+            self.assertEqual(detected_os(), "FreeBSD")
+
+    def test_solaris(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='SunOS')):
+            self.assertEqual(detected_os(), "SunOS")
diff --git a/conans/test/functional/old/deps_graph_test.py b/conans/test/unittests/client/graph/deps_graph_test.py
similarity index 100%
rename from conans/test/functional/old/deps_graph_test.py
rename to conans/test/unittests/client/graph/deps_graph_test.py
diff --git a/conans/test/unittests/client/tools/os_info/__init__.py b/conans/test/unittests/client/tools/os_info/__init__.py
new file mode 100644
index 000000000..992ec4875
--- /dev/null
+++ b/conans/test/unittests/client/tools/os_info/__init__.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/conans/test/unittests/client/tools/os_info/osinfo_test.py b/conans/test/unittests/client/tools/os_info/osinfo_test.py
new file mode 100644
index 000000000..c49f963f7
--- /dev/null
+++ b/conans/test/unittests/client/tools/os_info/osinfo_test.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
+
+import unittest
+import platform
+import os
+from conans.client.tools import OSInfo, environment_append, CYGWIN, MSYS2, MSYS, remove_from_path
+from conans.errors import ConanException
+from mock import mock
+from nose.plugins.attrib import attr
+
+
+class OSInfoTest(unittest.TestCase):
+
+    def setUp(self):
+        self._uname = None
+        self._version = None
+
+    def subprocess_check_output_mock(self, cmd, shell):
+        if cmd.endswith('"uname"'):
+            return self._uname.encode()
+        elif cmd.endswith('"uname -or"'):
+            return self._version.encode()
+        raise ValueError("don't know how to respond to %s" % cmd)
+
+    def test_windows(self):
+        self._uname = None
+        self._version = None
+        with mock.patch("platform.system", mock.MagicMock(return_value='Windows')):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    with remove_from_path("uname"):
+                        with remove_from_path("bash"):
+                            # FIXME: bug in environment_append removing variables which aren't exist (another PR)
+                            new_env = dict()
+                            if 'CONAN_BASH_PATH' in os.environ:
+                                new_env['CONAN_BASH_PATH'] = None
+                            with environment_append(new_env):
+                                self.assertTrue(OSInfo().is_windows)
+                                self.assertFalse(OSInfo().is_cygwin)
+                                self.assertFalse(OSInfo().is_msys)
+                                self.assertFalse(OSInfo().is_linux)
+                                self.assertFalse(OSInfo().is_freebsd)
+                                self.assertFalse(OSInfo().is_macos)
+                                self.assertFalse(OSInfo().is_solaris)
+
+                                with self.assertRaises(ConanException):
+                                    OSInfo.uname()
+                                self.assertIsNone(OSInfo.detect_windows_subsystem())
+
+    def test_cygwin(self):
+        self._uname = 'CYGWIN_NT-10.0'
+        self._version = '2.11.2(0.329/5/3)'
+        with mock.patch("platform.system", mock.MagicMock(return_value=self._uname)):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertTrue(OSInfo().is_windows)
+                    self.assertTrue(OSInfo().is_cygwin)
+                    self.assertFalse(OSInfo().is_msys)
+                    self.assertFalse(OSInfo().is_linux)
+                    self.assertFalse(OSInfo().is_freebsd)
+                    self.assertFalse(OSInfo().is_macos)
+                    self.assertFalse(OSInfo().is_solaris)
+
+                    with environment_append({"CONAN_BASH_PATH": "/fake/bash.exe"}):
+                        with mock.patch('subprocess.check_output', new=self.subprocess_check_output_mock):
+                            self.assertEqual(OSInfo.uname(), self._uname.lower())
+                            self.assertEqual(OSInfo.detect_windows_subsystem(), CYGWIN)
+
+    def test_msys2(self):
+        self._uname = 'MSYS_NT-10.0'
+        self._version = '1.0.18(0.48/3/2)'
+        with mock.patch("platform.system", mock.MagicMock(return_value=self._uname)):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertTrue(OSInfo().is_windows)
+                    self.assertFalse(OSInfo().is_cygwin)
+                    self.assertTrue(OSInfo().is_msys)
+                    self.assertFalse(OSInfo().is_linux)
+                    self.assertFalse(OSInfo().is_freebsd)
+                    self.assertFalse(OSInfo().is_macos)
+                    self.assertFalse(OSInfo().is_solaris)
+
+                    with environment_append({"CONAN_BASH_PATH": "/fake/bash.exe"}):
+                        with mock.patch('subprocess.check_output', new=self.subprocess_check_output_mock):
+                            self.assertEqual(OSInfo.uname(), self._uname.lower())
+                            self.assertEqual(OSInfo.detect_windows_subsystem(), MSYS)
+
+    def test_mingw32(self):
+        self._uname = 'MINGW32_NT-10.0'
+        self._version = '2.10.0(0.325/5/3)'
+        with mock.patch("platform.system", mock.MagicMock(return_value=self._uname)):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertTrue(OSInfo().is_windows)
+                    self.assertFalse(OSInfo().is_cygwin)
+                    self.assertTrue(OSInfo().is_msys)
+                    self.assertFalse(OSInfo().is_linux)
+                    self.assertFalse(OSInfo().is_freebsd)
+                    self.assertFalse(OSInfo().is_macos)
+                    self.assertFalse(OSInfo().is_solaris)
+
+                    with environment_append({"CONAN_BASH_PATH": "/fake/bash.exe"}):
+                        with mock.patch('subprocess.check_output', new=self.subprocess_check_output_mock):
+                            self.assertEqual(OSInfo.uname(), self._uname.lower())
+                            self.assertEqual(OSInfo.detect_windows_subsystem(), MSYS2)
+
+    def test_mingw64(self):
+        self._uname = 'MINGW64_NT-10.0'
+        self._version = '2.4.0(0.292/5/3)'
+        with mock.patch("platform.system", mock.MagicMock(return_value=self._uname)):
+            with mock.patch.object(OSInfo, "get_win_version_name", return_value="Windows 98"):
+                with mock.patch.object(OSInfo, "get_win_os_version", return_value="4.0"):
+                    self.assertTrue(OSInfo().is_windows)
+                    self.assertFalse(OSInfo().is_cygwin)
+                    self.assertTrue(OSInfo().is_msys)
+                    self.assertFalse(OSInfo().is_linux)
+                    self.assertFalse(OSInfo().is_freebsd)
+                    self.assertFalse(OSInfo().is_macos)
+                    self.assertFalse(OSInfo().is_solaris)
+
+                    with environment_append({"CONAN_BASH_PATH": "/fake/bash.exe"}):
+                        with mock.patch('subprocess.check_output', new=self.subprocess_check_output_mock):
+                            self.assertEqual(OSInfo.uname(), self._uname.lower())
+                            self.assertEqual(OSInfo.detect_windows_subsystem(), MSYS2)
+
+    def test_linux(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='Linux')):
+            with mock.patch.object(OSInfo, '_get_linux_distro_info'):
+                self.assertFalse(OSInfo().is_windows)
+                self.assertFalse(OSInfo().is_cygwin)
+                self.assertFalse(OSInfo().is_msys)
+                self.assertTrue(OSInfo().is_linux)
+                self.assertFalse(OSInfo().is_freebsd)
+                self.assertFalse(OSInfo().is_macos)
+                self.assertFalse(OSInfo().is_solaris)
+
+                with self.assertRaises(ConanException):
+                    OSInfo.uname()
+                self.assertIsNone(OSInfo.detect_windows_subsystem())
+
+    def test_macos(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='Darwin')):
+            self.assertFalse(OSInfo().is_windows)
+            self.assertFalse(OSInfo().is_cygwin)
+            self.assertFalse(OSInfo().is_msys)
+            self.assertFalse(OSInfo().is_linux)
+            self.assertFalse(OSInfo().is_freebsd)
+            self.assertTrue(OSInfo().is_macos)
+            self.assertFalse(OSInfo().is_solaris)
+
+            with self.assertRaises(ConanException):
+                OSInfo.uname()
+            self.assertIsNone(OSInfo.detect_windows_subsystem())
+
+    def test_freebsd(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='FreeBSD')):
+            self.assertFalse(OSInfo().is_windows)
+            self.assertFalse(OSInfo().is_cygwin)
+            self.assertFalse(OSInfo().is_msys)
+            self.assertFalse(OSInfo().is_linux)
+            self.assertTrue(OSInfo().is_freebsd)
+            self.assertFalse(OSInfo().is_macos)
+            self.assertFalse(OSInfo().is_solaris)
+
+            with self.assertRaises(ConanException):
+                OSInfo.uname()
+            self.assertIsNone(OSInfo.detect_windows_subsystem())
+
+    def test_solaris(self):
+        with mock.patch("platform.system", mock.MagicMock(return_value='SunOS')):
+            self.assertFalse(OSInfo().is_windows)
+            self.assertFalse(OSInfo().is_cygwin)
+            self.assertFalse(OSInfo().is_msys)
+            self.assertFalse(OSInfo().is_linux)
+            self.assertFalse(OSInfo().is_freebsd)
+            self.assertFalse(OSInfo().is_macos)
+            self.assertTrue(OSInfo().is_solaris)
+
+            with self.assertRaises(ConanException):
+                OSInfo.uname()
+            self.assertIsNone(OSInfo.detect_windows_subsystem())
diff --git a/conans/test/unittests/search/__init__.py b/conans/test/unittests/search/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/functional/old/disk_search_test.py b/conans/test/unittests/search/disk_search_test.py
similarity index 100%
rename from conans/test/functional/old/disk_search_test.py
rename to conans/test/unittests/search/disk_search_test.py
diff --git a/conans/test/functional/old/search_query_parse_test.py b/conans/test/unittests/search/search_query_parse_test.py
similarity index 100%
rename from conans/test/functional/old/search_query_parse_test.py
rename to conans/test/unittests/search/search_query_parse_test.py
diff --git a/conans/test/unittests/source/__init__.py b/conans/test/unittests/source/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/functional/old/merge_directories_test.py b/conans/test/unittests/source/merge_directories_test.py
similarity index 100%
rename from conans/test/functional/old/merge_directories_test.py
rename to conans/test/unittests/source/merge_directories_test.py
diff --git a/conans/test/unittests/util/build_sln_command_test.py b/conans/test/unittests/util/build_sln_command_test.py
index d2ca2c940..cd5364740 100644
--- a/conans/test/unittests/util/build_sln_command_test.py
+++ b/conans/test/unittests/util/build_sln_command_test.py
@@ -140,6 +140,7 @@ class BuildSLNCommandTest(unittest.TestCase):
         self.assertTrue(command.startswith('msbuild "dummy.sln" /p:Configuration="Debug" '
                                            '/p:Platform="ARM" '
                                            '/p:PlatformToolset="v110" '
+                                           '/verbosity:minimal '
                                            '/p:ForceImportBeforeCppTargets='), command)
 
     def properties_file_test(self):
@@ -154,6 +155,7 @@ class BuildSLNCommandTest(unittest.TestCase):
                                     parallel=False, output=ConanOutput(new_out))
         self.assertTrue(command.startswith('msbuild "dummy.sln" /p:Configuration="Debug" '
                                            '/p:Platform="ARM" '
+                                           '/verbosity:minimal '
                                            '/p:ForceImportBeforeCppTargets='), command)
         path_tmp = command.split("/p:ForceImportBeforeCppTargets=")[1][1:-1]  # remove quotes
         self.assertTrue(os.path.exists(path_tmp))