diff --git a/.ci/appveyor/install.bat b/.ci/appveyor/install.bat index 9548bc0e2c9..6eb65fd4484 100644 --- a/.ci/appveyor/install.bat +++ b/.ci/appveyor/install.bat @@ -18,3 +18,7 @@ SET CONAN_LOGGING_LEVEL=10 %PYTHON%/Scripts/pip.exe install -r conans/requirements.txt %PYTHON%/Scripts/pip.exe install -r conans/requirements_dev.txt %PYTHON%/Scripts/pip.exe install -r conans/requirements_server.txt + +C:/Python35/Scripts/pip.exe install meson +choco install pkgconfiglite -y +choco install ninja -y diff --git a/.ci/appveyor/test.bat b/.ci/appveyor/test.bat index d61badbf253..04f697d45ad 100644 --- a/.ci/appveyor/test.bat +++ b/.ci/appveyor/test.bat @@ -1 +1 @@ -nosetests --with-coverage --verbosity=2 conans.test --processes=4 --process-timeout=1000 +nosetests --with-coverage --verbosity=2 conans.test conans.test_integration --processes=4 --process-timeout=1000 diff --git a/.ci/travis/install.sh b/.ci/travis/install.sh index 930daa07903..0447cc2d63d 100755 --- a/.ci/travis/install.sh +++ b/.ci/travis/install.sh @@ -8,6 +8,8 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then brew outdated pyenv || brew upgrade pyenv brew install pyenv-virtualenv brew install pkg-config + brew install ninja + brew install meson if which pyenv > /dev/null; then eval "$(pyenv init -)" @@ -43,7 +45,15 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then pyenv activate conan else sudo apt-get update - sudo apt-get install gcc-multilib g++-multilib + sudo apt-get install gcc-multilib g++-multilib wget unzip + + wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip + unzip ninja-linux.zip + sudo mv ninja /usr/bin/ninja + rm ninja-linux.zip + + # Will fail if no python3 available + pip3 install meson || true fi pip install -r conans/requirements_dev.txt diff --git a/.ci/travis/run.sh b/.ci/travis/run.sh index 2c6c337c163..9527a5b1238 100755 --- a/.ci/travis/run.sh +++ b/.ci/travis/run.sh @@ -8,6 +8,10 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then eval "$(pyenv init -)" fi pyenv activate conan + # OSX py3 do not show output in more than 10min causing the build to fail + nosetests --with-coverage conans.test conans.test_integration --verbosity=2 +else + nosetests --with-coverage conans.test conans.test_integration --verbosity=2 --processes=4 --process-timeout=1000 fi -nosetests --with-coverage conans.test --verbosity=2 --processes=4 --process-timeout=1000 + diff --git a/conans/__init__.py b/conans/__init__.py index fc9d70be955..7963bf2c49d 100644 --- a/conans/__init__.py +++ b/conans/__init__.py @@ -4,6 +4,7 @@ from conans.model.options import Options from conans.model.settings import Settings from conans.client.cmake import CMake +from conans.client.meson import Meson from conans.client.gcc import GCC from conans.client.configure_environment import ConfigureEnvironment from conans.client.configure_build_environment import (AutoToolsBuildEnvironment, VisualStudioBuildEnvironment) @@ -16,4 +17,4 @@ SERVER_CAPABILITIES = [COMPLEX_SEARCH_CAPABILITY, ] -__version__ = '0.27.0' +__version__ = '0.28.0' diff --git a/conans/client/__init__.py b/conans/client/__init__.py index e69de29bb2d..1eef4896a7d 100644 --- a/conans/client/__init__.py +++ b/conans/client/__init__.py @@ -0,0 +1,7 @@ + +def defs_to_string(defs): + return " ".join(['-D{0}="{1}"'.format(k, v) for k, v in defs.items()]) + + +def join_arguments(args): + return " ".join(filter(None, args)) diff --git a/conans/client/build_requires.py b/conans/client/build_requires.py index 35abec078cd..470543f7a73 100644 --- a/conans/client/build_requires.py +++ b/conans/client/build_requires.py @@ -98,6 +98,6 @@ def _install(self, build_requires_references, build_requires_options, installer) self._profile_build_requires.pop("*", None) self._profile_build_requires.pop("&!", None) - installer.install(deps_graph, "") + installer.install(deps_graph) self._profile_build_requires = old_build_requires # Restore original values return deps_graph diff --git a/conans/client/client_cache.py b/conans/client/client_cache.py index 73219dd7032..cb71da1f1f1 100644 --- a/conans/client/client_cache.py +++ b/conans/client/client_cache.py @@ -1,6 +1,5 @@ import os from collections import OrderedDict -from genericpath import isdir from conans.client.conf import ConanClientConfigParser, default_client_conf, default_settings_yml from conans.client.detect import detect_defaults_settings @@ -14,6 +13,8 @@ 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 + CONAN_CONF = 'conan.conf' CONAN_SETTINGS = "settings.yml" @@ -34,8 +35,30 @@ def __init__(self, base_folder, store_folder, output): self._output = output self._store_folder = store_folder or self.conan_config.storage_path or self.conan_folder self._default_profile = None + self._no_lock = None super(ClientCache, self).__init__(self._store_folder) + def _no_locks(self): + if self._no_lock is None: + self._no_lock = self.conan_config.cache_no_locks + return self._no_lock + + def conanfile_read_lock(self, conan_ref): + if self._no_locks(): + return NoLock() + return ReadLock(os.path.join(self.conan(conan_ref), "rw")) + + def conanfile_write_lock(self, conan_ref): + if self._no_locks(): + return NoLock() + return WriteLock(os.path.join(self.conan(conan_ref), "rw")) + + def package_lock(self, package_ref): + if self._no_locks(): + return NoLock() + return SimpleLock(os.path.join(self.conan(package_ref.conan), "locks", + package_ref.package_id)) + @property def put_headers_path(self): return os.path.join(self.conan_folder, PUT_HEADERS) @@ -144,7 +167,7 @@ def conan_packages(self, conan_reference): packages_dir = self.packages(conan_reference) try: packages = [dirname for dirname in os.listdir(packages_dir) - if isdir(os.path.join(packages_dir, dirname))] + if os.path.isdir(os.path.join(packages_dir, dirname))] except: # if there isn't any package folder packages = [] return packages @@ -155,7 +178,7 @@ def conan_builds(self, conan_reference): builds_dir = self.builds(conan_reference) try: builds = [dirname for dirname in os.listdir(builds_dir) - if isdir(os.path.join(builds_dir, dirname))] + if os.path.isdir(os.path.join(builds_dir, dirname))] except: # if there isn't any package folder builds = [] return builds diff --git a/conans/client/cmake.py b/conans/client/cmake.py index 646d9e37398..be1cd84fb5c 100644 --- a/conans/client/cmake.py +++ b/conans/client/cmake.py @@ -4,6 +4,7 @@ from collections import OrderedDict from contextlib import contextmanager +from conans.client import defs_to_string, join_arguments from conans.errors import ConanException from conans.model.conan_file import ConanFile from conans.model.settings import Settings @@ -39,7 +40,7 @@ def _get_env_cmake_system_name(): class CMake(object): def __init__(self, settings_or_conanfile, generator=None, cmake_system_name=True, - parallel=True, build_type=None): + parallel=True, build_type=None, toolset=None): """ :param settings_or_conanfile: Conanfile instance (or settings for retro compatibility) :param generator: Generator name to use or none to autodetect @@ -47,6 +48,8 @@ def __init__(self, settings_or_conanfile, generator=None, cmake_system_name=True True for auto-detect or directly a string with the system name :param parallel: Try to build with multiple cores if available :param build_type: Overrides default build type comming from settings + :param toolset: Toolset name to use (such as llvm-vs2014) or none for default one, + applies only to certain generators (e.g. Visual Studio) """ if isinstance(settings_or_conanfile, Settings): self._settings = settings_or_conanfile @@ -71,6 +74,7 @@ def __init__(self, settings_or_conanfile, generator=None, cmake_system_name=True self._build_type = self._settings.get_safe("build_type") self.generator = generator or self._generator() + self.toolset = self._toolset(toolset) self.build_dir = None self._cmake_system_name = _get_env_cmake_system_name() if self._cmake_system_name is None: # Not overwritten using environment @@ -97,7 +101,7 @@ def build_type(self, build_type): @property def flags(self): - return _defs_to_string(self.definitions) + return defs_to_string(self.definitions) @staticmethod def options_cmd_line(options, option_upper=True, value_upper=True): @@ -143,6 +147,11 @@ def _generator(self): return "Unix Makefiles" + def _toolset(self, toolset=None): + if "CONAN_CMAKE_TOOLSET" in os.environ: + return os.environ["CONAN_CMAKE_TOOLSET"] + return toolset + def _cmake_compiler_options(self, the_os, arch): cmake_definitions = OrderedDict() @@ -171,7 +180,7 @@ def _cmake_cross_build_defines(self, the_os, os_ver): platform_os = {"Darwin": "Macos"}.get(platform.system(), platform.system()) if (platform_os != the_os) or os_ver: # We are cross building if the_os: - ret["CMAKE_SYSTEM_NAME"] = the_os + ret["CMAKE_SYSTEM_NAME"] = "Darwin" if the_os in ["iOS", "tvOS", "watchOS"] else the_os if os_ver: ret["CMAKE_SYSTEM_VERSION"] = os_ver else: @@ -225,11 +234,14 @@ def is_multi_configuration(self): @property def command_line(self): - return _join_arguments([ + args = [ '-G "%s"' % self.generator, self.flags, '-Wno-dev' - ]) + ] + if self.toolset: + args.append('-T "%s"' % self.toolset) + return join_arguments(args) def _build_type_definition(self): if self._build_type and not self.is_multi_configuration: @@ -238,7 +250,7 @@ def _build_type_definition(self): @property def runtime(self): - return _defs_to_string(self._runtime_definition()) + return defs_to_string(self._runtime_definition()) def _runtime_definition(self): if self._runtime: @@ -318,10 +330,10 @@ def _configure_new(self, args=None, defs=None, source_dir=None, build_dir=None): self.build_dir = build_dir or self.build_dir or self._conanfile.build_folder mkdir(self.build_dir) - arg_list = _join_arguments([ + arg_list = join_arguments([ self.command_line, args_to_string(args), - _defs_to_string(defs), + defs_to_string(defs), args_to_string([source_dir]) ]) command = "cd %s && cmake %s" % (args_to_string([self.build_dir]), arg_list) @@ -353,7 +365,7 @@ def _build_new(self, args=None, build_dir=None, target=None): args.append("--") args.append("-j%i" % cpu_count()) - arg_list = _join_arguments([ + arg_list = join_arguments([ args_to_string([build_dir]), self.build_config, args_to_string(args) @@ -362,6 +374,7 @@ def _build_new(self, args=None, build_dir=None, target=None): self._conanfile.run(command) def install(self, args=None, build_dir=None): + mkdir(self._conanfile.package_folder) if not self.definitions.get("CMAKE_INSTALL_PREFIX"): raise ConanException("CMAKE_INSTALL_PREFIX not defined for 'cmake.install()'\n" "Make sure 'package_folder' is defined") @@ -386,13 +399,6 @@ def verbose(self): def verbose(self, value): self.definitions["CMAKE_VERBOSE_MAKEFILE"] = "ON" if value else "OFF" -def _defs_to_string(defs): - return " ".join(['-D{0}="{1}"'.format(k, v) for k, v in defs.items()]) - - -def _join_arguments(args): - return " ".join(filter(None, args)) - @contextmanager def clean_sh_from_path(): diff --git a/conans/client/command.py b/conans/client/command.py index b6a4aca99e2..bb5c2fe3a34 100644 --- a/conans/client/command.py +++ b/conans/client/command.py @@ -2,8 +2,9 @@ import inspect import os import sys +from argparse import ArgumentError -from conans import __version__ as CLIENT_VERSION +from conans import __version__ as client_version from conans.client.conan_api import (Conan, default_manifest_folder) from conans.client.conan_command_output import CommandOutputer from conans.client.output import Color @@ -24,7 +25,6 @@ class Extender(argparse.Action): options = ['qt:value', 'mode:2'] settings = ['cucumber:true'] """ - def __call__(self, parser, namespace, values, option_strings=None): # @UnusedVariable # Need None here incase `argparse.SUPPRESS` was supplied for `dest` dest = getattr(namespace, self.dest, None) @@ -66,7 +66,7 @@ def new(self, *args): help='Create test_package skeleton to test package') parser.add_argument("-i", "--header", action='store_true', default=False, help='Create a headers only package template') - parser.add_argument("-c", "--pure_c", action='store_true', default=False, + parser.add_argument("-c", "--pure-c", "--pure_c", action='store_true', default=False, help='Create a C language package only package, ' 'deleting "self.settings.compiler.libcxx" setting ' 'in the configure method') @@ -75,25 +75,32 @@ def new(self, *args): 'using "exports_sources" instead of retrieving external code with ' 'the "source()" method') parser.add_argument("-b", "--bare", action='store_true', default=False, - help='Create the minimum package recipe, without build() or package()' - 'methods. Useful in combination with "package_files" command') - parser.add_argument("-cis", "--ci_shared", action='store_true', default=False, + help='Create the minimum package recipe, without build() method' + 'Useful in combination with "export-pkg" command') + parser.add_argument("-cis", "--ci-shared", "--ci_shared", action='store_true', + default=False, help='Package will have a "shared" option to be used in CI') - parser.add_argument("-cilg", "--ci_travis_gcc", action='store_true', default=False, + parser.add_argument("-cilg", "--ci-travis-gcc", "--ci_travis_gcc", action='store_true', + default=False, help='Generate travis-ci files for linux gcc') - parser.add_argument("-cilc", "--ci_travis_clang", action='store_true', default=False, + parser.add_argument("-cilc", "--ci-travis-clang", "--ci_travis_clang", action='store_true', + default=False, help='Generate travis-ci files for linux clang') - parser.add_argument("-cio", "--ci_travis_osx", action='store_true', default=False, + parser.add_argument("-cio", "--ci-travis-osx", "--ci_travis_osx", action='store_true', + default=False, help='Generate travis-ci files for OSX apple-clang') - parser.add_argument("-ciw", "--ci_appveyor_win", action='store_true', default=False, - help='Generate appveyor files for Appveyor Visual Studio') - parser.add_argument("-ciglg", "--ci_gitlab_gcc", action='store_true', default=False, + parser.add_argument("-ciw", "--ci-appveyor-win", "--ci_appveyor_win", action='store_true', + default=False, help='Generate appveyor files for Appveyor ' + 'Visual Studio') + parser.add_argument("-ciglg", "--ci-gitlab-gcc", "--ci_gitlab_gcc", action='store_true', + default=False, help='Generate GitLab files for linux gcc') - parser.add_argument("-ciglc", "--ci_gitlab_clang", action='store_true', default=False, + parser.add_argument("-ciglc", "--ci-gitlab-clang", "--ci_gitlab_clang", action='store_true', + default=False, help='Generate GitLab files for linux clang') parser.add_argument("-gi", "--gitignore", action='store_true', default=False, help='Generate a .gitignore with the known patterns to excluded') - parser.add_argument("-ciu", "--ci_upload_url", + parser.add_argument("-ciu", "--ci-upload-url", "--ci_upload_url", help='Define URL of the repository to upload') args = parser.parse_args(*args) @@ -108,11 +115,53 @@ def new(self, *args): gitlab_gcc_versions=args.ci_gitlab_gcc, gitlab_clang_versions=args.ci_gitlab_clang) + def test(self, *args): + """ Runs a test_folder/conanfile.py to test an existing package. + The package to be tested must exist in the local cache or any configured remote. + To create and test a binary package for a local directory conanfile.py use the + 'conan create' command. + """ + parser = argparse.ArgumentParser(description=self.test.__doc__, prog="conan test") + parser.add_argument("path", help='path to a recipe (conanfile.py), e.g., conan test ' + 'pkg/version@user/channel ') + parser.add_argument("reference", nargs="?", + help='a full package reference pkg/version@user/channel, ' + 'or just the package name "pkg" if the test_package conanfile is ' + 'requiring more than one reference. Empty if the conanfile has only' + 'one require') + + _add_common_install_arguments(parser, build_help=_help_build_policies) + + args = parser.parse_args(*args) + + if not args.reference: + name = version = user = channel = None + else: + try: + name, version, user, channel = ConanFileReference.loads(args.reference) + except ConanException: + if "@" not in args.reference: + if "/" in args.reference: + raise ConanException("Specify the full reference or only a package name " + "without version (if the test_package/conanfile.py " + "is requiring the reference to be tested") + else: + name = args.reference + version = None + channel = None + user = None + else: + raise ConanException("Invalid reference: %s" % args.reference) + + return self._conan.test(args.path, args.profile, args.settings, args.options, + args.env, args.remote, args.update, + user=user, channel=channel, name=name, + version=version, build_modes=args.build) + def test_package(self, *args): - """ Export, build package and test it with a consumer project. - The consumer project must have a 'conanfile.py' with a 'test()' method, and should be - located in a subfolder, named 'test_package` by default. It must 'require' the package - under testing. + """DEPRECATED, will be removed. Use 'conan create' and/or 'conan test'. + Use 'conan create' to generate binary packages for a recipe. + If you want to test a package you can use 'conan test' command. """ parser = argparse.ArgumentParser(description=self.test_package.__doc__, @@ -122,7 +171,7 @@ def test_package(self, *args): 'or just the user/channel if package and version are defined in recipe') parser.add_argument("-ne", "--not-export", default=False, action='store_true', help='Do not export the conanfile before test execution') - parser.add_argument("-tf", "--test_folder", + parser.add_argument("-tf", "--test-folder", "--test_folder", help='alternative test folder name, by default is "test_package"') parser.add_argument('--keep-source', '-k', default=False, action='store_true', help='Optional. Do not remove the source folder in local cache. ' @@ -151,23 +200,31 @@ def test_package(self, *args): version=version) def create(self, *args): - """ Export, build package and test it with a consumer project. - The consumer project must have a 'conanfile.py' with a 'test()' method, and should be - located in a subfolder, named 'test_package` by default. It must 'require' the package - under testing. + """ Builds a binary package for recipe (conanfile.py) located in current dir. + Uses the specified configuration in a profile or in -s settings, -o options etc. + If a 'test_package' folder (the name can be configured with -tf) is found, the command will + run the consumer project to ensure that the package has been created correctly. Check the + 'conan test' command to know more about the 'test_folder' project. """ parser = argparse.ArgumentParser(description=self.create.__doc__, prog="conan create") - parser.add_argument("reference", help='a full package reference Pkg/version@user/channel, ' - 'or just the user/channel if package and version are defined in recipe') + parser.add_argument("reference", help='user/channel, or a full package reference' + ' (Pkg/version@user/channel), if name and version ' + ' are not declared in the recipe') + parser.add_argument('--cwd', '-c', default=None, + help='Optional. Folder with a %s. Default current directory.' + % CONANFILE) + parser.add_argument("--file", "-f", help="specify conanfile filename") + parser.add_argument("-ne", "--not-export", default=False, action='store_true', help='Do not export the conanfile') - parser.add_argument("-tf", "--test_folder", + parser.add_argument("-tf", "--test-folder", "--test_folder", help='alternative test folder name, by default is "test_package"') parser.add_argument('--keep-source', '-k', default=False, action='store_true', help='Optional. Do not remove the source folder in local cache. ' 'Use for testing purposes only') - parser.add_argument("--cwd", "-c", help='Use this directory as the current directory') + parser.add_argument("--werror", action='store_true', default=False, + help='Error instead of warnings for graph inconsistencies') _add_manifests_arguments(parser) _add_common_install_arguments(parser, build_help=_help_build_policies) @@ -180,73 +237,54 @@ def create(self, *args): args.env, args.scope, args.test_folder, args.not_export, args.build, args.keep_source, args.verify, args.manifests, args.manifests_interactive, args.remote, args.update, - cwd=args.cwd, name=name, version=version, user=user, - channel=channel) - - def package_files(self, *args): - """Creates a package binary from given precompiled artifacts in user folder, skipping - the package recipe build() method. If source_folder or build_folder is specified, - then it will call the package() method to extract the artifacts. If source_folder - nor build_folder is not specified, then it will run an exact copy of the package, - as they are layout in the given folder, without running or even requiring to define a - package() method. - """ - parser = argparse.ArgumentParser(description=self.package_files.__doc__, - prog="conan package_files") + conan_file_path=args.cwd, name=name, version=version, user=user, + channel=channel, filename=args.file, werror=args.werror) + + def download(self, *args): + """Downloads recipe and binaries to the local cache, without using settings. + It works specifying the recipe reference and package ID to be installed. + Not transitive, requirements of the specified reference will be retrieved. + Useful together with 'conan copy' to automate the promotion of + packages to a different user/channel. If only a reference is specified, it will download + all packages in the specified remote. + If no remote is specified will search sequentially in the available configured remotes.""" + + parser = argparse.ArgumentParser(description=self.download.__doc__, prog="conan download") parser.add_argument("reference", help='package recipe reference e.g., MyPackage/1.2@user/channel') - parser.add_argument("--package_folder", "-pf", - help='Get binaries from this path, relative to current or absolute') - parser.add_argument("--source_folder", "-sf", - help='Get artifacts from this path, relative to current or absolute.' - ' If specified, artifacts will be extracted/copied calling the ' - 'package() method') - parser.add_argument("--build_folder", "-bf", - help='Get artifacts from this path, relative to current or absolute' - ' If specified, artifacts will be extracted/copied calling the ' - 'package() method') - parser.add_argument("--profile", "-pr", - help='Profile for this package') - parser.add_argument("--options", "-o", - help='Options for this package. e.g., -o with_qt=true', - nargs=1, action=Extender) - parser.add_argument("--settings", "-s", - help='Settings for this package e.g., -s compiler=gcc', - nargs=1, action=Extender) - parser.add_argument('-f', '--force', default=False, - action='store_true', help='Overwrite existing package if existing') + parser.add_argument("--package", "-p", nargs=1, action=Extender, + help='Force install specified package ID (ignore settings/options)') + parser.add_argument("-r", "--remote", help='look in the specified remote server') args = parser.parse_args(*args) - return self._conan.package_files(reference=args.reference, - source_folder=args.source_folder, - build_folder=args.build_folder, - package_folder=args.package_folder, - profile_name=args.profile, force=args.force, - settings=args.settings, options=args.options) + reference = ConanFileReference.loads(args.reference) + + return self._conan.download(reference=reference, package=args.package, remote=args.remote) def install(self, *args): - """Installs the requirements specified in a 'conanfile.py' or 'conanfile.txt'. - It can also be used to install a concrete recipe/package specified by the reference parameter. - If the recipe is not found in the local cache it will retrieve the recipe from a remote, - looking for it sequentially in the available configured remotes. - When the recipe has been downloaded it will try to download a binary package matching + """Installs the requirements specified in a conanfile (.py or .txt). + If any requirement is not found in the local cache it will retrieve the recipe from a + remote, looking for it sequentially in the available configured remotes. + When the recipes have been downloaded it will try to download a binary package matching the specified settings, only from the remote from which the recipe was retrieved. - If no binary package is found you can build the package from sources using the '--build' option. + If no binary package is found you can build the package from sources using the '--build' + option. + When the package is installed, Conan will write the files for the specified generators. + It can also be used to install a concrete recipe/package specifying a reference in the + "path" parameter. + """ parser = argparse.ArgumentParser(description=self.install.__doc__, prog="conan install") - parser.add_argument("reference", nargs='?', default="", - help='package recipe reference' - 'e.g., MyPackage/1.2@user/channel or ./my_project/') - parser.add_argument("--package", "-p", nargs=1, action=Extender, - help='Force install specified package ID (ignore settings/options)') - parser.add_argument("--all", action='store_true', default=False, - help='Install all packages from the specified package recipe') + parser.add_argument("path", nargs='?', default="", + help='path to a recipe (conanfile.py). e.g., ./my_project/') parser.add_argument("--file", "-f", help="specify conanfile filename") parser.add_argument("--generator", "-g", nargs=1, action=Extender, help='Generators to use') parser.add_argument("--werror", action='store_true', default=False, help='Error instead of warnings for graph inconsistencies') - parser.add_argument("--cwd", "-c", help='Use this directory as the current directory') + parser.add_argument("--install-folder", "--install_folder", "-if", + help='Use this directory as the directory where to put the generator' + 'files, conaninfo/conanbuildinfo.txt etc.') _add_manifests_arguments(parser) @@ -257,18 +295,33 @@ def install(self, *args): args = parser.parse_args(*args) - return self._conan.install(reference=args.reference, package=args.package, - settings=args.settings, options=args.options, - env=args.env, scope=args.scope, all=args.all, - remote=args.remote, werror=args.werror, - verify=args.verify, manifests=args.manifests, - manifests_interactive=args.manifests_interactive, - build=args.build, profile_name=args.profile, update=args.update, - generator=args.generator, no_imports=args.no_imports, - filename=args.file, cwd=args.cwd) + try: + reference = ConanFileReference.loads(args.path) + except ConanException: + return self._conan.install(path=args.path, + settings=args.settings, options=args.options, + env=args.env, scope=args.scope, + remote=args.remote, werror=args.werror, + 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, filename=args.file, + install_folder=args.install_folder) + else: + return self._conan.install_reference(reference, settings=args.settings, + options=args.options, + env=args.env, scope=args.scope, + remote=args.remote, werror=args.werror, + 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) def config(self, *args): - """Manages conan configuration information + """Manages configuration. Edits the conan.conf or installs config files. """ parser = argparse.ArgumentParser(description=self.config.__doc__, prog="conan config") @@ -277,7 +330,8 @@ def config(self, *args): set_subparser = subparsers.add_parser('set', help='set/add value') get_subparser = subparsers.add_parser('get', help='get the value of existing element') install_subparser = subparsers.add_parser('install', - help='install a full configuration from a zip file, local or remote') + help='install a full configuration from a zip ' + 'file, local or remote') rm_subparser.add_argument("item", help="item to remove") get_subparser.add_argument("item", nargs="?", help="item to print") @@ -299,13 +353,13 @@ def config(self, *args): return self._conan.config_install(args.item) def info(self, *args): - """Prints information about a package recipe's dependency graph. - You can use it for your current project (just point to the path of your conanfile - if you want), or for any existing package in your local cache. + """Gets information about the dependency graph of a recipe. + You can use it for your current project, by passing a path to a conanfile.py as the + reference, or for any existing package in your local cache. """ - info_only_options = ["id", "build_id", "remote", "url", "license", "requires", "update", "required", - "date", "author", "None"] + info_only_options = ["id", "build_id", "remote", "url", "license", "requires", "update", + "required", "date", "author", "None"] path_only_options = ["export_folder", "build_folder", "package_folder", "source_folder"] str_path_only_options = ", ".join(['"%s"' % field for field in path_only_options]) str_only_options = ", ".join(['"%s"' % field for field in info_only_options]) @@ -317,24 +371,25 @@ def info(self, *args): parser.add_argument("--file", "-f", help="specify conanfile filename") parser.add_argument("--only", "-n", nargs=1, action=Extender, help='show the specified fields only from: ' - '%s or use --paths with options %s. Use --only None to show only references.' + '%s or use --paths with options %s. Use --only None to show only ' + 'references.' % (str_only_options, str_path_only_options)) parser.add_argument("--paths", action='store_true', default=False, help='Show package paths in local cache') - parser.add_argument("--package_filter", nargs='?', + parser.add_argument("--package-filter", "--package_filter", nargs='?', help='print information only for packages that match the filter' 'e.g., MyPackage/1.2@user/channel or MyPackage*') - parser.add_argument("--build_order", "-bo", + parser.add_argument("--build-order", "--build_order", "-bo", help='given a modified reference, return an ordered list to build (CI)', nargs=1, action=Extender) parser.add_argument("--json", "-j", nargs='?', const="1", type=str, - help='Only with --build_order option, return the information in a json. e.j' - ' --json=/path/to/filename.json or --json to output the json') + help='Only with --build_order option, return the information in a json.' + ' e.j --json=/path/to/filename.json or --json to output the json') parser.add_argument("--graph", "-g", help='Creates file with project dependencies graph. It will generate ' 'a DOT or HTML file depending on the filename extension') - parser.add_argument("--cwd", "-c", help='Use this directory as the current directory') - build_help = 'given a build policy (same install command "build" parameter), return an ordered list of ' \ + build_help = 'given a build policy (same install command "build" parameter), return an ' \ + 'ordered list of ' \ 'packages that would be built from sources in install command (simulation)' _add_common_install_arguments(parser, build_help=build_help) @@ -342,13 +397,16 @@ def info(self, *args): # BUILD ORDER ONLY if args.build_order: - ret = self._conan.info_build_order(args.reference, settings=args.settings, options=args.options, - env=args.env, scope=args.scope, profile_name=args.profile, - filename=args.file, remote=args.remote, build_order=args.build_order, - check_updates=args.update, cwd=args.cwd) + ret = self._conan.info_build_order(args.reference, settings=args.settings, + options=args.options, + env=args.env, scope=args.scope, + profile_name=args.profile, + filename=args.file, remote=args.remote, + build_order=args.build_order, + check_updates=args.update) if args.json: json_arg = True if args.json == "1" else args.json - self._outputer.json_build_order(ret, json_arg, args.cwd) + self._outputer.json_build_order(ret, json_arg, os.getcwd()) else: self._outputer.build_order(ret) @@ -356,146 +414,263 @@ def info(self, *args): elif args.build is not None: nodes, _ = self._conan.info_nodes_to_build(args.reference, build_modes=args.build, settings=args.settings, - options=args.options, env=args.env, scope=args.scope, - profile_name=args.profile, filename=args.file, - remote=args.remote, check_updates=args.update, cwd=args.cwd) + options=args.options, env=args.env, + scope=args.scope, + profile_name=args.profile, + filename=args.file, + remote=args.remote, + check_updates=args.update) self._outputer.nodes_to_build(nodes) # INFO ABOUT DEPS OF CURRENT PROJECT OR REFERENCE else: - data = self._conan.info_get_graph(args.reference, remote=args.remote, settings=args.settings, + data = self._conan.info_get_graph(args.reference, remote=args.remote, + settings=args.settings, options=args.options, env=args.env, scope=args.scope, profile_name=args.profile, update=args.update, - filename=args.file, cwd=args.cwd) + filename=args.file) deps_graph, graph_updates_info, project_reference = data only = args.only if args.only == ["None"]: only = [] if only and args.paths and (set(only) - set(path_only_options)): - raise ConanException("Invalid --only value '%s' with --path specified, allowed values: [%s]." - % (only, str_path_only_options)) + raise ConanException("Invalid --only value '%s' with --path specified, allowed " + "values: [%s]." % (only, str_path_only_options)) elif only and not args.paths and (set(only) - set(info_only_options)): raise ConanException("Invalid --only value '%s', allowed values: [%s].\n" "Use --only=None to show only the references." % (only, str_only_options)) if args.graph: - self._outputer.info_graph(args.graph, deps_graph, project_reference, args.cwd) + self._outputer.info_graph(args.graph, deps_graph, project_reference, os.getcwd()) else: - self._outputer.info(deps_graph, graph_updates_info, only, args.remote, args.package_filter, args.paths, - project_reference) + self._outputer.info(deps_graph, graph_updates_info, only, args.remote, + args.package_filter, args.paths, project_reference) return + def source(self, *args): + """ Calls your local conanfile.py 'source()' method. + I.e., downloads and unzip the package source. + """ + parser = argparse.ArgumentParser(description=self.source.__doc__, prog="conan source") + parser.add_argument("path", help='path to a recipe (conanfile.py), e.g., conan source .') + + parser.add_argument("--source-folder", "--source_folder", "-s", + help='Destination directory. Defaulted to current directory') + parser.add_argument("--install-folder", "-if", + help="Optional. Local folder containing the conaninfo.txt and " + "conanbuildinfo.txt " + "files (from a previous conan install execution). Defaulted to the " + "current directory. Optional, source method will run without the " + "information retrieved from the conaninfo.txt and conanbuildinfo.txt, " + "only required when using conditional source() based on settings, " + "options, env_info and user_info ") + args = parser.parse_args(*args) + + try: + if "@" in args.path and ConanFileReference.loads(args.path): + raise ArgumentError(None, + "'conan source' doesn't accept a reference anymore. " + "If you were using it as a concurrency workaround, " + "you can call 'conan install' simultaneously from several " + "different processes, the concurrency is now natively supported" + ". The path parameter should be a folder containing a " + "conanfile.py file.") + except ConanException: + pass + + return self._conan.source(args.path, args.source_folder, args.install_folder) + def build(self, *args): - """ Utility command to run your current project 'conanfile.py' build() method. - It doesn't work for 'conanfile.txt'. It is convenient for automatic translation - of conan settings and options, for example to CMake syntax, as it can be done by - the CMake helper. It is also a good starting point if you would like to create - a package from your current project. + """ Calls your local conanfile.py 'build()' method. + The recipe will be built in the local directory specified by --build_folder, + reading the sources from --source_folder. If you are using a build helper, like CMake(), the + --package_folder will be configured as destination folder for the install step. """ + parser = argparse.ArgumentParser(description=self.build.__doc__, prog="conan build") - parser.add_argument("path", nargs="?", - help='path to conanfile.py, e.g., conan build .', - default="") + parser.add_argument("path", help='path to a recipe (conanfile.py), e.g., conan build .') parser.add_argument("--file", "-f", help="specify conanfile filename") - parser.add_argument("--source_folder", "-sf", help="local folder containing the sources") - parser.add_argument("--package_folder", "-pf", help="local folder to install the package " - "(when the build system and build() do it)") + parser.add_argument("--source-folder", "--source_folder", "-sf", + help="local folder containing the sources. Defaulted to the directory " + "of the conanfile. A relative path can also be specified " + "(relative to the current directory)") + parser.add_argument("--build-folder", "--build_folder", "-bf", + help="build folder, working directory of the build process. Defaulted " + "to the current directory. A relative path can also be specified " + "(relative to the current directory)") + parser.add_argument("--package-folder", "--package_folder", "-pf", + help="folder to install the package (when the build system or build() " + "method does it). Defaulted to the '{build_folder}/package' folder" + ". A relative path can be specified, relative to the current " + " folder. Also an absolute path is allowed.") + parser.add_argument("--install-folder", "--install_folder", "-if", + help="Optional. Local folder containing the conaninfo.txt and " + "conanbuildinfo.txt files (from a previous conan install " + "execution). Defaulted to --build-folder") args = parser.parse_args(*args) return self._conan.build(path=args.path, source_folder=args.source_folder, - package_folder=args.package_folder, filename=args.file) + package_folder=args.package_folder, filename=args.file, + build_folder=args.build_folder, + install_folder=args.install_folder) def package(self, *args): - """ Calls your conanfile.py 'package' method for a specific package recipe. - It won't create a new package, use 'install' or 'test_package' instead for - creating packages in the conan local cache, or 'build' for conanfile.py in user space. - - Intended for package creators, for regenerating a package without recompiling - the source, i.e. for troubleshooting, and fixing the package() method, not - normal operation. - - It requires the package has been built locally, it won't - re-package otherwise. When used in a user space project, it - will execute from the build folder specified as parameter, and the current - directory. This is useful while creating package recipes or just for - extracting artifacts from the current project, without even being a package - - This command also works locally, in the user space, and it will copy artifacts from the provided - folder to the current one. - """ - parser = argparse.ArgumentParser(description=self.package.__doc__, prog="conan package") - parser.add_argument("reference", help='package recipe reference ' - 'e.g. MyPkg/0.1@user/channel, or local path to the build folder' - ' (relative or absolute)') - parser.add_argument("package_id", nargs="?", default="", - help='Package ID to regenerate. e.g., ' - '9cf83afd07b678d38a9c1645f605875400847ff3' - ' This optional parameter is only used for the local conan ' - 'cache. If not specified, ALL binaries for this recipe are ' - 're-packaged') - parser.add_argument("--build_folder", "-bf", help="local folder containing the build") - parser.add_argument("--source_folder", "-sf", help="local folder containing the sources") + """ Calls your local conanfile.py 'package()' method. - args = parser.parse_args(*args) - return self._conan.package(reference=args.reference, package_id=args.package_id, - build_folder=args.build_folder, - source_folder=args.source_folder) + This command works locally, in the user space, and it will copy artifacts from the + --build_folder and --source_folder folder to the --package_folder one. - def source(self, *args): - """ Calls your conanfile.py 'source()' method to configure the source directory. - I.e., downloads and unzip the package source. + It won't create a new package in the local cache, if you want to do it, use 'create' or use + 'export-pkg' after a 'build' command. """ - parser = argparse.ArgumentParser(description=self.source.__doc__, prog="conan source") - parser.add_argument("reference", nargs='?', - default="", - help="package recipe reference. e.g., MyPackage/1.2@user/channel " - "or ./my_project/") - parser.add_argument("-f", "--force", default=False, - action="store_true", - help="In the case of local cache, force the removal of the source" - " folder, then the execution and retrieval of the source code." - " Otherwise, if the code has already been retrieved, it will" - " do nothing.") - parser.add_argument("--cwd", "-c", help='Use this directory as the current directory') - + parser = argparse.ArgumentParser(description=self.package.__doc__, prog="conan package") + parser.add_argument("path", help='path to a recipe (conanfile.py), e.g., conan package .') + parser.add_argument("--source-folder", "--source_folder", "-sf", + help="local folder containing the sources. Defaulted to the directory " + "of the conanfile. A relative path can also be specified " + "(relative to the current directory)") + parser.add_argument("--build-folder", "--build_folder", "-bf", + help="build folder, working directory of the build process. Defaulted " + "to the current directory. A relative path can also be specified " + "(relative to the current directory)") + parser.add_argument("--package-folder", "--package_folder", "-pf", + help="folder to install the package. Defaulted to the " + "'{build_folder}/package' folder. A relative path can be specified" + " (relative to the current directory). Also an absolute path" + " is allowed.") + parser.add_argument("--install-folder", "--install_folder", "-if", + help="Optional. Local folder containing the conaninfo.txt and " + "conanbuildinfo.txt files (from a previous conan install " + "execution). Defaulted to --build-folder ") args = parser.parse_args(*args) - return self._conan.source(args.reference, args.force, cwd=args.cwd) + try: + if "@" in args.path and ConanFileReference.loads(args.path): + raise ArgumentError(None, + "'conan package' doesn't accept a reference anymore. " + " The path parameter should be a folder containing a " + "conanfile.py file. If you were using the 'conan package' " + "command for development purposes we recommend to use " + "the local development commands: 'conan build' + " + "'conan package' and finally 'conan create' to regenerate the " + "package, or 'conan export_package' to store the already built " + "binaries in the local cache without rebuilding them.") + except ConanException: + pass + + return self._conan.package(path=args.path, build_folder=args.build_folder, + source_folder=args.source_folder, + package_folder=args.package_folder, + install_folder=args.install_folder) def imports(self, *args): - """ Execute the 'imports' stage of a conanfile.txt or a conanfile.py. - It requires to have been previously installed and have a conanbuildinfo.txt generated file. + """ Calls your local conanfile.py or conanfile.txt 'imports' method. + It requires to have been previously installed and have a conanbuildinfo.txt generated file + in the --install-folder (defaulted to current directory). """ parser = argparse.ArgumentParser(description=self.imports.__doc__, prog="conan imports") - parser.add_argument("reference", nargs='?', default="", - help="Specify the location of the folder containing the conanfile." - "By default it will be the current directory. It can also use a full " - "reference e.g. openssl/1.0.2@lasote/testing and the recipe " - "'imports()' for that package in the local conan cache will be used ") + parser.add_argument("path", + help="path to a recipe (conanfile.py). e.g., ./my_project/" + "With --undo option, this parameter is the folder " + "containing the conan_imports_manifest.txt file generated in a previous" + "execution. e.j: conan imports ./imported_files --undo ") parser.add_argument("--file", "-f", help="Use another filename, " "e.g.: conan imports -f=conanfile2.py") - parser.add_argument("-d", "--dest", + parser.add_argument("--import-folder", "--import_folder", "-imf", help="Directory to copy the artifacts to. By default it will be the" " current directory") + parser.add_argument("--install-folder", "--install_folder", "-if", + help="local folder containing the conaninfo.txt and conanbuildinfo.txt " + "files (from a previous conan install execution)") parser.add_argument("-u", "--undo", default=False, action="store_true", help="Undo imports. Remove imported files") + args = parser.parse_args(*args) + + if args.undo: + return self._conan.imports_undo(args.path) + + try: + if "@" in args.path and ConanFileReference.loads(args.path): + raise ArgumentError(None, "Parameter 'path' cannot be a reference," + " but a folder containing a conanfile.py or conanfile.txt" + " file.") + except ConanException: + pass + + return self._conan.imports(args.path, args.import_folder, args.file, args.install_folder) + + def export_pkg(self, *args): + """Exports a recipe & creates a package with given files calling 'package'. + It executes the package() method applied to the local folders '--source_folder' and + '--build_folder' and creates a new package in the local cache for the specified + 'reference' and for the specified '--settings', '--options' and or '--profile'. + """ + parser = argparse.ArgumentParser(description=self.export_pkg.__doc__, + prog="conan export-pkg .") + parser.add_argument("path", help='path to a recipe (conanfile.py). e.j: "." ') + parser.add_argument("reference", help='user/channel, or a full package reference' + ' (Pkg/version@user/channel), if name and version ' + ' are not declared in the recipe (conanfile.py)') + parser.add_argument("--source-folder", "--source_folder", "-sf", + help="local folder containing the sources. Defaulted to --build-folder." + " A relative path to the current dir can also be specified" ) + parser.add_argument("--build-folder", "--build_folder", "-bf", + help="build folder, working directory of the build process. Defaulted " + "to the current directory. A relative path can also be specified " + "(relative to the current directory)") + parser.add_argument("--install-folder", "-if", + help="local folder containing the conaninfo.txt and conanbuildinfo.txt " + "files (from a previous conan install execution). Defaulted to " + "--build-folder. If these files are found in the specified folder, " + "they will be used, then if you specify --profile, -s, -o, --env, " + "it will raise an error.") + parser.add_argument("--profile", "-pr", + help='Profile for this package') + parser.add_argument("--options", "-o", + help='Define options values, e.g., -o Pkg:with_qt=true', + nargs=1, action=Extender) + parser.add_argument("--settings", "-s", + help='Define settings values, e.g., -s compiler=gcc', + nargs=1, action=Extender) + parser.add_argument("--env", "-e", + help='Environment variables that will be set during the package build, ' + '-e CXX=/usr/bin/clang++', + nargs=1, action=Extender) + parser.add_argument('-f', '--force', default=False, + action='store_true', help='Overwrite existing package if existing') args = parser.parse_args(*args) - return self._conan.imports(args.reference, args.undo, args.dest, args.file) + name, version, user, channel = get_reference_fields(args.reference) + + return self._conan.export_pkg(path=args.path, + name=name, + version=version, + source_folder=args.source_folder, + build_folder=args.build_folder, + install_folder=args.install_folder, + profile_name=args.profile, + env=args.env, + settings=args.settings, + options=args.options, + force=args.force, + user=user, + channel=channel) def export(self, *args): - """ Copies the package recipe (conanfile.py and associated files) to your local cache. - From the local cache it can be shared and reused in other projects. - Also, from the local cache, it can be uploaded to any remote with the "upload" command. + """ Copies the recipe (conanfile.py & associated files) to your local cache. + Use the 'reference' param to specify a user and channel where to export. + Once the recipe is in the local cache it can be shared and reused. It can be uploaded + to any remote with the "conan upload" command. """ parser = argparse.ArgumentParser(description=self.export.__doc__, prog="conan export") - parser.add_argument("reference", help='a full package reference Pkg/version@user/channel, ' - 'or just the user/channel if package and version are defined in recipe') + parser.add_argument("reference", help='user/channel, or a full package reference' + ' (Pkg/version@user/channel), if name and version ' + ' are not declared in the recipe') parser.add_argument('--path', '-p', default=None, help='Optional. Folder with a %s. Default current directory.' % CONANFILE) + parser.add_argument("--file", "-f", help="specify conanfile filename") parser.add_argument('--keep-source', '-k', default=False, action='store_true', help='Optional. Do not remove the source folder in the local cache. ' 'Use for testing purposes only') - parser.add_argument("--file", "-f", help="specify conanfile filename") args = parser.parse_args(*args) name, version, user, channel = get_reference_fields(args.reference) @@ -504,7 +679,7 @@ def export(self, *args): name=name, version=version) def remove(self, *args): - """Remove any package recipe or binary matching a pattern. + """Removes packages or binaries matching pattern from local cache or remote. It can also be used to remove temporary source or build folders in the local conan cache. If no remote is specified, the removal will be done by default in the local conan cache. """ @@ -546,7 +721,7 @@ def remove(self, *args): force=args.force, remote=args.remote, outdated=args.outdated) def copy(self, *args): - """ Copy conan recipes and packages to another user/channel. + """ Copies conan recipes and packages to another user/channel. Useful to promote packages (e.g. from "beta" to "stable"). Also for moving packages from one user to another. """ @@ -566,11 +741,13 @@ def copy(self, *args): default=False, help='Override destination packages and the package recipe') args = parser.parse_args(*args) - return self._conan.copy(reference=args.reference, user_channel=args.user_channel, force=args.force, + return self._conan.copy(reference=args.reference, user_channel=args.user_channel, + force=args.force, all=args.all, package=args.package) def user(self, *parameters): - """ Update your cached user name (and auth token) to avoid it being requested later. + """ Authenticates against a remote with user/pass, caching the auth token. + Useful to avoid the user and password being requested later. e.g. while you're uploading a package. You can have more than one user (one per remote). Changing the user, or introducing the password is only necessary to upload packages to a remote. @@ -579,26 +756,35 @@ def user(self, *parameters): parser.add_argument("name", nargs='?', default=None, help='Username you want to use. ' 'If no name is provided it will show the current user.') - parser.add_argument("-p", "--password", help='User password. Use double quotes ' - 'if password with spacing, and escape quotes if existing') parser.add_argument("--remote", "-r", help='look in the specified remote server') parser.add_argument('-c', '--clean', default=False, action='store_true', help='Remove user and tokens for all remotes') + parser.add_argument("-p", "--password", nargs='?', const="", type=str, + help='User password. Use double quotes if password with spacing, and escape quotes if ' + 'existing. If empty, the password is requested interactively (not exposed)') args = parser.parse_args(*parameters) # To enable -h - return self._conan.user(name=args.name, clean=args.clean, remote=args.remote, password=args.password) + return self._conan.user(name=args.name, clean=args.clean, remote=args.remote, + password=args.password) def search(self, *args): - """ Search package recipes and binaries in the local cache or in a remote server. + """ Searches package recipes and binaries in the local cache or in a remote. + If you provide a pattern, then it will search for existing package recipes matching that pattern. + If a full and complete package reference is provided, like Pkg/0.1@user/channel, then the existing + binary packages for that reference will be displayed. You can search in a remote or in the local cache, if nothing is specified, the local conan cache is - assumed + assumed. + Search is case sensitive, exact case has to be used. For case insensitive file systems, like Windows, + case sensitive search can be forced with the --case-sensitive argument """ parser = argparse.ArgumentParser(description=self.search.__doc__, prog="conan search") parser.add_argument('pattern', nargs='?', help='Pattern name, e.g. openssl/* or package' ' recipe reference if "-q" is used. e.g. ' 'MyPackage/1.2@user/channel') parser.add_argument('--case-sensitive', default=False, - action='store_true', help='Make a case-sensitive search') + action='store_true', help='Make a case-sensitive search. ' + 'Use it to guarantee case-sensitive ' + 'search in Windows or other case-insensitive filesystems') parser.add_argument('-r', '--remote', help='Remote origin') parser.add_argument('--raw', default=False, action='store_true', help='Print just the list of recipes') @@ -621,7 +807,7 @@ def search(self, *args): # Fixes a version with only a wilcard (valid reference) but not real reference # e.j: conan search lib/*@lasote/stable reference = None - except: + except (TypeError, ConanException): reference = None if reference: @@ -641,19 +827,30 @@ def search(self, *args): self._outputer.print_search_references(refs, args.pattern, args.raw) def upload(self, *args): - """ Uploads a package recipe and the generated binary packages to a specified remote + """ Uploads a recipe and binary packages to a remote. + + If you use the --force variable, it won't check the package date. It will override + the remote with the local package. + If you use a pattern instead of a conan recipe reference you can use the -c or + --confirm option to upload all the matching recipes. + If you use the --retry option you can specify how many times should conan try to upload + the packages in case of failure. The default is 2. + With --retry_wait you can specify the seconds to wait between upload attempts. + If no remote is specified, the first configured remote (by default conan.io, use + 'conan remote list' to list the remotes) will be used. """ parser = argparse.ArgumentParser(description=self.upload.__doc__, prog="conan upload") - parser.add_argument('pattern', help='Pattern or package recipe reference, e.g., "openssl/*", ' - '"MyPackage/1.2@user/channel"') + parser.add_argument('pattern', help='Pattern or package recipe reference, ' + 'e.g., "openssl/*", "MyPackage/1.2@user/channel"') # TODO: packageparser.add_argument('package', help='user name') parser.add_argument("--package", "-p", default=None, help='package ID to upload') parser.add_argument("--remote", "-r", help='upload to this specific remote') parser.add_argument("--all", action='store_true', default=False, help='Upload both package recipe and packages') - parser.add_argument("--skip_upload", action='store_true', - default=False, help='Do not upload anything, just run the checks and the compression.') + parser.add_argument("--skip-upload", "--skip_upload", action='store_true', + default=False, help='Do not upload anything, just run ' + 'the checks and the compression.') parser.add_argument("--force", action='store_true', default=False, help='Do not check conan recipe date, override remote with local') @@ -661,20 +858,23 @@ def upload(self, *args): default=False, help='Perform an integrity check, using the manifests, before upload') parser.add_argument('--confirm', '-c', default=False, - action='store_true', help='If pattern is given upload all matching recipes without ' - 'confirmation') + action='store_true', + help='If pattern is given upload all matching recipes without ' + 'confirmation') parser.add_argument('--retry', default=2, type=int, help='In case of fail retries to upload again the specified times') - parser.add_argument('--retry_wait', default=5, type=int, + parser.add_argument('--retry-wait', '--retry_wait', default=5, type=int, help='Waits specified seconds before retry again') args = parser.parse_args(*args) - return self._conan.upload(pattern=args.pattern, package=args.package, remote=args.remote, all=args.all, - force=args.force, confirm=args.confirm, retry=args.retry, retry_wait=args.retry_wait, + return self._conan.upload(pattern=args.pattern, package=args.package, remote=args.remote, + all=args.all, + force=args.force, confirm=args.confirm, retry=args.retry, + retry_wait=args.retry_wait, skip_upload=args.skip_upload, integrity_check=args.check) def remote(self, *args): - """ Handles the remote list and the package recipes associated to a remote. + """ Manages the remote list and the package recipes associated to a remote. """ parser = argparse.ArgumentParser(description=self.remote.__doc__, prog="conan remote") subparsers = parser.add_subparsers(dest='subcommand', help='sub-command help') @@ -684,7 +884,8 @@ def remote(self, *args): parser_add = subparsers.add_parser('add', help='add a remote') parser_add.add_argument('remote', help='name of the remote') parser_add.add_argument('url', help='url of the remote') - parser_add.add_argument('verify_ssl', help='Verify SSL certificated. Default True', + parser_add.add_argument('verify_ssl', + help='Verify SSL certificated. Default True', default="True", nargs="?") parser_add.add_argument("-i", "--insert", nargs="?", const=0, type=int, help="insert remote at specific index") @@ -693,7 +894,8 @@ def remote(self, *args): parser_upd = subparsers.add_parser('update', help='update the remote url') parser_upd.add_argument('remote', help='name of the remote') parser_upd.add_argument('url', help='url') - parser_upd.add_argument('verify_ssl', help='Verify SSL certificated. Default True', + parser_upd.add_argument('verify_ssl', + help='Verify SSL certificated. Default True', default="True", nargs="?") parser_upd.add_argument("-i", "--insert", nargs="?", const=0, type=int, help="insert remote at specific index") @@ -740,7 +942,7 @@ def remote(self, *args): return self._conan.remote_update_ref(reference, remote) def profile(self, *args): - """ List profiles in the '.conan/profiles' folder, or show profile details. + """ Lists profiles in the '.conan/profiles' folder, or shows profile details. The 'list' subcommand will always use the default user 'conan/profiles' folder. But the 'show' subcommand is able to resolve absolute and relative paths, as well as to map names to '.conan/profiles' folder, in the same way as the '--profile' install argument. @@ -800,17 +1002,20 @@ def profile(self, *args): return def get(self, *args): - """ Gets a file or list a directory of a given reference or package + """ Gets a file or list a directory of a given reference or package. """ parser = argparse.ArgumentParser(description=self.get.__doc__, prog="conan get") parser.add_argument('reference', help='package recipe reference') - parser.add_argument('path', help='Path to the file or directory. If not specified will get the conafile' - ' if only a reference is specified and a conaninfo.txt file contents if ' - 'the package is also specified', default=None, nargs="?") + parser.add_argument('path', + help='Path to the file or directory. If not specified will get the ' + 'conafile if only a reference is specified and a conaninfo.txt ' + 'file contents if the package is also specified', + default=None, nargs="?") parser.add_argument("--package", "-p", default=None, help='package ID') parser.add_argument("--remote", "-r", help='Get from this specific remote') - parser.add_argument("--raw", "-raw", help='Do not decorate the text', default=False, action='store_true') + parser.add_argument("--raw", "-raw", help='Do not decorate the text', default=False, + action='store_true') args = parser.parse_args(*args) ret, path = self._conan.get_path(args.reference, args.package, args.path, args.remote) @@ -822,7 +1027,7 @@ def get(self, *args): return def alias(self, *args): - """ Creates and export an alias recipe + """ Creates and exports an 'alias recipe'. """ parser = argparse.ArgumentParser(description=self.upload.__doc__, prog="conan alias") @@ -837,15 +1042,36 @@ def alias(self, *args): def _show_help(self): """ prints a summary of all commands """ - self._user_io.out.writeln('Conan commands. Type $conan "command" -h for help', - Color.BRIGHT_YELLOW) - commands = self._commands() - # future-proof way to ensure tabular formatting - max_len = max((len(c) for c in commands)) + 2 + grps = [("Consumer commands", ("install", "config", "get", "info", "search")), + ("Creator commands", ("new", "create", "upload", "export", "export-pkg", "test")), + ("Package development commands", ("source", "build", "package")), + ("Misc commands", ("profile", "remote", "user", "imports", "copy", "remove", + "alias", "download")), + ("Deprecated", ("test_package",))] + + def check_all_commands_listed(): + """Keep updated the main directory, raise if don't""" + all_commands = self._commands() + all_in_grps = [command for _, command_list in grps for command in command_list] + if set(all_in_grps) != set(all_commands): + diff = set(all_commands) - set(all_in_grps) + raise Exception("Some command is missing in the main help: %s" % ",".join(diff)) + return all_commands + + commands = check_all_commands_listed() + max_len = max((len(c) for c in commands)) + 1 fmt = ' %-{}s'.format(max_len) - for name in sorted(self._commands()): - self._user_io.out.write(fmt % name, Color.GREEN) - self._user_io.out.writeln(commands[name].__doc__.split('\n', 1)[0].strip()) + + for group_name, comm_names in grps: + self._user_io.out.writeln(group_name, Color.BRIGHT_MAGENTA) + for name in comm_names: + # future-proof way to ensure tabular formatting + self._user_io.out.write(fmt % name, Color.GREEN) + self._user_io.out.writeln(commands[name].__doc__.split('\n', 1)[0].strip()) + + self._user_io.out.writeln("") + self._user_io.out.writeln('Conan commands. Type "conan -h" for help', + Color.BRIGHT_YELLOW) def _commands(self): """ returns a list of available commands @@ -854,19 +1080,23 @@ def _commands(self): for m in inspect.getmembers(self, predicate=inspect.ismethod): method_name = m[0] if not method_name.startswith('_'): + if "export_pkg" == method_name: + method_name = "export-pkg" method = m[1] if method.__doc__ and not method.__doc__.startswith('HIDDEN'): result[method_name] = method return result - def _check_query_parameter_and_get_reference(self, pattern, query): + @staticmethod + def _check_query_parameter_and_get_reference(pattern, query): reference = None if pattern: try: reference = ConanFileReference.loads(pattern) except ConanException: if query is not None: - msg = "-q parameter only allowed with a valid recipe reference as search pattern. e.j conan search " \ + msg = "-q parameter only allowed with a valid recipe reference as search " \ + "pattern. e.j conan search " \ "MyPackage/1.2@user/channel -q \"os=Windows\"" raise ConanException(msg) return reference @@ -883,13 +1113,13 @@ def run(self, *args): method = commands[command] except KeyError as exc: if command in ["-v", "--version"]: - self._user_io.out.success("Conan version %s" % CLIENT_VERSION) + self._user_io.out.success("Conan version %s" % client_version) return False self._show_help() if command in ["-h", "--help"]: return False raise ConanException("Unknown command %s" % str(exc)) - except IndexError as exc: # No parameters + except IndexError: # No parameters self._show_help() return False method(args[0][1:]) @@ -961,16 +1191,19 @@ def _add_common_install_arguments(parser, build_help): help='Apply the specified profile to the install command') parser.add_argument("-r", "--remote", help='look in the specified remote server') parser.add_argument("--options", "-o", - help='Options to build the package, overwriting the defaults. e.g., -o with_qt=true', + help='Options to build the package, overwriting the defaults. e.g., ' + '-o with_qt=true', nargs=1, action=Extender) parser.add_argument("--settings", "-s", - help='Settings to build the package, overwriting the defaults. e.g., -s compiler=gcc', + help='Settings to build the package, overwriting the defaults. e.g., ' + '-s compiler=gcc', nargs=1, action=Extender) parser.add_argument("--env", "-e", - help='Environment variables that will be set during the package build, -e CXX=/usr/bin/clang++', + help='Environment variables that will be set during the package build, ' + '-e CXX=/usr/bin/clang++', nargs=1, action=Extender) - - parser.add_argument("--build", "-b", action=Extender, nargs="*", help=build_help) + if build_help: + parser.add_argument("--build", "-b", action=Extender, nargs="*", help=build_help) _help_build_policies = '''Optional, use it to choose if you want to build from sources: @@ -979,7 +1212,7 @@ def _add_common_install_arguments(parser, build_help): --build=never Default option. Never build, use binary packages or fail if a binary package is not found. --build=missing Build from code if a binary package is not found. --build=outdated Build from code if the binary is not built with the current recipe or when missing binary package. - --build=[pattern] Build always these packages from source, but never build the others. Allows multiple --build parameters. + --build=[pattern] Build always these packages from source, but never build the others. Allows multiple --build parameters. 'pattern' is a fnmatch file pattern of a package name. ''' @@ -988,17 +1221,17 @@ def main(args): parse parameters """ try: - conan_api = Conan.factory() + conan_api, client_cache, user_io = Conan.factory() except ConanException: # Error migrating sys.exit(-1) - outputer = CommandOutputer(conan_api._user_io, conan_api._client_cache) - command = Command(conan_api, conan_api._client_cache, conan_api._user_io, outputer) + outputer = CommandOutputer(user_io, client_cache) + command = Command(conan_api, client_cache, user_io, outputer) current_dir = os.getcwd() try: import signal - def sigint_handler(signal, frame): # @UnusedVariable + def sigint_handler(_, __): print('You pressed Ctrl+C!') sys.exit(0) diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py index 819729aaea4..dc37b0d9b4c 100644 --- a/conans/client/conan_api.py +++ b/conans/client/conan_api.py @@ -1,19 +1,19 @@ import hashlib import os import sys -from collections import defaultdict, OrderedDict import requests import conans -from conans import __version__ as CLIENT_VERSION, tools +from conans import __version__ as client_version, tools from conans.client.client_cache import ClientCache from conans.client.conf import MIN_SERVER_COMPATIBLE_VERSION, ConanClientConfigParser from conans.client.detect import detect_defaults_settings -from conans.client.manager import ConanManager +from conans.client.manager import ConanManager, existing_info_files from conans.client.migrations import ClientMigrator from conans.client.output import ConanOutput, ScopedOutput -from conans.client.profile_loader import read_profile, get_profile_path +from conans.client.profile_loader import read_profile, get_profile_path, profile_from_args, \ + read_conaninfo_profile from conans.client.remote_manager import RemoteManager from conans.client.remote_registry import RemoteRegistry from conans.client.rest.auth_manager import ConanApiAuthManager @@ -21,22 +21,25 @@ from conans.client.rest.version_checker import VersionCheckerRequester from conans.client.runner import ConanRunner from conans.client.store.localdb import LocalDB +from conans.client.package_tester import PackageTester from conans.client.userio import UserIO from conans.errors import ConanException from conans.model.env_info import EnvValues from conans.model.options import OptionsValues from conans.model.profile import Profile -from conans.model.ref import ConanFileReference, is_a_reference +from conans.model.ref import ConanFileReference from conans.model.scope import Scopes from conans.model.version import Version -from conans.paths import CONANFILE, get_conan_user_home +from conans.paths import CONANFILE, get_conan_user_home, CONANFILE_TXT, CONANINFO, BUILD_INFO from conans.search.search import DiskSearchManager, DiskSearchAdapter from conans.util.env_reader import get_env -from conans.util.files import rmdir, save_files, exception_message_safe, save +from conans.util.files import rmdir, save_files, exception_message_safe, save, mkdir from conans.util.log import configure_logger from conans.util.tracer import log_command, log_exception from conans.client.loader_parse import load_conanfile_class from conans.client import settings_preprocessor +from conans.tools import set_global_instances +from conans.client.uploader import is_a_reference default_manifest_folder = '.conan_manifests' @@ -91,12 +94,13 @@ def factory(): def instance_remote_manager(client_cache): requester = get_basic_requester(client_cache) # Verify client version against remotes - version_checker_requester = VersionCheckerRequester(requester, Version(CLIENT_VERSION), - Version(MIN_SERVER_COMPATIBLE_VERSION), - out) + version_checker_req = VersionCheckerRequester(requester, Version(client_version), + Version(MIN_SERVER_COMPATIBLE_VERSION), + out) # To handle remote connections put_headers = client_cache.read_put_headers() - rest_api_client = RestApiClient(out, requester=version_checker_requester, put_headers=put_headers) + rest_api_client = RestApiClient(out, requester=version_checker_req, + put_headers=put_headers) # To store user and token localdb = LocalDB(client_cache.localdb) # Wraps RestApiClient to add authentication support (same interface) @@ -136,7 +140,7 @@ def instance_remote_manager(client_cache): conan = Conan(client_cache, user_io, get_conan_runner(), remote_manager, search_manager, settings_preprocessor) - return conan + return conan, client_cache, user_io def __init__(self, client_cache, user_io, runner, remote_manager, search_manager, settings_preprocessor): @@ -148,13 +152,13 @@ def __init__(self, client_cache, user_io, runner, remote_manager, search_manager self._manager = ConanManager(client_cache, user_io, runner, remote_manager, search_manager, settings_preprocessor) # Patch the tools module with a good requester and user_io - tools._global_requester = get_basic_requester(self._client_cache) - tools._global_output = self._user_io.out + set_global_instances(self._user_io.out, get_basic_requester(self._client_cache)) @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, osx_clang_versions=None, - shared=None, upload_url=None, gitignore=None, gitlab_gcc_versions=None, gitlab_clang_versions=None): + 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, + osx_clang_versions=None, shared=None, upload_url=None, gitignore=None, + gitlab_gcc_versions=None, gitlab_clang_versions=None): from conans.client.new import get_files cwd = prepare_cwd(cwd) files = get_files(name, header=header, pure_c=pure_c, test=test, @@ -171,6 +175,25 @@ def new(self, name, header=False, pure_c=False, test=False, exports_sources=Fals for f in sorted(files): self._user_io.out.success("File saved: %s" % f) + @api_method + def test(self, path, profile_name=None, settings=None, options=None, env=None, remote=None, + update=False, user=None, channel=None, name=None, + version=None, build_modes=None): + + settings = settings or [] + options = options or [] + env = env or [] + cwd = os.getcwd() + base_folder = self._abs_relative_to(path, cwd, default=cwd) + conanfile_abs_path = self._get_conanfile_path(base_folder, "conanfile.py") + + profile = profile_from_args(profile_name, settings, options, env, None, cwd, + self._client_cache) + + pt = PackageTester(self._manager, self._user_io) + pt.install_build_and_test(conanfile_abs_path, profile, name, version, user, channel, remote, + update, build_modes=build_modes) + @api_method def test_package(self, profile_name=None, settings=None, options=None, env=None, scope=None, test_folder=None, not_export=False, build=None, keep_source=False, @@ -178,6 +201,12 @@ def test_package(self, profile_name=None, settings=None, options=None, env=None, manifests_interactive=None, remote=None, update=False, cwd=None, user=None, channel=None, name=None, version=None): + + self._user_io.out.warn("THIS METHOD IS DEPRECATED and will be removed. " + "Use 'conan create' to generate binary packages for a " + "recipe. If you want to test a package you can use 'conan test' " + "command.") + settings = settings or [] options = options or [] env = env or [] @@ -210,7 +239,7 @@ def test_package(self, profile_name=None, settings=None, options=None, env=None, # shutil.copytree(test_folder, build_folder) profile = profile_from_args(profile_name, settings, options, env, scope, cwd, - self._client_cache.profiles_path) + self._client_cache) loader = self._manager.get_loader(profile) test_conanfile = loader.load_conan(test_conanfile_path, self._user_io.out, consumer=True) @@ -248,7 +277,7 @@ def test_package(self, profile_name=None, settings=None, options=None, env=None, manifest_folder, manifest_interactive, manifest_verify = manifests self._manager.install(inject_require=conanfile_reference, reference=test_folder, - current_path=build_folder, + install_folder=build_folder, manifest_folder=manifest_folder, manifest_verify=manifest_verify, manifest_interactive=manifest_interactive, @@ -261,24 +290,29 @@ def test_package(self, profile_name=None, settings=None, options=None, env=None, test_conanfile = os.path.join(test_folder, CONANFILE) self._manager.build(test_conanfile, test_folder, build_folder, package_folder=None, + install_folder=build_folder, test=str(conanfile_reference)) @api_method def create(self, profile_name=None, settings=None, - options=None, env=None, scope=None, test_folder=None, not_export=False, build=None, + options=None, env=None, scope=None, test_folder=None, not_export=False, + build_modes=None, keep_source=False, verify=None, manifests=None, manifests_interactive=None, - remote=None, update=False, cwd=None, - user=None, channel=None, name=None, version=None): + remote=None, update=False, conan_file_path=None, filename=None, + user=None, channel=None, name=None, version=None, werror=False): settings = settings or [] options = options or [] env = env or [] - cwd = prepare_cwd(cwd) + self._user_io.out.werror_active = werror + + cwd = os.getcwd() + conanfile_folder = self._abs_relative_to(conan_file_path, cwd, default=cwd) if not name or not version: - conanfile_path = os.path.join(cwd, "conanfile.py") - conanfile = load_conanfile_class(conanfile_path) + conanfile_abs_path = self._get_conanfile_path(conanfile_folder, filename or CONANFILE) + conanfile = load_conanfile_class(conanfile_abs_path) name, version = conanfile.name, conanfile.version if not name or not version: raise ConanException("conanfile.py doesn't declare package name or version") @@ -288,121 +322,178 @@ def create(self, profile_name=None, settings=None, # Forcing an export! if not not_export: scoped_output.highlight("Exporting package recipe") - self._manager.export(user, channel, cwd, keep_source=keep_source, name=name, - version=version) + self._manager.export(user, channel, conanfile_folder, keep_source=keep_source, + name=name, version=version, filename=filename) - if build is None: # Not specified, force build the tested library - build = [name] + if build_modes is None: # Not specified, force build the tested library + build_modes = [name] manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd) manifest_folder, manifest_interactive, manifest_verify = manifests profile = profile_from_args(profile_name, settings, options, env, scope, - cwd, self._client_cache.profiles_path) + cwd, self._client_cache) self._manager.install(reference=reference, - current_path=cwd, + install_folder=None, # Not output anything manifest_folder=manifest_folder, manifest_verify=manifest_verify, manifest_interactive=manifest_interactive, remote=remote, profile=profile, - build_modes=build, - update=update - ) + build_modes=build_modes, + update=update, + filename=filename) - test_folders = [test_folder] if test_folder else ["test_package", "test"] - for test_folder_name in test_folders: - test_folder = os.path.join(cwd, test_folder_name) - test_conanfile_path = os.path.join(test_folder, "conanfile.py") - if os.path.exists(test_conanfile_path): - break + base_folder = self._abs_relative_to(conan_file_path, cwd, default=cwd) + + def get_test_conanfile_path(tf): + """Searchs in the declared test_folder or in the standard locations""" + test_folders = [tf] if tf else ["test_package", "test"] + + for test_folder_name in test_folders: + test_folder = os.path.join(base_folder, test_folder_name) + test_conanfile_path = os.path.join(test_folder, "conanfile.py") + if os.path.exists(test_conanfile_path): + return test_conanfile_path + else: + if tf: + raise ConanException("test folder '%s' not available, " + "or it doesn't have a conanfile.py" % tf) + + test_conanfile_path = get_test_conanfile_path(test_folder) + if test_conanfile_path: + pt = PackageTester(self._manager, self._user_io) + scoped_output.highlight("Testing with 'test_package'") + pt.install_build_and_test(test_conanfile_path, profile, name, version, user, + channel, remote, update) + + def _get_profile(self, profile_name, settings, options, env, cwd, install_folder): + + infos_present = existing_info_files(install_folder) + + if not infos_present: + profile = profile_from_args(profile_name, settings, options, env=env, scope=None, + cwd=cwd, client_cache=self._client_cache) else: - self._user_io.out.warn("test package folder not available, or it doesn't have " - "a conanfile.py\nIt is recommended to set a 'test_package' " - "while creating packages") - return + profile = read_conaninfo_profile(install_folder) - scoped_output.highlight("Testing with 'test_package'") - sha = hashlib.sha1("".join(options + settings).encode()).hexdigest() - build_folder = os.path.join(test_folder, "build", sha) - rmdir(build_folder) + return profile - test_conanfile = os.path.join(test_folder, CONANFILE) - self._manager.install(inject_require=reference, - reference=test_folder, - current_path=build_folder, + def _validate_can_read_infos(self, install_folder, cwd): + if install_folder and not existing_info_files(self._abs_relative_to(install_folder, cwd)): + raise ConanException("The specified --install-folder doesn't contain '%s' and '%s' " + "files" % (CONANINFO, BUILD_INFO)) + + @staticmethod + def _validate_one_settings_source(install_folder, profile_name, settings, options, env): + if install_folder and existing_info_files(install_folder) and \ + (profile_name or settings or options or env): + raise ConanException("%s and %s are found, at '%s' folder, so specifying profile, " + "settings, options or env is not allowed" % (CONANINFO, BUILD_INFO, + install_folder)) + + @api_method + def export_pkg(self, path, name, channel, source_folder=None, build_folder=None, + install_folder=None, profile_name=None, settings=None, options=None, + env=None, force=False, user=None, version=None): + + settings = settings or [] + options = options or [] + env = env or [] + cwd = os.getcwd() + + # Checks that info files exists if the install folder is specified + self._validate_can_read_infos(install_folder, cwd) + + path = self._abs_relative_to(path, cwd) + build_folder = self._abs_relative_to(build_folder, cwd, default=cwd) + install_folder = self._abs_relative_to(install_folder, cwd, default=build_folder) + source_folder = self._abs_relative_to(source_folder, cwd, default=build_folder) + + # Checks that no both settings and info files are specified + self._validate_one_settings_source(install_folder, profile_name, settings, options, env) + + profile = self._get_profile(profile_name, settings, options, env, cwd, install_folder) + conanfile_abs_path = self._get_conanfile_path(path, "conanfile.py") + conanfile = load_conanfile_class(conanfile_abs_path) + if (name and conanfile.name and conanfile.name != name) or \ + (version and conanfile.version and conanfile.version != version): + raise ConanException("Specified name/version doesn't match with the " + "name/version in the conanfile") + self._manager.export(user, channel, path, name=name, version=version) + + if not (name and version): + name = conanfile.name + version = conanfile.version + + reference = ConanFileReference(name, version, user, channel) + self._manager.export_pkg(reference, source_folder=source_folder, build_folder=build_folder, + install_folder=install_folder, profile=profile, force=force) + + @api_method + def download(self, reference, remote=None, package=None): + # Install packages without settings (fixed ids or all) + if not reference or not isinstance(reference, ConanFileReference): + raise ConanException("Invalid package recipe reference. " + "e.g., MyPackage/1.2@user/channel") + self._manager.download(reference, package, remote=remote) + + @api_method + def install_reference(self, reference, settings=None, options=None, env=None, scope=None, + remote=None, werror=False, verify=None, manifests=None, + manifests_interactive=None, build=None, profile_name=None, + update=False, generators=None, install_folder=None): + + self._user_io.out.werror_active = werror + cwd = os.getcwd() + install_folder = self._abs_relative_to(install_folder, cwd, default=cwd) + + manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd) + manifest_folder, manifest_interactive, manifest_verify = manifests + + profile = profile_from_args(profile_name, settings, options, env, scope, cwd, + self._client_cache) + + if not generators: # We don't want the default txt + generators = False + self._manager.install(reference=reference, install_folder=install_folder, remote=remote, + profile=profile, build_modes=build, update=update, manifest_folder=manifest_folder, manifest_verify=manifest_verify, manifest_interactive=manifest_interactive, - remote=remote, - profile=profile, - update=update, - generators=["txt"] - ) - self._manager.build(test_conanfile, test_folder, build_folder, package_folder=None, - test=str(reference)) + generators=generators, + cwd=cwd, deploy=True) @api_method - def package_files(self, reference, source_folder=None, build_folder=None, package_folder=None, - profile_name=None, force=False, settings=None, options=None, cwd=None): - - cwd = prepare_cwd(cwd) - - reference = ConanFileReference.loads(reference) - profile = profile_from_args(profile_name, settings, options, env=None, scope=None, cwd=cwd, - default_folder=self._client_cache.profiles_path) - package_folder = package_folder or cwd - if not source_folder and build_folder: - source_folder = build_folder - if not os.path.isabs(package_folder): - package_folder = os.path.join(cwd, package_folder) - if source_folder and not os.path.isabs(source_folder): - source_folder = os.path.normpath(os.path.join(cwd, source_folder)) - if build_folder and not os.path.isabs(build_folder): - build_folder = os.path.normpath(os.path.join(cwd, build_folder)) - self._manager.package_files(reference=reference, source_folder=source_folder, - build_folder=build_folder, package_folder=package_folder, - profile=profile, force=force) - - @api_method - def install(self, reference="", package=None, settings=None, options=None, env=None, scope=None, all=False, + def install(self, path="", settings=None, options=None, env=None, scope=None, remote=None, werror=False, verify=None, manifests=None, manifests_interactive=None, build=None, profile_name=None, - update=False, generator=None, no_imports=False, filename=None, cwd=None): + update=False, generators=None, no_imports=False, filename=None, + install_folder=None): self._user_io.out.werror_active = werror - cwd = prepare_cwd(cwd) - try: - ref = ConanFileReference.loads(reference) - except: - ref = os.path.normpath(os.path.join(cwd, reference)) - - if all or package: # Install packages without settings (fixed ids or all) - if all: - package = [] - if not reference or not isinstance(ref, ConanFileReference): - raise ConanException("Invalid package recipe reference. " - "e.g., MyPackage/1.2@user/channel") - self._manager.download(ref, package, remote=remote) - else: # Classic install, package chosen with settings and options - manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd) - manifest_folder, manifest_interactive, manifest_verify = manifests - - profile = profile_from_args(profile_name, settings, options, env, scope, cwd, - self._client_cache.profiles_path) - - self._manager.install(reference=ref, - current_path=cwd, - remote=remote, - profile=profile, - build_modes=build, - filename=filename, - update=update, - manifest_folder=manifest_folder, - manifest_verify=manifest_verify, - manifest_interactive=manifest_interactive, - generators=generator, - no_imports=no_imports) + cwd = os.getcwd() + install_folder = self._abs_relative_to(install_folder, cwd, default=cwd) + conanfile_folder = self._abs_relative_to(path, cwd, default=cwd) + + manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd) + manifest_folder, manifest_interactive, manifest_verify = manifests + + profile = profile_from_args(profile_name, settings, options, env, scope, cwd, + self._client_cache) + + self._manager.install(reference=conanfile_folder, + install_folder=install_folder, + remote=remote, + profile=profile, + build_modes=build, + filename=filename, + update=update, + manifest_folder=manifest_folder, + manifest_verify=manifest_verify, + manifest_interactive=manifest_interactive, + generators=generators, + no_imports=no_imports) @api_method def config_get(self, item): @@ -426,119 +517,155 @@ def config_install(self, item): return configuration_install(item, self._client_cache, self._user_io.out, self._runner) @api_method - def info_build_order(self, reference, settings=None, options=None, env=None, scope=None, profile_name=None, - filename=None, remote=None, build_order=None, check_updates=None, cwd=None): + def info_build_order(self, reference, settings=None, options=None, env=None, scope=None, + profile_name=None, filename=None, remote=None, build_order=None, + check_updates=None, build_folder=None): - current_path = prepare_cwd(cwd) + current_path = os.getcwd() try: reference = ConanFileReference.loads(reference) except: reference = os.path.normpath(os.path.join(current_path, reference)) - profile = profile_from_args(profile_name, settings, options, env, scope, cwd, self._client_cache.profiles_path) - graph = self._manager.info_build_order(reference, profile, filename, build_order, remote, check_updates, cwd=cwd) + profile = profile_from_args(profile_name, settings, options, env, scope, build_folder, + self._client_cache) + graph = self._manager.info_build_order(reference, profile, filename, build_order, + remote, check_updates) return graph @api_method - def info_nodes_to_build(self, reference, build_modes, settings=None, options=None, env=None, scope=None, - profile_name=None, filename=None, remote=None, check_updates=None, cwd=None): + def info_nodes_to_build(self, reference, build_modes, settings=None, options=None, env=None, + scope=None, profile_name=None, filename=None, remote=None, + check_updates=None, build_folder=None): - current_path = prepare_cwd(cwd) + current_path = os.getcwd() try: reference = ConanFileReference.loads(reference) except: reference = os.path.normpath(os.path.join(current_path, reference)) - profile = profile_from_args(profile_name, settings, options, env, scope, cwd, self._client_cache.profiles_path) - ret = self._manager.info_nodes_to_build(reference, profile, filename, build_modes, remote, check_updates, cwd) + profile = profile_from_args(profile_name, settings, options, env, scope, build_folder, + self._client_cache) + ret = self._manager.info_nodes_to_build(reference, profile, filename, build_modes, remote, + check_updates) ref_list, project_reference = ret return ref_list, project_reference @api_method - def info_get_graph(self, reference, remote=None, settings=None, options=None, env=None, scope=None, - profile_name=None, update=False, filename=None, cwd=None): + def info_get_graph(self, reference, remote=None, settings=None, options=None, env=None, + scope=None, profile_name=None, update=False, filename=None, + build_folder=None): - current_path = prepare_cwd(cwd) + current_path = os.getcwd() try: reference = ConanFileReference.loads(reference) except: reference = os.path.normpath(os.path.join(current_path, reference)) - profile = profile_from_args(profile_name, settings, options, env, scope, current_path, - self._client_cache.profiles_path) - ret = self._manager.info_get_graph(reference=reference, current_path=current_path, remote=remote, - profile=profile, check_updates=update, filename=filename) + profile = profile_from_args(profile_name, settings, options, env, scope, build_folder, + self._client_cache) + ret = self._manager.info_get_graph(reference=reference, + remote=remote, profile=profile, check_updates=update, + filename=filename) deps_graph, graph_updates_info, project_reference = ret return deps_graph, graph_updates_info, project_reference @api_method - def build(self, path="", source_folder=None, package_folder=None, filename=None, cwd=None): + def build(self, path, source_folder=None, package_folder=None, filename=None, + build_folder=None, install_folder=None): - current_path = prepare_cwd(cwd) - if path: - root_path = os.path.abspath(path) - else: - root_path = current_path + cwd = os.getcwd() + conanfile_folder = self._abs_relative_to(path, cwd) + build_folder = self._abs_relative_to(build_folder, cwd, default=cwd) + install_folder = self._abs_relative_to(install_folder, cwd, default=build_folder) + source_folder = self._abs_relative_to(source_folder, cwd, default=conanfile_folder) + default_pkg_folder = os.path.join(build_folder, "package") + package_folder = self._abs_relative_to(package_folder, cwd, default=default_pkg_folder) - build_folder = current_path - source_folder = source_folder or root_path - if not os.path.isabs(source_folder): - source_folder = os.path.normpath(os.path.join(current_path, source_folder)) + conanfile_abs_path = self._get_conanfile_path(conanfile_folder, filename) + if conanfile_abs_path.endswith(".txt"): + raise ConanException("A conanfile.py is needed to call 'conan build' " + "(not valid conanfile.txt)") - if package_folder and not os.path.isabs(package_folder): - package_folder = os.path.normpath(os.path.join(current_path, package_folder)) - - if filename and filename.endswith(".txt"): - raise ConanException("A conanfile.py is needed to call 'conan build'") - conanfile_path = os.path.join(root_path, filename or CONANFILE) - self._manager.build(conanfile_path, source_folder, build_folder, package_folder) + self._manager.build(conanfile_abs_path, source_folder, build_folder, package_folder, + install_folder) @api_method - def package(self, reference="", package_id=None, build_folder=None, source_folder=None, - cwd=None): - try: - ref = ConanFileReference.loads(reference) - except: - if "@" in reference: - raise - ref = None - - if ref: # cache packaging - # TODO: other args are unused. Either raise, or split API in two methods - self._manager.package(ref, package_id) - else: # local packaging - current_path = prepare_cwd(cwd) - recipe_folder = reference - if not os.path.isabs(recipe_folder): - recipe_folder = os.path.join(current_path, recipe_folder) - recipe_folder = os.path.normpath(recipe_folder) - build_folder = build_folder or recipe_folder - if not os.path.isabs(build_folder): - build_folder = os.path.join(current_path, build_folder) - build_folder = os.path.normpath(build_folder) - package_folder = current_path - source_folder = source_folder or recipe_folder - self._manager.local_package(package_folder, recipe_folder, build_folder, source_folder) - - @api_method - def source(self, reference, force=False, cwd=None): - cwd = prepare_cwd(cwd) - current_path, reference = _get_reference(reference, cwd) - self._manager.source(current_path, reference, force) + def package(self, path, build_folder, package_folder, source_folder=None, install_folder=None): + cwd = os.getcwd() + conanfile_folder = self._abs_relative_to(path, cwd) + build_folder = self._abs_relative_to(build_folder, cwd, default=cwd) + install_folder = self._abs_relative_to(install_folder, cwd, default=build_folder) + source_folder = self._abs_relative_to(source_folder, cwd, default=conanfile_folder) + default_pkg = os.path.join(build_folder, "package") + package_folder = self._abs_relative_to(package_folder, cwd, default=default_pkg) + self._manager.local_package(package_folder, conanfile_folder, build_folder, source_folder, + install_folder) @api_method - def imports(self, reference, undo=False, dest=None, filename=None, cwd=None): - cwd = prepare_cwd(cwd) + def source(self, path, source_folder=None, info_folder=None): + cwd = os.getcwd() + path = self._abs_relative_to(path, cwd) + source_folder = self._abs_relative_to(source_folder, cwd, default=cwd) + info_folder = self._abs_relative_to(info_folder, cwd, default=cwd) - if undo: - if not os.path.isabs(reference): - current_path = os.path.normpath(os.path.join(cwd, reference)) - else: - current_path = reference - self._manager.imports_undo(current_path) + mkdir(source_folder) + if not os.path.exists(info_folder): + raise ConanException("Specified info-folder doesn't exist") + + conanfile_abs_path = self._get_conanfile_path(path, CONANFILE) + self._manager.source(conanfile_abs_path, source_folder, info_folder) + + @staticmethod + def _abs_relative_to(path, base_relative, default=None): + """Gets an absolute path from "path" parameter, prepending base_relative if not abs yet. + If path is none, will return the 'default'""" + if not path: + return default + if not os.path.isabs(path): + return os.path.normpath(os.path.join(base_relative, path)) + else: + return path + + @staticmethod + def _get_conanfile_path(conanfile_folder, the_filename=None): + def raise_if_not_exists(some_path): + if not os.path.exists(some_path): + raise ConanException("Conanfile not found: %s" % some_path) + + if the_filename: + conanfile_path = os.path.join(conanfile_folder, the_filename) + raise_if_not_exists(conanfile_path) else: - current_path, reference = _get_reference(reference, cwd) - self._manager.imports(current_path, reference, filename, dest) + conanfile_path = os.path.join(conanfile_folder, CONANFILE) + if not os.path.exists(conanfile_path): + conanfile_path = os.path.join(conanfile_folder, CONANFILE_TXT) + raise_if_not_exists(conanfile_path) + return conanfile_path + + + @api_method + def imports(self, path, dest=None, filename=None, info_folder=None): + """ + :param path: Path to the conanfile + :param dest: Dir to put the imported files. (Abs path or relative to cwd) + :param filename: Alternative name of the conanfile. Default: conanfile.py or conanfile.txt + :param build_folder: Dir where the conaninfo.txt and conanbuildinfo.txt files are + :return: None + """ + cwd = os.getcwd() + conanfile_folder = self._abs_relative_to(path, cwd) + info_folder = self._abs_relative_to(info_folder, cwd, default=cwd) + dest = self._abs_relative_to(dest, cwd, default=cwd) + + mkdir(dest) + conanfile_abs_path = self._get_conanfile_path(conanfile_folder, filename) + self._manager.imports(conanfile_abs_path, dest, info_folder) + + @api_method + def imports_undo(self, manifest_path): + manifest_path = self._abs_relative_to(manifest_path, os.getcwd()) + self._manager.imports_undo(manifest_path) @api_method def export(self, user, channel, path=None, keep_source=False, filename=None, cwd=None, @@ -642,7 +769,8 @@ def remote_update_ref(self, reference, remote): def profile_list(self): folder = self._client_cache.profiles_path if os.path.exists(folder): - return [name for name in os.listdir(folder) if not os.path.isdir(name)] + return [name for name in os.listdir(folder) + if not os.path.isdir(os.path.join(folder, name))] else: self._user_io.out.info("No profiles defined") return [] @@ -826,78 +954,8 @@ def migrate_and_get_client_cache(base_folder, out, storage_folder=None): client_cache = ClientCache(base_folder, storage_folder, out) # Migration system - migrator = ClientMigrator(client_cache, Version(CLIENT_VERSION), out) + migrator = ClientMigrator(client_cache, Version(client_version), out) migrator.migrate() return client_cache - -# Profile helpers - - -def profile_from_args(profile, settings, options, env, scope, cwd, default_folder): - """ Return a Profile object, as the result of merging a potentially existing Profile - file and the args command-line arguments - """ - file_profile, _ = read_profile(profile, cwd, default_folder) - args_profile = _profile_parse_args(settings, options, env, scope) - - if file_profile: - file_profile.update(args_profile) - return file_profile - else: - return args_profile - - -def _profile_parse_args(settings, options, envs, scopes): - """ return a Profile object result of parsing raw data - """ - def _get_tuples_list_from_extender_arg(items): - if not items: - return [] - # Validate the pairs - for item in items: - chunks = item.split("=", 1) - if len(chunks) != 2: - raise ConanException("Invalid input '%s', use 'name=value'" % item) - return [(item[0], item[1]) for item in [item.split("=", 1) for item in items]] - - def _get_simple_and_package_tuples(items): - """Parse items like "thing:item=value or item2=value2 and returns a tuple list for - the simple items (name, value) and a dict for the package items - {package: [(item, value)...)], ...} - """ - simple_items = [] - package_items = defaultdict(list) - tuples = _get_tuples_list_from_extender_arg(items) - for name, value in tuples: - if ":" in name: # Scoped items - tmp = name.split(":", 1) - ref_name = tmp[0] - name = tmp[1] - package_items[ref_name].append((name, value)) - else: - simple_items.append((name, value)) - return simple_items, package_items - - def _get_env_values(env, package_env): - env_values = EnvValues() - for name, value in env: - env_values.add(name, EnvValues.load_value(value)) - for package, data in package_env.items(): - for name, value in data: - env_values.add(name, EnvValues.load_value(value), package) - return env_values - - result = Profile() - options = _get_tuples_list_from_extender_arg(options) - result.options = OptionsValues(options) - env, package_env = _get_simple_and_package_tuples(envs) - env_values = _get_env_values(env, package_env) - result.env_values = env_values - settings, package_settings = _get_simple_and_package_tuples(settings) - result.settings = OrderedDict(settings) - for pkg, values in package_settings.items(): - result.package_settings[pkg] = OrderedDict(values) - result.scopes = Scopes.from_list(scopes) if scopes else Scopes() - return result diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py index c52a9ed0062..fb42dc78f6d 100644 --- a/conans/client/conf/__init__.py +++ b/conans/client/conf/__init__.py @@ -19,12 +19,16 @@ Android: api_level: ANY iOS: - version: ["7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3"] + version: ["7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3", "11.0"] + watchOS: + version: ["4.0"] + tvOS: + version: ["11.0"] FreeBSD: SunOS: Arduino: board: ANY -arch: [x86, x86_64, ppc64le, ppc64, armv6, armv7, armv7hf, armv8, sparc, sparcv9, mips, mips64, avr] +arch: [x86, x86_64, ppc64le, ppc64, armv6, armv7, armv7hf, armv8, sparc, sparcv9, mips, mips64, avr, armv7s, armv7k] compiler: sun-cc: version: ["5.10", "5.11", "5.12", "5.13", "5.14"] @@ -39,7 +43,7 @@ runtime: [MD, MT, MTd, MDd] version: ["8", "9", "10", "11", "12", "14", "15"] clang: - version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0"] + version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "5.0"] libcxx: [libstdc++, libstdc++11, libc++] apple-clang: version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0"] @@ -63,8 +67,12 @@ # verbose_traceback = False # environment CONAN_VERBOSE_TRACEBACK # bash_path = "" # environment CONAN_BASH_PATH (only windows) # 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 +# user_home_short = your_path # environment CONAN_USER_HOME_SHORT +# conan_make_program = make # environment CONAN_MAKE_PROGRAM (overrides the make program used in AutoToolsBuildEnvironment.make) # cmake_generator # environment CONAN_CMAKE_GENERATOR # http://www.vtk.org/Wiki/CMake_Cross_Compiling @@ -124,6 +132,7 @@ def env_vars(self): "CONAN_SYSREQUIRES_SUDO": self._env_c("general.sysrequires_sudo", "CONAN_SYSREQUIRES_SUDO", "False"), "CONAN_RECIPE_LINTER": self._env_c("general.recipe_linter", "CONAN_RECIPE_LINTER", "True"), "CONAN_CPU_COUNT": self._env_c("general.cpu_count", "CONAN_CPU_COUNT", None), + "CONAN_READ_ONLY_CACHE": self._env_c("general.read_only_cache", "CONAN_READ_ONLY_CACHE", None), "CONAN_USER_HOME_SHORT": self._env_c("general.user_home_short", "CONAN_USER_HOME_SHORT", None), "CONAN_VERBOSE_TRACEBACK": self._env_c("general.verbose_traceback", "CONAN_VERBOSE_TRACEBACK", None), # http://www.vtk.org/Wiki/CMake_Cross_Compiling @@ -148,7 +157,7 @@ def env_vars(self): None), "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), } # Filter None values @@ -234,6 +243,13 @@ def default_profile(self): except ConanException: return DEFAULT_PROFILE_NAME + @property + def cache_no_locks(self): + try: + return self.get_item("general.cache_no_locks") + except ConanException: + return False + @property def storage(self): return dict(self.get_conf("storage")) @@ -274,6 +290,17 @@ def proxies(self): proxies = self.get_conf("proxies") # If there is proxies section, but empty, it will try to use system proxy if not proxies: + # We don't have evidences that this following line is necessary. + # If the proxies has been + # configured at system level, conan will use it, and shouldn't be necessary + # to return here the proxies read from the system. + # Furthermore, the urls excluded for use proxies at system level do not work in + # this case, then the only way is to remove the [proxies] section with + # conan config remote proxies, then this method will return None and the proxies + # dict passed to requests will be empty. + # We don't remove this line because we are afraid to break something, but maybe + # until now is working because no one is using system-wide proxies or those proxies + # rules don't contain excluded urls.c #1777 return urllib.request.getproxies() result = {k: (None if v == "None" else v) for k, v in proxies} return result diff --git a/conans/client/configure_build_environment.py b/conans/client/configure_build_environment.py index 43fb2f10529..9dd3eff7417 100644 --- a/conans/client/configure_build_environment.py +++ b/conans/client/configure_build_environment.py @@ -2,6 +2,7 @@ import platform import os +from conans.client import join_arguments from conans.tools import environment_append, args_to_string, cpu_count, cross_building, detected_architecture sun_cc_libcxx_flags_dict = {"libCstd": "-library=Cstd", @@ -118,21 +119,25 @@ def _get_host_build_target_flags(self, arch_detected, os_detected): os_setting = self._conanfile.settings.get_safe("os") if os_detected == "Windows" and os_setting != "Windows": - return None, None, None # Don't know what to do with these, even exists? its only for configure + # Don't know what to do with these, even exists? its only for configure + return None, None, None # Building FOR windows if os_setting == "Windows": build = "i686-w64-mingw32" if arch_detected == "x86" else "x86_64-w64-mingw32" host = "i686-w64-mingw32" if arch_setting == "x86" else "x86_64-w64-mingw32" else: # Building for Linux or Android - build = "%s-%s" % (arch_detected, {"Linux": "linux-gnu", "Darwin": "apple-macos"}.get(os_detected, - os_detected.lower())) - if arch_setting == "armv8": + build = "%s-%s" % (arch_detected, {"Linux": "linux-gnu", + "Darwin": "apple-macos"}.get(os_detected, + os_detected.lower())) + if arch_setting == "x86": + host_arch = "i686" + elif arch_setting == "armv8": host_arch = "aarch64" else: host_arch = "arm" if "arm" in arch_setting else arch_setting - host = "%s%s" % (host_arch, {"Linux": "-linux-gnueabi", + host = "%s%s" % (host_arch, { "Linux": "-linux-gnueabi", "Android": "-linux-android"}.get(os_setting, "")) if arch_setting == "armv7hf" and os_setting == "Linux": host += "hf" @@ -182,11 +187,12 @@ def configure(self, configure_dir=None, args=None, build=None, host=None, target self._conanfile.run("%s/configure %s %s" % (configure_dir, args_to_string(args), " ".join(triplet_args))) - def make(self, args=""): + def make(self, args="", make_program=None): + make_program = os.getenv("CONAN_MAKE_PROGRAM") or make_program or "make" with environment_append(self.vars): str_args = args_to_string(args) - cpu_count_option = ("-j%s" % cpu_count()) if "-j" not in str_args else "" - self._conanfile.run("make %s %s" % (str_args, cpu_count_option)) + cpu_count_option = ("-j%s" % cpu_count()) if "-j" not in str_args else None + self._conanfile.run("%s" % join_arguments([make_program, str_args, cpu_count_option])) @property def _sysroot_flag(self): @@ -207,7 +213,7 @@ def _configure_flags(self): if self._build_type == "Debug": ret.append("-g") # default debug information elif self._build_type == "Release" and self._compiler == "gcc": - ret.append("-s") # Remove all symbol table and relocation information from the executable. + ret.append("-s") # Remove all symbol table and relocation information from the executable. if self._sysroot_flag: ret.append(self._sysroot_flag) return ret @@ -244,8 +250,8 @@ def append(*args): ret.append(arg) return ret - lib_paths = ['-L%s' % x for x in self.library_paths] - include_paths = ['-I%s' % x for x in self.include_paths] + lib_paths = ['-L%s' % x.replace("\\", "/") for x in self.library_paths] + include_paths = ['-I%s' % x.replace("\\", "/") for x in self.include_paths] ld_flags = append(self.link_flags, lib_paths) cpp_flags = append(include_paths, ["-D%s" % x for x in self.defines]) diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index 15f4404b407..5ba5a5e0c7c 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -1,5 +1,6 @@ from os.path import join +from conans.client.generators.pkg_config import PkgConfigGenerator from conans.errors import ConanException from conans.util.files import save, normalize @@ -11,11 +12,11 @@ from .qbs import QbsGenerator from .scons import SConsGenerator from .visualstudio import VisualStudioGenerator +from .visualstudio_multi import VisualStudioMultiGenerator from .visualstudiolegacy import VisualStudioLegacyGenerator from .xcode import XCodeGenerator from .ycm import YouCompleteMeGenerator from .virtualenv import VirtualEnvGenerator -from .env import ConanEnvGenerator from .cmake_multi import CMakeMultiGenerator from .virtualbuildenv import VirtualBuildEnvGenerator @@ -41,7 +42,6 @@ def __getitem__(self, key): registered_generators = _GeneratorManager() - registered_generators.add("txt", TXTGenerator) registered_generators.add("gcc", GCCGenerator) registered_generators.add("cmake", CMakeGenerator) @@ -50,13 +50,14 @@ def __getitem__(self, key): registered_generators.add("qbs", QbsGenerator) registered_generators.add("scons", SConsGenerator) registered_generators.add("visual_studio", VisualStudioGenerator) +registered_generators.add("visual_studio_multi", VisualStudioMultiGenerator) registered_generators.add("visual_studio_legacy", VisualStudioLegacyGenerator) registered_generators.add("xcode", XCodeGenerator) registered_generators.add("ycm", YouCompleteMeGenerator) registered_generators.add("virtualenv", VirtualEnvGenerator) -registered_generators.add("env", ConanEnvGenerator) registered_generators.add("virtualbuildenv", VirtualBuildEnvGenerator) registered_generators.add("virtualrunenv", VirtualRunEnvGenerator) +registered_generators.add("pkg_config", PkgConfigGenerator) def write_generators(conanfile, path, output): @@ -76,6 +77,7 @@ def write_generators(conanfile, path, output): generator = generator_class(conanfile.deps_cpp_info, conanfile.cpp_info) try: + generator.output_path = path content = generator.content if isinstance(content, dict): if generator.filename: diff --git a/conans/client/generators/cmake.py b/conans/client/generators/cmake.py index 07e007aa302..21007a03ed3 100644 --- a/conans/client/generators/cmake.py +++ b/conans/client/generators/cmake.py @@ -1,5 +1,4 @@ from conans.model import Generator -from conans.model.build_info import CppInfo from conans.paths import BUILD_INFO_CMAKE from conans.client.generators.cmake_common import cmake_dependency_vars,\ cmake_macros, generate_targets_section, cmake_dependencies, cmake_package_info,\ diff --git a/conans/client/generators/cmake_common.py b/conans/client/generators/cmake_common.py index 1b419390e14..4e045a2e9d2 100644 --- a/conans/client/generators/cmake_common.py +++ b/conans/client/generators/cmake_common.py @@ -458,18 +458,18 @@ def generate_targets_section(dependencies): macro(conan_set_vs_runtime) if(CONAN_LINK_RUNTIME) - if(DEFINED CMAKE_CXX_FLAGS_RELEASE) - string(REPLACE "/MD" ${CONAN_LINK_RUNTIME} CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - endif() - if(DEFINED CMAKE_CXX_FLAGS_DEBUG) - string(REPLACE "/MDd" ${CONAN_LINK_RUNTIME} CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - endif() - if(DEFINED CMAKE_C_FLAGS_RELEASE) - string(REPLACE "/MD" ${CONAN_LINK_RUNTIME} CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) - endif() - if(DEFINED CMAKE_C_FLAGS_DEBUG) - string(REPLACE "/MDd" ${CONAN_LINK_RUNTIME} CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) - endif() + foreach(flag CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL) + if(DEFINED ${flag}) + string(REPLACE "/MD" ${CONAN_LINK_RUNTIME} ${flag} "${${flag}}") + endif() + endforeach() + foreach(flag CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG) + if(DEFINED ${flag}) + string(REPLACE "/MDd" ${CONAN_LINK_RUNTIME} ${flag} "${${flag}}") + endif() + endforeach() endif() endmacro() @@ -524,18 +524,18 @@ def generate_targets_section(dependencies): # debug, forcing MXd for debug builds. It will generate MSVCRT warnings if the dependencies # are installed with "conan install" and the wrong build time. if(CONAN_LINK_RUNTIME MATCHES "MT") - if(DEFINED CMAKE_CXX_FLAGS_RELEASE) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - endif() - if(DEFINED CMAKE_CXX_FLAGS_DEBUG) - string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - endif() - if(DEFINED CMAKE_C_FLAGS_RELEASE) - string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) - endif() - if(DEFINED CMAKE_C_FLAGS_DEBUG) - string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) - endif() + foreach(flag CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL) + if(DEFINED ${flag}) + string(REPLACE "/MD" "/MT" ${flag} "${${flag}}") + endif() + endforeach() + foreach(flag CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG) + if(DEFINED ${flag}) + string(REPLACE "/MDd" "/MTd" ${flag} "${${flag}}") + endif() + endforeach() endif() endmacro() diff --git a/conans/client/generators/env.py b/conans/client/generators/env.py deleted file mode 100644 index e954bf474df..00000000000 --- a/conans/client/generators/env.py +++ /dev/null @@ -1,13 +0,0 @@ -from conans.model import Generator -from conans.paths import CONANENV - - -class ConanEnvGenerator(Generator): - - @property - def filename(self): - return CONANENV - - @property - def content(self): - return self.deps_env_info.dumps() diff --git a/conans/client/generators/pkg_config.py b/conans/client/generators/pkg_config.py new file mode 100644 index 00000000000..77ff14868e9 --- /dev/null +++ b/conans/client/generators/pkg_config.py @@ -0,0 +1,71 @@ +from conans.model import Generator + +""" +PC FILE EXAMPLE: + +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: my-project +Description: Some brief but informative description +Version: 1.2.3 +Libs: -L${libdir} -lmy-project-1 -linkerflag +Cflags: -I${includedir}/my-project-1 +Requires: glib-2.0 >= 2.40 gio-2.0 >= 2.42 nice >= 0.1.6 +Requires.private: gthread-2.0 >= 2.40 +""" + + +def concat_if_not_empty(groups): + return " ".join([param for group in groups for param in group if param and param.strip()]) + + +def single_pc_file_contents(name, cpp_info): + lines = ['prefix=%s' % cpp_info.rootpath.replace("\\", "/")] + libdir_vars = [] + for i, libdir in enumerate(cpp_info.libdirs): + varname = "libdir" if i == 0 else "libdir%d" % (i + 2) + lines.append("%s=${prefix}/%s" % (varname, libdir)) + libdir_vars.append(varname) + include_dir_vars = [] + for i, includedir in enumerate(cpp_info.includedirs): + varname = "includedir" if i == 0 else "includedir%d" % (i + 2) + lines.append("%s=${prefix}/%s" % (varname, includedir)) + include_dir_vars.append(varname) + lines.append("") + lines.append("Name: %s" % name) + description = cpp_info.description or "Conan package: %s" % name + lines.append("Description: %s" % description) + lines.append("Version: %s" % cpp_info.version) + libdirs_flags = ["-L${%s}" % name for name in libdir_vars] + libnames_flags = ["-l%s " % name for name in cpp_info.libs] + shared_flags = cpp_info.sharedlinkflags + cpp_info.exelinkflags + lines.append("Libs: %s" % concat_if_not_empty([libdirs_flags, libnames_flags, shared_flags])) + include_dirs_flags = ["-I${%s}" % name for name in include_dir_vars] + + lines.append("Cflags: %s" % concat_if_not_empty( + [include_dirs_flags, + cpp_info.cppflags, + cpp_info.cflags, + ["-D%s" % d for d in cpp_info.defines]])) + + if cpp_info.public_deps: + public_deps = " ".join(cpp_info.public_deps) + lines.append("Requires: %s" % public_deps) + return "\n".join(lines) + "\n" + + +class PkgConfigGenerator(Generator): + + @property + def filename(self): + pass + + @property + def content(self): + ret = {} + for depname, cpp_info in self.deps_build_info.dependencies: + ret["%s.pc" % depname] = single_pc_file_contents(depname, cpp_info) + return ret diff --git a/conans/client/generators/text.py b/conans/client/generators/text.py index 3e549b9d0dc..82d85246f0f 100644 --- a/conans/client/generators/text.py +++ b/conans/client/generators/text.py @@ -4,7 +4,8 @@ from conans.errors import ConanException from conans.model import Generator from conans.model.build_info import DepsCppInfo, CppInfo -from conans.model.user_info import UserDepsInfo +from conans.model.env_info import DepsEnvInfo +from conans.model.user_info import DepsUserInfo from conans.paths import BUILD_INFO from conans.util.log import logger @@ -38,22 +39,37 @@ def filename(self): @staticmethod def loads(text): user_defines_index = text.find("[USER_") + env_defines_index = text.find("[ENV_") if user_defines_index != -1: deps_cpp_info_txt = text[:user_defines_index] - user_info_txt = text[user_defines_index:] + if env_defines_index != -1: + user_info_txt = text[user_defines_index:env_defines_index] + deps_env_info_txt = text[env_defines_index:] + else: + user_info_txt = text[user_defines_index:] + deps_env_info_txt = "" else: - deps_cpp_info_txt = text + if env_defines_index != -1: + deps_cpp_info_txt = text[:env_defines_index] + deps_env_info_txt = text[env_defines_index:] + else: + deps_cpp_info_txt = text + deps_env_info_txt = "" + user_info_txt = "" deps_cpp_info = TXTGenerator._loads_cpp_info(deps_cpp_info_txt) - user_info = TXTGenerator._loads_deps_user_info(user_info_txt) - return deps_cpp_info, user_info + deps_user_info = TXTGenerator._loads_deps_user_info(user_info_txt) + deps_env_info = DepsEnvInfo.loads(deps_env_info_txt) + return deps_cpp_info, deps_user_info, deps_env_info @staticmethod def _loads_deps_user_info(text): - ret = UserDepsInfo() + ret = DepsUserInfo() lib_name = None for line in text.splitlines(): + if not line: + continue if not lib_name and not line.startswith("[USER_"): raise ConanException("Error, invalid file format reading user info variables") elif line.startswith("[USER_"): @@ -142,10 +158,13 @@ def content(self): all_flags = template.format(dep=dep, deps=deps, config=":" + config) sections.append(all_flags) - # Generate the user info variables as [LIB_A_USER_VAR]\n + # Generate the user info variables as [USER_{DEP_NAME}] and then the values with key=value for dep, the_vars in self._deps_user_info.items(): sections.append("[USER_%s]" % dep) for name, value in sorted(the_vars.vars.items()): sections.append("%s=%s" % (name, value)) + # Generate the env info variables as [ENV_{DEP_NAME}] and then the values with key=value + sections.append(self._deps_env_info.dumps()) + return "\n".join(sections) diff --git a/conans/client/generators/visualstudio_multi.py b/conans/client/generators/visualstudio_multi.py new file mode 100644 index 00000000000..8b5c5e2f8a6 --- /dev/null +++ b/conans/client/generators/visualstudio_multi.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +from conans.model import Generator +from conans.client.generators import VisualStudioGenerator +from xml.dom import minidom +from conans.util.files import load + + +class VisualStudioMultiGenerator(Generator): + template = """ + + + + + + + + +""" + + @property + def filename(self): + pass + + @property + def content(self): + configuration = str(self.conanfile.settings.build_type) + platform = {'x86': 'Win32', 'x86_64': 'x64'}.get(str(self.conanfile.settings.arch)) + vsversion = str(self.settings.compiler.version) + + # there is also ClCompile.RuntimeLibrary, but it's handling is a bit complicated, so skipping for now + condition = " '$(Configuration)' == '%s' And '$(Platform)' == '%s' And '$(VisualStudioVersion)' == '%s' "\ + % (configuration, platform, vsversion + '.0') + + name_multi = 'conanbuildinfo_multi.props' + name_current = ('conanbuildinfo_%s_%s_%s.props' % (configuration, platform, vsversion)).lower() + + multi_path = os.path.join(self.output_path, name_multi) + if os.path.isfile(multi_path): + content_multi = load(multi_path) + else: + content_multi = self.template + + dom = minidom.parseString(content_multi) + import_node = dom.createElement('Import') + import_node.setAttribute('Condition', condition) + import_node.setAttribute('Project', name_current) + import_group = dom.getElementsByTagName('ImportGroup')[0] + children = import_group.getElementsByTagName("Import") + for node in children: + if name_current == node.getAttribute("Project") and condition == node.getAttribute("Condition"): + break + else: + import_group.appendChild(import_node) + content_multi = dom.toprettyxml() + content_multi = "\n".join(line for line in content_multi.splitlines() if line.strip()) + + vs_generator = VisualStudioGenerator(self.conanfile) + content_current = vs_generator.content + + return {name_multi: content_multi, name_current: content_current} diff --git a/conans/client/generators/xcode.py b/conans/client/generators/xcode.py index 8f9fa052bb8..2c1c6291ebb 100644 --- a/conans/client/generators/xcode.py +++ b/conans/client/generators/xcode.py @@ -9,9 +9,9 @@ class XCodeGenerator(Generator): LIBRARY_SEARCH_PATHS = $(inherited) {lib_dirs} OTHER_LDFLAGS = $(inherited) {linker_flags} {libs} -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) {compiler_flags} -OTHER_CFLAGS = $(inherited) -OTHER_CPLUSPLUSFLAGS = $(inherited) +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) {definitions} +OTHER_CFLAGS = $(inherited) {c_compiler_flags} +OTHER_CPLUSPLUSFLAGS = $(inherited) {cpp_compiler_flags} ''' def __init__(self, conanfile): @@ -23,7 +23,8 @@ def __init__(self, conanfile): for p in deps_cpp_info.lib_paths) self.libs = " ".join(['-l%s' % lib for lib in deps_cpp_info.libs]) self.definitions = " ".join('"%s"' % d for d in deps_cpp_info.defines) - self.compiler_flags = " ".join(deps_cpp_info.cppflags + deps_cpp_info.cflags) + self.c_compiler_flags = " ".join(deps_cpp_info.cflags) + self.cpp_compiler_flags = " ".join(deps_cpp_info.cppflags) self.linker_flags = " ".join(deps_cpp_info.sharedlinkflags) @property diff --git a/conans/client/generators/ycm.py b/conans/client/generators/ycm.py index f27c75c09dc..ef00968b65b 100644 --- a/conans/client/generators/ycm.py +++ b/conans/client/generators/ycm.py @@ -175,8 +175,10 @@ def prefixed(prefix, values): flags = ['-x', 'c++'] flags.extend(self.deps_build_info.cppflags) flags.extend(self.build_info.cppflags) + flags.extend(prefixed("-D", self.deps_build_info.defines)) flags.extend(prefixed("-D", self.build_info.defines)) + flags.extend(prefixed("-I", self.build_info.include_paths)) flags.extend(prefixed("-I", self.deps_build_info.include_paths)) @@ -187,4 +189,4 @@ def prefixed(prefix, values): pass return self.template.format(default_flags="'" + "', '".join(flags) + "'", - cxx_version=cxx_version) + cxx_version=cxx_version) diff --git a/conans/client/importer.py b/conans/client/importer.py index 78b39f3ff30..24bbf063a9b 100644 --- a/conans/client/importer.py +++ b/conans/client/importer.py @@ -3,6 +3,7 @@ import os import time +from conans import tools from conans.client.file_copier import FileCopier, report_copied_files from conans.client.output import ScopedOutput from conans.errors import ConanException @@ -48,15 +49,8 @@ def undo_imports(current_path, output): raise ConanException("Cannot remove manifest file (open or busy): %s" % manifest_path) -def run_imports(conanfile, dest_folder, output): - file_importer = _FileImporter(conanfile, dest_folder) - conanfile.copy = file_importer - conanfile.imports_folder = dest_folder - with environment_append(conanfile.env): - conanfile.imports() - copied_files = file_importer.copied_files - import_output = ScopedOutput("%s imports()" % output.scope, output) - report_copied_files(copied_files, import_output) +def _report_save_manifest(copied_files, output, dest_folder, manifest_name): + report_copied_files(copied_files, output) if copied_files: date = calendar.timegm(time.gmtime()) file_dict = {} @@ -64,10 +58,44 @@ def run_imports(conanfile, dest_folder, output): abs_path = os.path.join(dest_folder, f) file_dict[f] = md5sum(abs_path) manifest = FileTreeManifest(date, file_dict) - save(os.path.join(dest_folder, IMPORTS_MANIFESTS), str(manifest)) + save(os.path.join(dest_folder, manifest_name), str(manifest)) + + +def run_imports(conanfile, dest_folder, output): + file_importer = _FileImporter(conanfile, dest_folder) + conanfile.copy = file_importer + conanfile.imports_folder = dest_folder + with environment_append(conanfile.env): + with tools.chdir(dest_folder): + conanfile.imports() + copied_files = file_importer.copied_files + import_output = ScopedOutput("%s imports()" % output.scope, output) + _report_save_manifest(copied_files, import_output, dest_folder, IMPORTS_MANIFESTS) return copied_files +def run_deploy(conanfile, dest_folder, output): + deploy_output = ScopedOutput("%s deploy()" % output.scope, output) + file_importer = _FileImporter(conanfile, dest_folder) + package_copied = set() + + # This is necessary to capture FileCopier full destination paths + # Maybe could be improved in FileCopier + def file_copier(*args, **kwargs): + file_copy = FileCopier(conanfile.package_folder, dest_folder) + copied = file_copy(*args, **kwargs) + package_copied.update(copied) + + conanfile.copy_deps = file_importer + conanfile.copy = file_copier + with environment_append(conanfile.env): + conanfile.deploy() + + copied_files = file_importer.copied_files + copied_files.update(package_copied) + _report_save_manifest(copied_files, deploy_output, dest_folder, "deploy_manifest.txt") + + class _FileImporter(object): """ manages the copy of files, resources, libs from the local store to the user space. E.g.: shared libs, dlls, they will be in the package folder of your diff --git a/conans/client/installer.py b/conans/client/installer.py index 26acc3302d5..f7829c5021d 100644 --- a/conans/client/installer.py +++ b/conans/client/installer.py @@ -1,12 +1,12 @@ import os import time -import platform import shutil +from conans.client import tools 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 +from conans.paths import CONANINFO, BUILD_INFO, RUN_LOG_NAME, long_paths_support +from conans.util.files import save, rmdir, mkdir, make_read_only from conans.model.ref import PackageReference from conans.util.log import logger from conans.errors import (ConanException, conanfile_exception_formatter, @@ -18,9 +18,10 @@ from conans.client.source import config_source from conans.tools import environment_append from conans.util.tracer import log_package_built +from conans.util.env_reader import get_env -def _init_package_info(deps_graph, paths, current_path): +def _init_package_info(deps_graph, paths): for node in deps_graph.nodes: conan_ref, conan_file = node if conan_ref: @@ -30,7 +31,10 @@ def _init_package_info(deps_graph, paths, current_path): conan_file.package_folder = package_folder conan_file.cpp_info = CppInfo(package_folder) else: - conan_file.cpp_info = CppInfo(current_path) + conan_file.cpp_info = CppInfo("") + + conan_file.cpp_info.version = conan_file.version + conan_file.cpp_info.description = conan_file.description conan_file.env_info = EnvInfo() conan_file.user_info = UserInfo() @@ -58,16 +62,61 @@ def __init__(self, conan_file, package_reference, client_cache, output): self._out = output self._package_reference = package_reference self._conan_ref = self._package_reference.conan - self._build_folder = None + self._skip_build = False # If build_id() + + new_id = build_id(self._conan_file) + self.build_reference = PackageReference(self._conan_ref, new_id) if new_id else package_reference + self.build_folder = self._client_cache.build(self.build_reference, + self._conan_file.short_paths) + + def prepare_build(self): + if os.path.exists(self.build_folder) and hasattr(self._conan_file, "build_id"): + self._skip_build = True + return + + # build_id is not caching the build folder, so actually rebuild the package + _handle_system_requirements(self._conan_file, self._package_reference, + self._client_cache, self._out) + package_folder = self._client_cache.package(self._package_reference, + self._conan_file.short_paths) + src_folder = self._client_cache.source(self._conan_ref, self._conan_file.short_paths) + export_folder = self._client_cache.export(self._conan_ref) + export_source_folder = self._client_cache.export_sources(self._conan_ref, + self._conan_file.short_paths) + + try: + rmdir(self.build_folder) + rmdir(package_folder) + except OSError as e: + raise ConanException("%s\n\nCouldn't remove folder, might be busy or open\n" + "Close any app using it, and retry" % str(e)) + + self._out.info('Building your package in %s' % self.build_folder) + config_source(export_folder, export_source_folder, src_folder, + self._conan_file, self._out) + self._out.info('Copying sources to build folder') + + if getattr(self._conan_file, 'no_copy_source', False): + mkdir(self.build_folder) + self._conan_file.source_folder = src_folder + else: + if not long_paths_support: + from conans.util.windows import ignore_long_path_files + ignore = ignore_long_path_files(src_folder, self.build_folder, self._out) + else: + ignore = None + + shutil.copytree(src_folder, self.build_folder, symlinks=True, ignore=ignore) + logger.debug("Copied to %s", self.build_folder) + logger.debug("Files copied %s", os.listdir(self.build_folder)) + self._conan_file.source_folder = self.build_folder def build(self): """Calls the conanfile's build method""" - if not os.path.exists(self.build_folder) or not hasattr(self._conan_file, "build_id"): - # build_id is not caching the build folder, so actually rebuild the package - _handle_system_requirements(self._conan_file, self._package_reference, - self._client_cache, self._out) - with environment_append(self._conan_file.env): - self._build_package() + if self._skip_build: + return + with environment_append(self._conan_file.env): + self._build_package() def package(self): """Generate the info txt files and calls the conanfile package method. @@ -93,8 +142,12 @@ def package(self): with environment_append(self._conan_file.env): package_folder = self._client_cache.package(self._package_reference, self._conan_file.short_paths) + install_folder = self.build_folder # While installing, the infos goes to build folder create_package(self._conan_file, source_folder, self.build_folder, package_folder, - self._out) + install_folder, self._out) + + if get_env("CONAN_READ_ONLY_CACHE", False): + make_read_only(package_folder) def _build_package(self): """ builds the package, creating the corresponding build folder if necessary @@ -102,48 +155,20 @@ def _build_package(self): in every build, as some configure processes actually change the source code. Receives the build_folder because it can change if the method build_id() exists """ - package_folder = self._client_cache.package(self._package_reference, self._conan_file.short_paths) - src_folder = self._client_cache.source(self._conan_ref, self._conan_file.short_paths) - export_folder = self._client_cache.export(self._conan_ref) - export_source_folder = self._client_cache.export_sources(self._conan_ref, - self._conan_file.short_paths) - - try: - rmdir(self.build_folder) - rmdir(package_folder) - except Exception as e: - raise ConanException("%s\n\nCouldn't remove folder, might be busy or open\n" - "Close any app using it, and retry" % str(e)) - - self._out.info('Building your package in %s' % self.build_folder) - config_source(export_folder, export_source_folder, src_folder, - self._conan_file, self._out) - self._out.info('Copying sources to build folder') - - if getattr(self._conan_file, 'no_copy_source', False): - mkdir(self.build_folder) - self._conan_file.source_folder = src_folder - else: - if platform.system() == "Windows" and os.getenv("CONAN_USER_HOME_SHORT") != "None": - from conans.util.windows import ignore_long_path_files - ignore = ignore_long_path_files(src_folder, self.build_folder, self._out) - else: - ignore = None - - shutil.copytree(src_folder, self.build_folder, symlinks=True, ignore=ignore) - logger.debug("Copied to %s" % self.build_folder) - logger.debug("Files copied %s" % os.listdir(self.build_folder)) - self._conan_file.source_folder = self.build_folder os.chdir(self.build_folder) self._conan_file.build_folder = self.build_folder - self._conan_file._conanfile_directory = self.build_folder + self._conan_file.conanfile_directory = self.build_folder + self._conan_file.package_folder = package_folder + # In local cache, install folder always is build_folder + self._conan_file.install_folder = self.build_folder + # Read generators from conanfile and generate the needed files logger.debug("Writing generators") write_generators(self._conan_file, self.build_folder, self._out) - logger.debug("Files copied after generators %s" % os.listdir(self.build_folder)) + logger.debug("Files copied after generators %s", os.listdir(self.build_folder)) # Build step might need DLLs, binaries as protoc to generate source files # So execute imports() before build, storing the list of copied_files @@ -153,8 +178,8 @@ def _build_package(self): try: # This is necessary because it is different for user projects # than for packages - logger.debug("Call conanfile.build() with files in build folder: %s" - % os.listdir(self.build_folder)) + logger.debug("Call conanfile.build() with files in build folder: %s", + os.listdir(self.build_folder)) self._out.highlight("Calling build()") with conanfile_exception_formatter(str(self._conan_file), "build"): self._conan_file.build() @@ -162,32 +187,23 @@ def _build_package(self): self._out.success("Package '%s' built" % self._conan_file.info.package_id()) self._out.info("Build folder %s" % self.build_folder) except Exception as exc: - os.chdir(src_folder) self._out.writeln("") self._out.error("Package '%s' build failed" % self._conan_file.info.package_id()) self._out.warn("Build folder %s" % self.build_folder) if isinstance(exc, ConanExceptionInUserConanfileMethod): raise exc raise ConanException(exc) - finally: - self._conan_file._conanfile_directory = export_folder + export_folder = self._client_cache.export(self._conan_ref) + self._conan_file.conanfile_directory = export_folder # Now remove all files that were imported with imports() for f in copied_files: try: if f.startswith(self.build_folder): os.remove(f) - except Exception: + except OSError: self._out.warn("Unable to remove imported file from build: %s" % f) - @property - def build_folder(self): - if not self._build_folder: - new_id = build_id(self._conan_file) - new_ref = PackageReference(self._conan_ref, new_id) if new_id else self._package_reference - self._build_folder = self._client_cache.build(new_ref, self._conan_file.short_paths) - return self._build_folder - def _raise_package_not_found_error(conan_file, conan_ref, out): settings_text = ", ".join(conan_file.info.full_settings.dumps().splitlines()) @@ -239,11 +255,12 @@ def call_system_requirements(conanfile, output): raise ConanException("Error in system requirements") -def call_package_info(conanfile): +def call_package_info(conanfile, package_folder): # Once the node is build, execute package info, so it has access to the # package folder and artifacts - with conanfile_exception_formatter(str(conanfile), "package_info"): - conanfile.package_info() + with tools.chdir(package_folder): + with conanfile_exception_formatter(str(conanfile), "package_info"): + conanfile.package_info() class ConanInstaller(object): @@ -258,20 +275,20 @@ def __init__(self, client_cache, output, remote_proxy, build_mode, build_require self._build_mode = build_mode self._built_packages = set() # To avoid re-building twice the same package reference - def install(self, deps_graph, current_path): + def install(self, deps_graph): """ given a DepsGraph object, build necessary nodes or retrieve them """ t1 = time.time() - _init_package_info(deps_graph, self._client_cache, current_path) + _init_package_info(deps_graph, self._client_cache) # order by levels and propagate exports as download imports nodes_by_level = deps_graph.by_levels() - logger.debug("Install-Process buildinfo %s" % (time.time() - t1)) + logger.debug("Install-Process buildinfo %s", (time.time() - t1)) t1 = time.time() skip_private_nodes = self._compute_private_nodes(deps_graph) - logger.debug("Install-Process private %s" % (time.time() - t1)) + logger.debug("Install-Process private %s", (time.time() - t1)) t1 = time.time() self._build(nodes_by_level, skip_private_nodes, deps_graph) - logger.debug("Install-build %s" % (time.time() - t1)) + logger.debug("Install-build %s", (time.time() - t1)) def _compute_private_nodes(self, deps_graph): """ computes a list of nodes that are not required to be built, as they are @@ -339,9 +356,9 @@ def _build(self, nodes_by_level, skip_private_nodes, deps_graph): for conan_ref, package_id, conan_file, build_needed in nodes_to_process: output = ScopedOutput(str(conan_ref), self._out) - package_ref = PackageReference(conan_ref, package_id) if build_needed and (conan_ref, package_id) not in self._built_packages: + package_ref = PackageReference(conan_ref, package_id) build_allowed = self._build_mode.allowed(conan_file, conan_ref) if not build_allowed: _raise_package_not_found_error(conan_file, conan_ref, output) @@ -357,32 +374,42 @@ def _build(self, nodes_by_level, skip_private_nodes, deps_graph): # Assign to node the propagated info self._propagate_info(conan_file, conan_ref, flat, deps_graph) - self._remote_proxy.get_recipe_sources(conan_ref, conan_file.short_paths) builder = _ConanPackageBuilder(conan_file, package_ref, self._client_cache, output) - builder.build() - builder.package() - - self._remote_proxy.handle_package_manifest(package_ref, installed=True) - - # Call the info method - call_package_info(conan_file) - - # Log build - self._log_built_package(conan_file, package_ref, time.time() - t1) - self._built_packages.add((conan_ref, package_id)) + with self._client_cache.conanfile_write_lock(conan_ref): + self._remote_proxy.get_recipe_sources(conan_ref, conan_file.short_paths) + builder.prepare_build() + + with self._client_cache.conanfile_read_lock(conan_ref): + with self._client_cache.package_lock(builder.build_reference): + builder.build() + builder.package() + + self._remote_proxy.handle_package_manifest(package_ref, installed=True) + package_folder = self._client_cache.package(package_ref, conan_file.short_paths) + # Call the info method + call_package_info(conan_file, package_folder) + + # Log build + self._log_built_package(conan_file, package_ref, time.time() - t1) + self._built_packages.add((conan_ref, package_id)) else: # Get the package, we have a not outdated remote package + package_ref = None if conan_ref: - self.get_remote_package(conan_file, package_ref, output) + package_ref = PackageReference(conan_ref, package_id) + with self._client_cache.package_lock(package_ref): + self._get_remote_package(conan_file, package_ref, output) # Assign to the node the propagated info # (conan_ref could be None if user project, but of course assign the info self._propagate_info(conan_file, conan_ref, flat, deps_graph) - # Call the info method - call_package_info(conan_file) + if package_ref: + # Call the info method + package_folder = self._client_cache.package(package_ref, conan_file.short_paths) + call_package_info(conan_file, package_folder) - def get_remote_package(self, conan_file, package_reference, output): + def _get_remote_package(self, conan_file, package_reference, output): """Get remote package. It won't check if it's outdated""" # Compute conan_file package from local (already compiled) or from remote @@ -399,6 +426,8 @@ def get_remote_package(self, conan_file, package_reference, output): short_paths=conan_file.short_paths): _handle_system_requirements(conan_file, package_reference, self._client_cache, output) + if get_env("CONAN_READ_ONLY_CACHE", False): + make_read_only(package_folder) return True _raise_package_not_found_error(conan_file, package_reference.conan, output) @@ -420,9 +449,6 @@ def _propagate_info(conan_file, conan_ref, flat, deps_graph): conan_file.deps_env_info.update(n.conanfile.env_info, n.conan_ref.name) conan_file.deps_user_info[n.conan_ref.name] = n.conanfile.user_info - # Update the env_values with the inherited from dependencies - conan_file._env_values.update(conan_file.deps_env_info) - # Update the info but filtering the package values that not apply to the subtree # of this current node and its dependencies. subtree_libnames = [ref.name for (ref, _) in node_order] @@ -452,7 +478,7 @@ def _get_nodes(self, nodes_by_level, skip_nodes): build_node = False package_id = None if conan_ref: - logger.debug("Processing node %s" % repr(conan_ref)) + logger.debug("Processing node %s", repr(conan_ref)) package_id = conan_file.info.package_id() package_reference = PackageReference(conan_ref, package_id) # Avoid processing twice the same package reference diff --git a/conans/client/linter.py b/conans/client/linter.py index ec2be42737e..2402afefa57 100644 --- a/conans/client/linter.py +++ b/conans/client/linter.py @@ -85,7 +85,8 @@ def _normal_linter(conanfile_path): output_json = _runner(args) - dynamic_fields = "source_folder", "build_folder", "info_build", "build_requires", "info" + dynamic_fields = ("source_folder", "build_folder", "package_folder", "info_build", + "build_requires", "info") def _accept_message(msg): symbol = msg.get("symbol") diff --git a/conans/client/loader.py b/conans/client/loader.py index bb20887aeb2..248c00186eb 100644 --- a/conans/client/loader.py +++ b/conans/client/loader.py @@ -87,7 +87,7 @@ def _parse_conan_txt(self, contents, path, output): # It is necessary to copy the settings, because the above is only a constraint of # conanfile settings, and a txt doesn't define settings. Necessary for generators, # as cmake_multi, that check build_type. - conanfile.settings = self._settings.copy() + conanfile.settings = self._settings.copy_values() try: parser = ConanFileTextLoader(contents) @@ -109,12 +109,12 @@ def _parse_conan_txt(self, contents, path, output): conanfile._env_values.update(self._env_values) return conanfile - def load_virtual(self, references, current_path, scope_options=True, + def load_virtual(self, references, cwd, scope_options=True, build_requires_options=None): # If user don't specify namespace in options, assume that it is # for the reference (keep compatibility) - conanfile = ConanFile(None, self._runner, self._settings.copy(), current_path) - + conanfile = ConanFile(None, self._runner, self._settings.copy(), conanfile_directory=cwd) + conanfile.settings = self._settings.copy_values() # Assign environment conanfile._env_values.update(self._env_values) diff --git a/conans/client/loader_parse.py b/conans/client/loader_parse.py index 457215e63fc..0e52ae2c44b 100644 --- a/conans/client/loader_parse.py +++ b/conans/client/loader_parse.py @@ -7,7 +7,6 @@ from conans.errors import ConanException, NotFoundException from conans.model.conan_file import ConanFile from conans.util.config_parser import ConfigParser -from conans.util.files import rmdir from conans.tools import chdir from conans.client.generators import registered_generators from conans.model import Generator @@ -40,7 +39,7 @@ class defining the Recipe, but also process possible existing generators raise ConanException("More than 1 conanfile in the file") if (inspect.isclass(attr) and issubclass(attr, Generator) and attr != Generator and attr.__dict__["__module__"] == filename): - registered_generators.add(attr.__name__, attr) + registered_generators.add(attr.__name__, attr) if result is None: raise ConanException("No subclass of ConanFile") @@ -51,14 +50,6 @@ class defining the Recipe, but also process possible existing generators def _parse_file(conan_file_path): """ From a given path, obtain the in memory python import module """ - # Check if precompiled exist, delete it - if os.path.exists(conan_file_path + "c"): - os.unlink(conan_file_path + "c") - - # Python 3 - pycache = os.path.join(os.path.dirname(conan_file_path), "__pycache__") - if os.path.exists(pycache): - rmdir(pycache) if not os.path.exists(conan_file_path): raise NotFoundException("%s not found!" % conan_file_path) @@ -70,17 +61,23 @@ def _parse_file(conan_file_path): sys.path.append(current_dir) old_modules = list(sys.modules.keys()) with chdir(current_dir): + sys.dont_write_bytecode = True loaded = imp.load_source(filename, conan_file_path) + sys.dont_write_bytecode = False # Put all imported files under a new package name module_id = uuid.uuid1() added_modules = set(sys.modules).difference(old_modules) for added in added_modules: module = sys.modules[added] if module: - folder = os.path.dirname(module.__file__) - if folder.startswith(current_dir): - module = sys.modules.pop(added) - sys.modules["%s.%s" % (module_id, added)] = module + try: + folder = os.path.dirname(module.__file__) + except AttributeError: # some module doesn't have __file__ + pass + else: + if folder.startswith(current_dir): + module = sys.modules.pop(added) + sys.modules["%s.%s" % (module_id, added)] = module except Exception: import traceback trace = traceback.format_exc().split('\n') diff --git a/conans/client/manager.py b/conans/client/manager.py index 3adde12c21c..d4984a94292 100644 --- a/conans/client/manager.py +++ b/conans/client/manager.py @@ -1,7 +1,6 @@ import fnmatch import os import time -import shutil from collections import OrderedDict, Counter import copy @@ -13,7 +12,7 @@ from conans.client.export import export_conanfile, load_export_conanfile from conans.client.generators import write_generators from conans.client.generators.text import TXTGenerator -from conans.client.importer import run_imports, undo_imports +from conans.client.importer import run_imports, undo_imports, run_deploy from conans.client.installer import ConanInstaller, call_system_requirements from conans.client.loader import ConanFileLoader from conans.client.manifest_manager import ManifestManager @@ -25,7 +24,7 @@ from conans.client.remote_registry import RemoteRegistry from conans.client.remover import ConanRemover from conans.client.require_resolver import RequireResolver -from conans.client.source import config_source, config_source_local +from conans.client.source import config_source_local from conans.client.uploader import ConanUploader from conans.client.userio import UserIO from conans.errors import NotFoundException, ConanException, conanfile_exception_formatter @@ -116,35 +115,26 @@ def __init__(self, client_cache, user_io, runner, remote_manager, search_manager self._search_manager = search_manager self._settings_preprocessor = settings_preprocessor - def load_consumer_conanfile(self, conanfile_path, current_path, output, reference=None, - deps_cpp_info_required=False): + def load_consumer_conanfile(self, conanfile_path, info_folder, output, reference=None, + deps_info_required=False): - profile = read_conaninfo_profile(current_path) or self._client_cache.default_profile + profile = read_conaninfo_profile(info_folder) or self._client_cache.default_profile loader = self.get_loader(profile) if conanfile_path.endswith(".py"): consumer = not reference conanfile = loader.load_conan(conanfile_path, output, consumer, reference) else: conanfile = loader.load_conan_txt(conanfile_path, output) - if deps_cpp_info_required is not None: - _load_deps_cpp_info(current_path, conanfile, required=deps_cpp_info_required) + if deps_info_required is not None: + _load_deps_info(info_folder, conanfile, required=deps_info_required) + return conanfile def get_loader(self, profile): - return ConanFileLoader(self._runner, self._get_settings(profile), - self._profile_with_defaults(profile)) - - def _get_settings(self, profile): - self._client_cache.settings.values = self._profile_with_defaults(profile).settings_values + self._client_cache.settings.values = profile.settings_values # Settings preprocessor self._settings_preprocessor.preprocess(self._client_cache.settings) - return self._client_cache.settings - - def _profile_with_defaults(self, profile): - # Get the defaults, and apply on it the profile - tmp_profile = copy.copy(self._client_cache.default_profile) - tmp_profile.update(profile) - return tmp_profile + return ConanFileLoader(self._runner, self._client_cache.settings, profile) def export(self, user, channel, conan_file_path, keep_source=False, filename=None, name=None, version=None): @@ -174,25 +164,25 @@ def export(self, user, channel, conan_file_path, keep_source=False, filename=Non "You exported '%s' but already existing '%s'" % (conan_ref_str, " ".join(str(s) for s in refs))) output = ScopedOutput(str(conan_ref), self._user_io.out) - export_conanfile(output, self._client_cache, conanfile, src_folder, conan_ref, keep_source, - filename) + with self._client_cache.conanfile_write_lock(conan_ref): + export_conanfile(output, self._client_cache, conanfile, src_folder, conan_ref, keep_source, + filename) + + def export_pkg(self, reference, source_folder, build_folder, install_folder, profile, force): - def package_files(self, reference, source_folder, build_folder, package_folder, profile, - force): - """ Bundle pre-existing binaries - @param reference: ConanFileReference - """ conan_file_path = self._client_cache.conanfile(reference) if not os.path.exists(conan_file_path): raise ConanException("Package recipe '%s' does not exist" % str(reference)) - current_path = package_folder remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote_name=None, update=False, check_updates=False, manifest_manager=None) loader = self.get_loader(profile) - conanfile = loader.load_virtual([reference], current_path) + conanfile = loader.load_virtual([reference], None) + 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) deps_graph = graph_builder.load(conanfile) @@ -210,20 +200,16 @@ def package_files(self, reference, source_folder, build_folder, package_folder, if force: rmdir(dest_package_folder) else: - raise ConanException("Package already exists. Please use --force, -f to overwrite it") + raise ConanException("Package already exists. Please use --force, -f to " + "overwrite it") recipe_hash = self._client_cache.load_manifest(reference).summary_hash conanfile.info.recipe_hash = recipe_hash if source_folder or build_folder: + install_folder = build_folder # conaninfo.txt will be there package_output = ScopedOutput(str(reference), self._user_io.out) packager.create_package(conanfile, source_folder, build_folder, dest_package_folder, - package_output, local=True) - else: # we are specifying a final package - shutil.copytree(package_folder, dest_package_folder, symlinks=True) - save(os.path.join(dest_package_folder, CONANINFO), conanfile.info.dumps()) - # Create the digest for the package - digest = FileTreeManifest.create(dest_package_folder) - save(os.path.join(dest_package_folder, CONAN_MANIFEST), str(digest)) + install_folder, package_output, local=True) def download(self, reference, package_ids, remote=None): """ Download conanfile and specified packages to local repository @@ -241,6 +227,8 @@ def download(self, reference, package_ids, remote=None): if package_ids: remote_proxy.download_packages(reference, package_ids) else: + self._user_io.out.info("Getting the complete package list " + "from '%s'..." % str(reference)) packages_props = remote_proxy.search_packages(reference, None) if not packages_props: output = ScopedOutput(str(reference), self._user_io.out) @@ -248,9 +236,11 @@ def download(self, reference, package_ids, remote=None): else: remote_proxy.download_packages(reference, list(packages_props.keys())) - def _get_conanfile_object(self, loader, reference_or_path, conanfile_filename, current_path): + def _get_conanfile_object(self, loader, reference_or_path, conanfile_filename, cwd=None): + """cwd only used for virtuals, to pass it the current directory and make available the + conanfile.conanfile_directory (smell)""" if isinstance(reference_or_path, ConanFileReference): - conanfile = loader.load_virtual([reference_or_path], current_path) + conanfile = loader.load_virtual([reference_or_path], cwd) else: output = ScopedOutput("PROJECT", self._user_io.out) try: @@ -264,7 +254,8 @@ def _get_conanfile_object(self, loader, reference_or_path, conanfile_filename, c return conanfile - def _inject_require(self, conanfile, inject_require): + @staticmethod + def _inject_require(conanfile, inject_require): """ test_package functionality requires injecting the tested package as requirement before running the install """ @@ -280,24 +271,24 @@ def _get_graph_builder(self, loader, update, remote_proxy): graph_builder = DepsGraphBuilder(remote_proxy, self._user_io.out, loader, resolver) return graph_builder - def _get_deps_graph(self, reference, profile, filename, current_path, remote_proxy): + def _get_deps_graph(self, reference, profile, filename, remote_proxy): loader = self.get_loader(profile) - conanfile = self._get_conanfile_object(loader, reference, filename, current_path) + conanfile = self._get_conanfile_object(loader, reference, filename) graph_builder = self._get_graph_builder(loader, False, remote_proxy) deps_graph = graph_builder.load(conanfile) return deps_graph, graph_builder, conanfile - def info_build_order(self, reference, profile, filename, build_order, remote, check_updates, cwd): + def info_build_order(self, reference, profile, filename, build_order, remote, check_updates): remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote, update=False, check_updates=check_updates) - deps_graph, _, _ = self._get_deps_graph(reference, profile, filename, cwd, remote_proxy) + deps_graph, _, _ = self._get_deps_graph(reference, profile, filename, remote_proxy) result = deps_graph.build_order(build_order) return result - def info_nodes_to_build(self, reference, profile, filename, build_modes, remote, check_updates, cwd): + def info_nodes_to_build(self, reference, profile, filename, build_modes, remote, check_updates): remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote, update=False, check_updates=check_updates) - deps_graph, _, conanfile = self._get_deps_graph(reference, profile, filename, cwd, remote_proxy) + deps_graph, _, conanfile = self._get_deps_graph(reference, profile, filename, remote_proxy) build_mode = BuildMode(build_modes, self._user_io.out) installer = ConanInstaller(self._client_cache, self._user_io.out, remote_proxy, build_mode, None) @@ -314,7 +305,7 @@ def _get_project_reference(self, reference, conanfile): return project_reference - def info_get_graph(self, reference, current_path, profile, remote=None, filename=None, check_updates=False): + def info_get_graph(self, reference, profile, remote=None, filename=None, check_updates=False): """ Fetch and build all dependencies for the given reference @param reference: ConanFileReference or path to user space conanfile @param current_path: where the output files will be saved @@ -327,8 +318,7 @@ def info_get_graph(self, reference, current_path, profile, remote=None, filename remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote, update=False, check_updates=check_updates) - deps_graph, graph_builder, conanfile = self._get_deps_graph(reference, profile, filename, - current_path, remote_proxy) + deps_graph, graph_builder, conanfile = self._get_deps_graph(reference, profile, filename, remote_proxy) if check_updates: graph_updates_info = graph_builder.get_graph_updates_info(deps_graph) @@ -337,34 +327,41 @@ def info_get_graph(self, reference, current_path, profile, remote=None, filename return deps_graph, graph_updates_info, self._get_project_reference(reference, conanfile) - def install(self, reference, current_path, profile, remote=None, + def install(self, reference, install_folder, profile, remote=None, build_modes=None, filename=None, update=False, manifest_folder=None, manifest_verify=False, manifest_interactive=False, - generators=None, no_imports=False, inject_require=None): + generators=None, no_imports=False, inject_require=None, cwd=None, deploy=False): """ Fetch and build all dependencies for the given reference @param reference: ConanFileReference or path to user space conanfile - @param current_path: where the output files will be saved + @param install_folder: where the output files will be saved @param remote: install only from that remote - @param profile: Profile object with both the -s introduced options and profile readed values + @param profile: Profile object with both the -s introduced options and profile read values @param build_modes: List of build_modes specified @param filename: Optional filename of the conanfile @param update: Check for updated in the upstream remotes (and update) @param manifest_folder: Folder to install the manifests @param manifest_verify: Verify dependencies manifests against stored ones - @param manifest_interactive: Install deps manifests in folder for later verify, asking user for confirmation - @param generators: List of generators from command line + @param manifest_interactive: Install deps manifests in folder for later verify, asking user + for confirmation + @param generators: List of generators from command line. If False, no generator will be + written @param no_imports: Install specified packages but avoid running imports + @param inject_require: Reference to add as a requirement to the conanfile + @param cwd: Only used in case of reference, to get a conanfile_directory to a virtual SMELL """ - generators = generators or [] + if generators is not False: + generators = set(generators) if generators else set() + generators.add("txt") # Add txt generator by default + manifest_manager = ManifestManager(manifest_folder, user_io=self._user_io, client_cache=self._client_cache, verify=manifest_verify, interactive=manifest_interactive) if manifest_folder else None remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote, - update=update, check_updates=False, manifest_manager=manifest_manager) + update=update, manifest_manager=manifest_manager) loader = self.get_loader(profile) - conanfile = self._get_conanfile_object(loader, reference, filename, current_path) + conanfile = self._get_conanfile_object(loader, reference, filename, cwd=cwd) if inject_require: self._inject_require(conanfile, inject_require) graph_builder = self._get_graph_builder(loader, update, remote_proxy) @@ -404,134 +401,79 @@ def install(self, reference, current_path, profile, remote=None, if not isinstance(reference, ConanFileReference): build_requires.install("", conanfile, installer) - installer.install(deps_graph, current_path) + installer.install(deps_graph) build_mode.report_matches() - # Write generators - tmp = list(conanfile.generators) # Add the command line specified generators - tmp.extend([g for g in generators if g not in tmp]) - conanfile.generators = tmp - write_generators(conanfile, current_path, output) - - if not isinstance(reference, ConanFileReference): + if install_folder: + # Write generators + if generators is not False: + tmp = list(conanfile.generators) # Add the command line specified generators + tmp.extend([g for g in generators if g not in tmp]) + conanfile.generators = tmp + write_generators(conanfile, install_folder, output) + # Write conaninfo content = normalize(conanfile.info.dumps()) - save(os.path.join(current_path, CONANINFO), content) + save(os.path.join(install_folder, CONANINFO), content) output.info("Generated %s" % CONANINFO) if not no_imports: - run_imports(conanfile, current_path, output) + run_imports(conanfile, install_folder, output) call_system_requirements(conanfile, output) + if deploy: + # The conanfile loaded is really a virtual one. The one with the deploy is the first level one + deploy_conanfile = deps_graph.inverse_levels()[1][0].conanfile + if hasattr(deploy_conanfile, "deploy") and callable(deploy_conanfile.deploy): + run_deploy(deploy_conanfile, install_folder, output) + if manifest_manager: manifest_manager.print_log() - def source(self, current_path, reference, force): - if not isinstance(reference, ConanFileReference): - output = ScopedOutput("PROJECT", self._user_io.out) - conanfile_path = os.path.join(reference, CONANFILE) - conanfile = self.load_consumer_conanfile(conanfile_path, current_path, - output, deps_cpp_info_required=None) - config_source_local(current_path, conanfile, output) - else: - output = ScopedOutput(str(reference), self._user_io.out) - conanfile_path = self._client_cache.conanfile(reference) - conanfile = self.load_consumer_conanfile(conanfile_path, current_path, - output, reference=reference, - deps_cpp_info_required=None) - src_folder = self._client_cache.source(reference, conanfile.short_paths) - export_folder = self._client_cache.export(reference) - export_src_folder = self._client_cache.export_sources(reference, conanfile.short_paths) - config_source(export_folder, export_src_folder, src_folder, conanfile, output, force) + def source(self, conanfile_path, source_folder, info_folder): + """ + :param conanfile_path: Absolute path to a conanfile + :param source_folder: Absolute path where to put the files + :param info_folder: Absolute path where to read the info files + :param package_folder: Absolute path to the package_folder, only to have the var present + :return: + """ + output = ScopedOutput("PROJECT", self._user_io.out) + # only infos if exist + conanfile = self.load_consumer_conanfile(conanfile_path, info_folder, output) + config_source_local(source_folder, conanfile, output) def imports_undo(self, current_path): undo_imports(current_path, self._user_io.out) - def imports(self, current_path, reference, conan_file_path, dest_folder): - if not isinstance(reference, ConanFileReference): - output = ScopedOutput("PROJECT", self._user_io.out) - if not conan_file_path: - conan_file_path = os.path.join(reference, CONANFILE) - if not os.path.exists(conan_file_path): - conan_file_path = os.path.join(reference, CONANFILE_TXT) - elif not os.path.isabs(conan_file_path): - conan_file_path = os.path.abspath(os.path.join(reference, conan_file_path)) - reference = None - else: - output = ScopedOutput(str(reference), self._user_io.out) - conan_file_path = self._client_cache.conanfile(reference) + def imports(self, conan_file_path, dest_folder, info_folder): + """ + :param conan_file_path: Abs path to a conanfile + :param dest_folder: Folder where to put the files + :param info_folder: Folder containing the conaninfo/conanbuildinfo.txt files + :return: + """ - conanfile = self.load_consumer_conanfile(conan_file_path, current_path, - output, reference=reference, - deps_cpp_info_required=True) + output = ScopedOutput("PROJECT", self._user_io.out) + conanfile = self.load_consumer_conanfile(conan_file_path, info_folder, + output, deps_info_required=True) - if dest_folder: - if not os.path.isabs(dest_folder): - dest_folder = os.path.normpath(os.path.join(current_path, dest_folder)) - mkdir(dest_folder) - else: - dest_folder = current_path run_imports(conanfile, dest_folder, output) - def local_package(self, package_folder, recipe_folder, build_folder, source_folder): + def local_package(self, package_folder, recipe_folder, build_folder, source_folder, + install_folder): if package_folder == build_folder: raise ConanException("Cannot 'conan package' to the build folder. " "--build_folder and package folder can't be the same") output = ScopedOutput("PROJECT", self._user_io.out) conan_file_path = os.path.join(recipe_folder, CONANFILE) - conanfile = self.load_consumer_conanfile(conan_file_path, build_folder, output) - packager.create_package(conanfile, source_folder, build_folder, package_folder, output, - local=True, copy_info=True) - - def package(self, reference, package_id): - # Package paths - conan_file_path = self._client_cache.conanfile(reference) - if not os.path.exists(conan_file_path): - raise ConanException("Package recipe '%s' does not exist" % str(reference)) + conanfile = self.load_consumer_conanfile(conan_file_path, install_folder, output, + deps_info_required=True) + packager.create_package(conanfile, source_folder, build_folder, package_folder, + install_folder, output, local=True, copy_info=True) - conanfile = load_conanfile_class(conan_file_path) - if hasattr(conanfile, "build_id"): - raise ConanException("package command does not support recipes with 'build_id'\n" - "To repackage them use 'conan install'") - - if not package_id: - packages = [PackageReference(reference, packid) - for packid in self._client_cache.conan_builds(reference)] - if not packages: - raise NotFoundException("%s: Package has not been built in local cache\n" - "Please read the 'conan package' command help\n" - "Use 'conan install' or 'conan test_package' to build and " - "create binaries" % str(reference)) - else: - packages = [PackageReference(reference, package_id)] - - package_source_folder = self._client_cache.source(reference, conanfile.short_paths) - for package_reference in packages: - build_folder = self._client_cache.build(package_reference, short_paths=None) - if not os.path.exists(build_folder): - raise NotFoundException("%s: Package binary '%s' folder doesn't exist\n" - "Please read the 'conan package' command help\n" - "Use 'conan install' or 'conan test_package' to build and " - "create binaries" - % (str(reference), package_reference.package_id)) - # The package already exist, we can use short_paths if they were defined - package_folder = self._client_cache.package(package_reference, short_paths=None) - # Will read current conaninfo with specified options and load conanfile with them - output = ScopedOutput(str(reference), self._user_io.out) - output.info("Re-packaging %s" % package_reference.package_id) - conanfile = self.load_consumer_conanfile(conan_file_path, build_folder, - output, reference=reference) - rmdir(package_folder) - if getattr(conanfile, 'no_copy_source', False): - source_folder = package_source_folder - else: - source_folder = build_folder - with environment_append(conanfile.env): - packager.create_package(conanfile, source_folder, build_folder, package_folder, - output, copy_info=True) - - def build(self, conanfile_path, source_folder, build_folder, package_folder, test=False): + def build(self, conanfile_path, source_folder, build_folder, package_folder, install_folder, + test=False): """ Call to build() method saved on the conanfile.py - param conanfile_path: the original source directory of the user containing a - conanfile.py + param conanfile_path: path to a conanfile.py """ logger.debug("Building in %s" % build_folder) logger.debug("Conanfile in %s" % conanfile_path) @@ -540,7 +482,8 @@ def build(self, conanfile_path, source_folder, build_folder, package_folder, tes # Append env_vars to execution environment and clear when block code ends output = ScopedOutput(("%s test package" % test) if test else "Project", self._user_io.out) - conan_file = self.load_consumer_conanfile(conanfile_path, build_folder, output) + conan_file = self.load_consumer_conanfile(conanfile_path, install_folder, output, + deps_info_required=True) except NotFoundException: # TODO: Auto generate conanfile from requirements file raise ConanException("'%s' file is needed for build.\n" @@ -555,11 +498,13 @@ def build(self, conanfile_path, source_folder, build_folder, package_folder, tes pass try: + mkdir(build_folder) os.chdir(build_folder) - conan_file._conanfile_directory = source_folder + conan_file.conanfile_directory = source_folder conan_file.build_folder = build_folder conan_file.source_folder = source_folder conan_file.package_folder = package_folder + conan_file.install_folder = install_folder with environment_append(conan_file.env): output.highlight("Running build()") with conanfile_exception_formatter(str(conan_file), "build"): @@ -682,6 +627,10 @@ def remove(self, pattern, src=False, build_ids=None, package_ids_filter=None, fo def user(self, remote=None, name=None, password=None): remote_proxy = ConanProxy(self._client_cache, self._user_io, self._remote_manager, remote) + if password == "": + if not remote: + remote = remote_proxy.registry.default_remote.name + name, password = self._user_io.request_login(remote_name=remote, username=name) return remote_proxy.authenticate(name, password) def get_path(self, reference, package_id=None, path=None, remote=None): @@ -709,13 +658,13 @@ class AliasConanfile(ConanFile): save(os.path.join(export_path, CONAN_MANIFEST), str(digest)) -def _load_deps_cpp_info(current_path, conanfile, required): +def _load_deps_info(current_path, conanfile, required): def get_forbidden_access_object(field_name): class InfoObjectNotDefined(object): def __getitem__(self, item): raise ConanException("self.%s not defined. If you need it for a " - "local command run 'conan install -g txt'" % field_name) + "local command run 'conan install'" % field_name) __getattr__ = __getitem__ return InfoObjectNotDefined() @@ -724,15 +673,21 @@ def __getitem__(self, item): return info_file_path = os.path.join(current_path, BUILD_INFO) try: - deps_cpp_info, deps_user_info = TXTGenerator.loads(load(info_file_path)) + deps_cpp_info, deps_user_info, deps_env_info = TXTGenerator.loads(load(info_file_path)) conanfile.deps_cpp_info = deps_cpp_info conanfile.deps_user_info = deps_user_info + conanfile.deps_env_info = deps_env_info except IOError: if required: raise ConanException("%s file not found in %s\nIt is required for this command\n" - "You can generate it using 'conan install -g txt'" + "You can generate it using 'conan install'" % (BUILD_INFO, current_path)) conanfile.deps_cpp_info = get_forbidden_access_object("deps_cpp_info") conanfile.deps_user_info = get_forbidden_access_object("deps_user_info") except ConanException: - raise ConanException("Parse error in '%s' file in %s" % (BUILD_INFO, current_path)) \ No newline at end of file + raise ConanException("Parse error in '%s' file in %s" % (BUILD_INFO, current_path)) + + +def existing_info_files(folder): + return os.path.exists(os.path.join(folder, CONANINFO)) and \ + os.path.exists(os.path.join(folder, BUILD_INFO)) diff --git a/conans/client/meson.py b/conans/client/meson.py new file mode 100644 index 00000000000..ba4efc2de73 --- /dev/null +++ b/conans/client/meson.py @@ -0,0 +1,88 @@ +import os + +from conans import tools +from conans.client import join_arguments, defs_to_string +from conans.errors import ConanException +from conans.tools import args_to_string +from conans.util.files import mkdir + + +class Meson(object): + + def __init__(self, conanfile, backend=None, build_type=None): + """ + :param conanfile: Conanfile instance (or settings for retro compatibility) + :param backend: Generator name to use or none to autodetect. + Possible values: ninja,vs,vs2010,vs2015,vs2017,xcode + :param build_type: Overrides default build type comming from settings + """ + self._conanfile = conanfile + self._settings = conanfile.settings + + self._os = self._settings.get_safe("os") + self._compiler = self._settings.get_safe("compiler") + self._compiler_version = self._settings.get_safe("compiler.version") + self._build_type = self._settings.get_safe("build_type") + + self.backend = backend or "ninja" # Other backends are poorly supported, not default other. + self.build_dir = None + self.definitions = {} + if build_type and build_type != self._build_type: + # Call the setter to warn and update the definitions if needed + self.build_type = build_type + + @property + def build_type(self): + return self._build_type + + @build_type.setter + def build_type(self, build_type): + settings_build_type = self._settings.get_safe("build_type") + if build_type != settings_build_type: + self._conanfile.output.warn( + 'Set build type "%s" is different than the settings build_type "%s"' + % (build_type, settings_build_type)) + self._build_type = build_type + self.definitions.update(self._build_type_definition()) + + def configure(self, args=None, defs=None, source_dir=None, build_dir=None, + pkg_config_paths=None): + args = args or [] + defs = defs or {} + pc_paths = os.pathsep.join(pkg_config_paths or [self._conanfile.build_folder]) + source_dir = source_dir or self._conanfile.source_folder + self.build_dir = build_dir or self.build_dir or self._conanfile.build_folder or "." + + mkdir(self.build_dir) + build_type = ("--buildtype=%s" % self.build_type if self.build_type else "").lower() + arg_list = join_arguments([ + "--backend=%s" % self.backend, + args_to_string(args), + defs_to_string(defs), + build_type + ]) + command = 'meson "%s" "%s" %s' % (source_dir, self.build_dir, arg_list) + command = self._append_vs_if_needed(command) + with tools.environment_append({"PKG_CONFIG_PATH": pc_paths}): + self._conanfile.run(command) + + def _append_vs_if_needed(self, command): + if self._compiler == "Visual Studio" and self.backend == "ninja": + command = "%s && %s" % (tools.vcvars_command(self._conanfile.settings), command) + return command + + def build(self, args=None, build_dir=None, targets=None): + if self.backend != "ninja": + raise ConanException("Build only supported with 'ninja' backend") + + args = args or [] + build_dir = build_dir or self.build_dir or self._conanfile.build_folder + + arg_list = join_arguments([ + "-C %s" % build_dir, + args_to_string(args), + args_to_string(targets) + ]) + command = "ninja %s" % arg_list + command = self._append_vs_if_needed(command) + self._conanfile.run(command) diff --git a/conans/client/new.py b/conans/client/new.py index 03e93d2d7d6..33f428a2756 100644 --- a/conans/client/new.py +++ b/conans/client/new.py @@ -29,9 +29,13 @@ def source(self): def build(self): cmake = CMake(self) - self.run('cmake hello %s' % cmake.command_line) - self.run("cmake --build . %s" % cmake.build_config) + cmake.configure(source_dir="%s/hello" % self.source_folder) + cmake.build() + # Explicit way: + # self.run('cmake %s/hello %s' % (self.source_folder, cmake.command_line)) + # self.run("cmake --build . %s" % cmake.build_config) + def package(self): self.copy("*.h", dst="include", src="hello") self.copy("*hello.lib", dst="lib", keep_path=False) @@ -45,6 +49,7 @@ def package_info(self): """ conanfile_bare = """from conans import ConanFile +from conans import tools class {package_name}Conan(ConanFile): name = "{name}" @@ -54,8 +59,11 @@ class {package_name}Conan(ConanFile): url = "None" license = "None" + def package(self): + self.copy("*") + def package_info(self): - self.cpp_info.libs = self.collect_libs() + self.cpp_info.libs = tools.collect_libs(self) """ conanfile_sources = """from conans import ConanFile, CMake @@ -75,8 +83,12 @@ class {package_name}Conan(ConanFile): def build(self): cmake = CMake(self) - self.run('cmake %s/src %s' % (self.source_folder, cmake.command_line)) - self.run("cmake --build . %s" % cmake.build_config) + cmake.configure(source_dir="%s/src" % self.source_folder) + cmake.build() + + # Explicit way: + # self.run('cmake %s/src %s' % (self.source_folder, cmake.command_line)) + # self.run("cmake --build . %s" % cmake.build_config) def package(self): self.copy("*.h", dst="include", src="src") diff --git a/conans/client/new_ci.py b/conans/client/new_ci.py index 2dc8c80c474..16fc223ca2f 100644 --- a/conans/client/new_ci.py +++ b/conans/client/new_ci.py @@ -177,7 +177,8 @@ def get_travis(name, version, user, channel, linux_gcc_versions, linux_clang_ver xcode_map = {"8.1": "8.3", "8.0": "8.2", - "7.3": "7.3"} + "7.3": "7.3", + "9.0": "9"} for apple_clang in osx_clang_versions: xcode = xcode_map[apple_clang] config.append(osx_config.format(xcode=xcode, version=apple_clang)) @@ -246,7 +247,7 @@ def ci_get_files(name, version, user, channel, visual_versions, linux_gcc_versio if gitlab_clang_versions is True: gitlab_clang_versions = clang_versions if osx_clang_versions is True: - osx_clang_versions = ["7.3", "8.0", "8.1"] + osx_clang_versions = ["7.3", "8.0", "8.1", "9.0"] if not visual_versions: visual_versions = [] if not linux_gcc_versions: diff --git a/conans/client/package_tester.py b/conans/client/package_tester.py new file mode 100644 index 00000000000..0ac8522b954 --- /dev/null +++ b/conans/client/package_tester.py @@ -0,0 +1,89 @@ +import hashlib +import os + + +from conans.errors import ConanException +from conans.model.ref import ConanFileReference +from conans.util.files import rmdir + + +class PackageTester(object): + + def __init__(self, manager, user_io): + self._manager = manager + self._user_io = user_io + + def _call_requirements(self, conanfile_path, profile): + + loader = self._manager.get_loader(profile) + test_conanfile = loader.load_conan(conanfile_path, self._user_io.out, consumer=True) + try: + if hasattr(test_conanfile, "requirements"): + test_conanfile.requirements() + except Exception as e: + raise ConanException("Error in test_package/conanfile.py requirements(). %s" % str(e)) + + return test_conanfile + + def install_build_and_test(self, conanfile_abs_path, profile, name, version, user, channel, + remote, update, build_modes=None): + """ + Installs the reference (specified by the parameters or extracted from the test conanfile) + and builds the test_package/conanfile.py running the test() method. + """ + base_folder = os.path.dirname(conanfile_abs_path) + build_folder = self._build_folder(profile, base_folder) + rmdir(build_folder) + test_conanfile = self._call_requirements(conanfile_abs_path, profile) + ref = self._get_reference_to_test(test_conanfile.requires, name, version, user, channel) + if build_modes is None: + build_modes = ["never"] + self._manager.install(inject_require=ref, + reference=base_folder, + install_folder=build_folder, + remote=remote, + profile=profile, + update=update, + build_modes=build_modes) + self._manager.build(conanfile_abs_path, base_folder, build_folder, package_folder=None, + install_folder=build_folder, test=str(ref)) + + @staticmethod + def _build_folder(profile, test_folder): + sha = hashlib.sha1("".join(profile.dumps()).encode()).hexdigest() + build_folder = os.path.join(test_folder, "build", sha) + return build_folder + + @staticmethod + def _get_reference_to_test(requires, name, version, user, channel): + """Given the requirements of a test_package/conanfile.py and a user specified values, + check if there are any conflict in the specified version and return the package to be + tested""" + + # User do not specify anything, and there is a require + if name is None and len(requires.items()) == 1: + _, req = list(requires.items())[0] + pname, pversion, puser, pchannel = req.conan_reference + # The specified name is already in the test_package/conanfile requires, check conflicts + elif name is not None and name in requires: + a_ref = requires[name].conan_reference + if version and (version != a_ref.version): + raise ConanException("The specified version doesn't match with the " + "requirement of the test_package/conanfile.py") + pname, pversion, puser, pchannel = a_ref + if user and channel: # Override from the command line + puser, pchannel = user, channel + # Different from the requirements in test_package + elif name is not None: + if not version or not channel or not user: + reqs = ", ".join(requires.keys()) + raise ConanException("The package name '%s' doesn't match with any requirement " + "in the testing conanfile.py: %s" % (name, reqs)) + else: + pname, pversion, puser, pchannel = name, version, user, channel + else: + raise ConanException("Cannot deduce the reference to be tested, specify a reference in " + "the 'conan test' command or a single requirement in the " + "test_package/conanfile.py file.") + + return ConanFileReference(pname, pversion, puser, pchannel) diff --git a/conans/client/packager.py b/conans/client/packager.py index 2a708743cb2..a201a2bda75 100644 --- a/conans/client/packager.py +++ b/conans/client/packager.py @@ -1,6 +1,7 @@ import os import shutil +from conans.client import tools from conans.util.files import mkdir, save, rmdir from conans.util.log import logger from conans.paths import CONANINFO, CONAN_MANIFEST @@ -11,8 +12,8 @@ from conans.client.file_copier import FileCopier -def create_package(conanfile, source_folder, build_folder, package_folder, output, local=False, - copy_info=False): +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 package folder """ @@ -35,14 +36,21 @@ def new_method(pattern, src=""): try: package_output = ScopedOutput("%s package()" % output.scope, output) output.highlight("Calling package()") + conanfile.package_folder = package_folder + conanfile.source_folder = source_folder + conanfile.build_folder = build_folder + if source_folder != build_folder: conanfile.copy = FileCopier(source_folder, package_folder, build_folder) with conanfile_exception_formatter(str(conanfile), "package"): - conanfile.package() + with tools.chdir(build_folder): + conanfile.package() conanfile.copy.report(package_output, warn=True) conanfile.copy = FileCopier(build_folder, package_folder) - with conanfile_exception_formatter(str(conanfile), "package"): - conanfile.package() + + with tools.chdir(build_folder): + with conanfile_exception_formatter(str(conanfile), "package"): + conanfile.package() conanfile.copy.report(package_output, warn=True) except Exception as e: if not local: @@ -57,22 +65,22 @@ def new_method(pattern, src=""): raise raise ConanException(e) - _create_aux_files(build_folder, package_folder, conanfile, copy_info) + _create_aux_files(install_folder, package_folder, conanfile, copy_info) output.success("Package '%s' created" % os.path.basename(package_folder)) -def _create_aux_files(build_folder, package_folder, conanfile, copy_info): +def _create_aux_files(install_folder, package_folder, conanfile, copy_info): """ auxiliary method that creates CONANINFO and manifest in the package_folder """ logger.debug("Creating config files to %s" % package_folder) if copy_info: try: - shutil.copy(os.path.join(build_folder, CONANINFO), package_folder) + shutil.copy(os.path.join(install_folder, CONANINFO), package_folder) except IOError: raise ConanException("%s does not exist inside of your %s folder. " "Try to re-build it again to solve it." - % (CONANINFO, build_folder)) + % (CONANINFO, install_folder)) else: save(os.path.join(package_folder, CONANINFO), conanfile.info.dumps()) diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index bb4d063b08e..539f65ad105 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -1,5 +1,5 @@ import os -from collections import OrderedDict +from collections import OrderedDict, defaultdict from conans.errors import ConanException from conans.model.env_info import EnvValues, unquote @@ -222,3 +222,74 @@ def get_package_name_value(item): base_profile.options.update(OptionsValues.loads(doc.options)) base_profile.env_values.update(EnvValues.loads(doc.env)) + + +def profile_from_args(profile, settings, options, env, scope, cwd, client_cache): + """ Return a Profile object, as the result of merging a potentially existing Profile + file and the args command-line arguments + """ + if profile is None: + file_profile = client_cache.default_profile + else: + file_profile, _ = read_profile(profile, cwd, client_cache.profiles_path) + args_profile = _profile_parse_args(settings, options, env, scope) + + if file_profile: + file_profile.update(args_profile) + return file_profile + else: + return args_profile + + +def _profile_parse_args(settings, options, envs, scopes): + """ return a Profile object result of parsing raw data + """ + def _get_tuples_list_from_extender_arg(items): + if not items: + return [] + # Validate the pairs + for item in items: + chunks = item.split("=", 1) + if len(chunks) != 2: + raise ConanException("Invalid input '%s', use 'name=value'" % item) + return [(item[0], item[1]) for item in [item.split("=", 1) for item in items]] + + def _get_simple_and_package_tuples(items): + """Parse items like "thing:item=value or item2=value2 and returns a tuple list for + the simple items (name, value) and a dict for the package items + {package: [(item, value)...)], ...} + """ + simple_items = [] + package_items = defaultdict(list) + tuples = _get_tuples_list_from_extender_arg(items) + for name, value in tuples: + if ":" in name: # Scoped items + tmp = name.split(":", 1) + ref_name = tmp[0] + name = tmp[1] + package_items[ref_name].append((name, value)) + else: + simple_items.append((name, value)) + return simple_items, package_items + + def _get_env_values(env, package_env): + env_values = EnvValues() + for name, value in env: + env_values.add(name, EnvValues.load_value(value)) + for package, data in package_env.items(): + for name, value in data: + env_values.add(name, EnvValues.load_value(value), package) + return env_values + + result = Profile() + options = _get_tuples_list_from_extender_arg(options) + result.options = OptionsValues(options) + env, package_env = _get_simple_and_package_tuples(envs) + env_values = _get_env_values(env, package_env) + result.env_values = env_values + settings, package_settings = _get_simple_and_package_tuples(settings) + result.settings = OrderedDict(settings) + for pkg, values in package_settings.items(): + result.package_settings[pkg] = OrderedDict(values) + result.scopes = Scopes.from_list(scopes) if scopes else Scopes() + return result diff --git a/conans/client/proxy.py b/conans/client/proxy.py index afb69f15b2c..7185219b4e3 100644 --- a/conans/client/proxy.py +++ b/conans/client/proxy.py @@ -7,7 +7,7 @@ from conans.client.remote_registry import RemoteRegistry from conans.util.log import logger import os -from conans.paths import rm_conandir, EXPORT_SOURCES_TGZ_NAME +from conans.paths import EXPORT_SOURCES_TGZ_NAME from conans.client.remover import DiskRemover from conans.util.tracer import log_package_got_from_local_cache,\ log_recipe_got_from_local_cache @@ -123,20 +123,12 @@ def get_recipe_sources(self, conan_reference, short_paths=False): current_remote) def get_recipe(self, conan_reference): - output = ScopedOutput(str(conan_reference), self._out) + with self._client_cache.conanfile_write_lock(conan_reference): + result = self._get_recipe(conan_reference) + return result - def _refresh(): - export_path = self._client_cache.export(conan_reference) - rmdir(export_path) - # It might need to remove shortpath - rm_conandir(self._client_cache.source(conan_reference)) - current_remote, _ = self._get_remote(conan_reference) - output.info("Retrieving from remote '%s'..." % current_remote.name) - self._remote_manager.get_recipe(conan_reference, export_path, current_remote) - if self._update: - output.info("Updated!") - else: - output.info("Installed!") + def _get_recipe(self, conan_reference): + output = ScopedOutput(str(conan_reference), self._out) # check if it is in disk conanfile_path = self._client_cache.conanfile(conan_reference) @@ -159,10 +151,11 @@ def _refresh(): % remote.name) output.warn("Refused to install!") else: - if remote != ref_remote: - # Delete packages, could be non coherent with new remote - DiskRemover(self._client_cache).remove_packages(conan_reference) - _refresh() + export_path = self._client_cache.export(conan_reference) + DiskRemover(self._client_cache).remove(conan_reference) + output.info("Retrieving from remote '%s'..." % remote.name) + self._remote_manager.get_recipe(conan_reference, export_path, remote) + output.info("Updated!") elif ret == -1: if not self._update: output.info("Current conanfile is newer " @@ -384,6 +377,7 @@ def download_packages(self, reference, package_ids): for package_id in package_ids: package_ref = PackageReference(reference, package_id) package_folder = self._client_cache.package(package_ref, short_paths=short_paths) + self._out.info("Downloading %s" % str(package_ref)) self._retrieve_remote_package(package_ref, package_folder, output, remote) def _retrieve_remote_package(self, package_ref, package_folder, output, remote=None): diff --git a/conans/client/require_resolver.py b/conans/client/require_resolver.py index d0cf3900ad0..cfcf5e5452f 100644 --- a/conans/client/require_resolver.py +++ b/conans/client/require_resolver.py @@ -50,7 +50,8 @@ def resolve(self, require, base_conanref): search_ref = str(ConanFileReference(ref.name, "*", ref.user, ref.channel)) resolved = self._resolve_local(search_ref, version_range) if not resolved: - remote_found = self._remote_search.search_remotes(search_ref) + # We should use ignorecase=False, we want the exact case! + remote_found = self._remote_search.search_remotes(search_ref, ignorecase=False) if remote_found: resolved = self._resolve_version(version_range, remote_found) diff --git a/conans/client/rest/rest_client.py b/conans/client/rest/rest_client.py index eb055e2cac3..7af2aac665f 100644 --- a/conans/client/rest/rest_client.py +++ b/conans/client/rest/rest_client.py @@ -1,4 +1,5 @@ -from conans.errors import EXCEPTION_CODE_MAPPING, NotFoundException, ConanException +from conans.errors import EXCEPTION_CODE_MAPPING, NotFoundException, ConanException, \ + AuthenticationException from requests.auth import AuthBase, HTTPBasicAuth from conans.util.log import logger import json @@ -9,7 +10,7 @@ import os from conans.model.manifest import FileTreeManifest from conans.client.rest.uploader_downloader import Uploader, Downloader -from conans.model.ref import ConanFileReference, PackageReference +from conans.model.ref import ConanFileReference from six.moves.urllib.parse import urlsplit, parse_qs, urlencode from conans import COMPLEX_SEARCH_CAPABILITY from conans.search.search import filter_packages @@ -241,12 +242,18 @@ def upload_package(self, package_reference, the_files, retry, retry_wait): @handle_return_deserializer() def authenticate(self, user, password): - '''Sends user + password to get a token''' + """Sends user + password to get a token""" auth = HTTPBasicAuth(user, password) url = "%s/users/authenticate" % self._remote_api_url t1 = time.time() ret = self.requester.get(url, auth=auth, headers=self.custom_headers, verify=self.verify_ssl) + if ret.status_code == 401: + raise AuthenticationException("Wrong user or password") + # Cannot check content-type=text/html, conan server is doing it wrong + if not ret.ok or "html>" in str(ret.content): + raise ConanException("%s\n\nInvalid server response, check remote URL and " + "try again" % str(ret.content)) duration = time.time() - t1 log_client_rest_api_call(url, "GET", duration, self.custom_headers) return ret diff --git a/conans/client/rest/uploader_downloader.py b/conans/client/rest/uploader_downloader.py index fa4ed60b68a..f887cf1bbeb 100644 --- a/conans/client/rest/uploader_downloader.py +++ b/conans/client/rest/uploader_downloader.py @@ -1,7 +1,7 @@ from conans.errors import ConanException, ConanConnectionError from conans.util.log import logger import traceback -from conans.util.files import save, sha1sum, exception_message_safe +from conans.util.files import save, sha1sum, exception_message_safe, to_file_bytes, mkdir import os import time from conans.util.tracer import log_download @@ -113,15 +113,24 @@ def __init__(self, requester, output, verify, chunk_size=1000): self.requester = requester self.verify = verify - def download(self, url, file_path=None, auth=None, retry=1, retry_wait=0): + def download(self, url, file_path=None, auth=None, retry=1, retry_wait=0, overwrite=False, + headers=None): + + if file_path and not os.path.isabs(file_path): + file_path = os.path.abspath(file_path) if file_path and os.path.exists(file_path): - # Should not happen, better to raise, probably we had to remove the dest folder before - raise ConanException("Error, the file to download already exists: '%s'" % file_path) + if overwrite: + if self.output: + self.output.warn("file '%s' already exists, overwriting" % file_path) + else: + # Should not happen, better to raise, probably we had to remove + # the dest folder before + raise ConanException("Error, the file to download already exists: '%s'" % file_path) t1 = time.time() ret = bytearray() - response = call_with_retry(self.output, retry, retry_wait, self._download_file, url, auth) + response = call_with_retry(self.output, retry, retry_wait, self._download_file, url, auth, headers) if not response.ok: # Do not retry if not found or whatever controlled error raise ConanException("Error %d downloading file %s" % (response.status_code, url)) @@ -137,23 +146,38 @@ def download(self, url, file_path=None, auth=None, retry=1, retry_wait=0): print_progress(self.output, 50, progress) save(file_path, response.content, append=True) else: - dl = 0 total_length = int(total_length) - last_progress = None - chunk_size = 1024 if not file_path else 1024 * 100 - for data in response.iter_content(chunk_size=chunk_size): - dl += len(data) - if not file_path: - ret.extend(data) - else: - save(file_path, data, append=True) - - units = progress_units(dl, total_length) - progress = human_readable_progress(dl, total_length) - if last_progress != units: # Avoid screen refresh if nothing has change - if self.output: - print_progress(self.output, units, progress) - last_progress = units + + def download_chunks(file_handler=None, ret_buffer=None): + """Write to a buffer or to a file handler""" + chunk_size = 1024 if not file_path else 1024 * 100 + download_size = 0 + last_progress = None + for data in response.iter_content(chunk_size=chunk_size): + download_size += len(data) + if ret_buffer is not None: + ret_buffer.extend(data) + if file_handler is not None: + file_handler.write(to_file_bytes(data)) + + units = progress_units(download_size, total_length) + progress = human_readable_progress(download_size, total_length) + if last_progress != units: # Avoid screen refresh if nothing has change + if self.output: + print_progress(self.output, units, progress) + last_progress = units + return download_size + + if file_path: + mkdir(os.path.dirname(file_path)) + with open(file_path, 'ab') as handle: + dl_size = download_chunks(file_handler=handle) + else: + dl_size = download_chunks(ret_buffer=ret) + + if dl_size != total_length: + raise ConanException("Transfer interrupted before " + "complete: %s < %s" % (dl_size, total_length)) duration = time.time() - t1 log_download(url, duration) @@ -169,9 +193,10 @@ def download(self, url, file_path=None, auth=None, retry=1, retry_wait=0): raise ConanConnectionError("Download failed, check server, possibly try again\n%s" % str(e)) - def _download_file(self, url, auth): + def _download_file(self, url, auth, headers): try: - response = self.requester.get(url, stream=True, verify=self.verify, auth=auth) + response = self.requester.get(url, stream=True, verify=self.verify, auth=auth, + headers=headers) except Exception as exc: raise ConanException("Error downloading file %s: '%s'" % (url, exception_message_safe(exc))) diff --git a/conans/client/rest/version_checker.py b/conans/client/rest/version_checker.py index 1706902db66..c8346c9a294 100644 --- a/conans/client/rest/version_checker.py +++ b/conans/client/rest/version_checker.py @@ -69,8 +69,3 @@ def _handle_ret(self, ret): "Please, contact with your system administrator" \ " and upgrade the server." % server_version raise ConanOutdatedClient(msg) - else: - msg = "The conan remote version is outdated (v%s). " \ - "Please, contact with your system administrator and upgrade the " \ - "remote to avoid deprecation." % server_version - logger.debug(msg) diff --git a/conans/client/source.py b/conans/client/source.py index b8ef83a2241..065e78a352c 100644 --- a/conans/client/source.py +++ b/conans/client/source.py @@ -69,9 +69,12 @@ def remove_source(raise_error=True): set_dirty(src_folder) os.chdir(src_folder) + conan_file.source_folder = src_folder try: with tools.environment_append(conan_file.env): with conanfile_exception_formatter(str(conan_file), "source"): + conan_file.build_folder = None + conan_file.package_folder = None conan_file.source() clean_dirty(src_folder) # Everything went well, remove DIRTY flag except Exception as e: @@ -85,12 +88,16 @@ def remove_source(raise_error=True): raise ConanException(e) -def config_source_local(current_path, conan_file, output): - output.info('Configuring sources in %s' % current_path) - with tools.chdir(current_path): +def config_source_local(dest_dir, conan_file, output): + output.info('Configuring sources in %s' % dest_dir) + conan_file.source_folder = dest_dir + + with tools.chdir(dest_dir): try: with conanfile_exception_formatter(str(conan_file), "source"): with tools.environment_append(conan_file.env): + conan_file.build_folder = None + conan_file.package_folder = None conan_file.source() except ConanExceptionInUserConanfileMethod: raise diff --git a/conans/client/tools/files.py b/conans/client/tools/files.py index 9601b899f0b..7142868bb61 100644 --- a/conans/client/tools/files.py +++ b/conans/client/tools/files.py @@ -1,17 +1,15 @@ import platform -from contextlib import contextmanager - import logging - import re - import os import sys +from contextlib import contextmanager +from patch import fromfile, fromstring + from conans.client.output import ConanOutput from conans.errors import ConanException from conans.util.files import load, save, _generic_algorithm_sum -from patch import fromfile, fromstring _global_output = None @@ -70,8 +68,10 @@ def unzip(filename, destination=".", keep_permissions=False): if hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): def print_progress(the_size, uncomp_size): the_size = (the_size * 100.0 / uncomp_size) if uncomp_size != 0 else 0 - txt_msg = "Unzipping %.0f %%" % the_size - _global_output.rewrite_line(txt_msg) + if the_size > print_progress.last_size + 1: + txt_msg = "Unzipping %d %%" % the_size + _global_output.rewrite_line(txt_msg) + print_progress.last_size = the_size else: def print_progress(_, __): pass @@ -83,14 +83,13 @@ def print_progress(_, __): else: _global_output.info("Unzipping %s" % human_size(uncompress_size)) extracted_size = 0 + + print_progress.last_size = -1 if platform.system() == "Windows": for file_ in z.infolist(): extracted_size += file_.file_size print_progress(extracted_size, uncompress_size) try: - # Win path limit is 260 chars - if len(file_.filename) + len(full_path) >= 260: - raise ValueError("Filename too long") z.extract(file_, full_path) except Exception as e: _global_output.error("Error extract %s\n%s" % (file_.filename, str(e))) @@ -118,9 +117,11 @@ def untargz(filename, destination="."): def check_with_algorithm_sum(algorithm_name, file_path, signature): real_signature = _generic_algorithm_sum(file_path, algorithm_name) if real_signature != signature: - raise ConanException("%s signature failed for '%s' file." + raise ConanException("%s signature failed for '%s' file. \n" + " Provided signature: %s \n" " Computed signature: %s" % (algorithm_name, os.path.basename(file_path), + signature, real_signature)) @@ -168,6 +169,29 @@ def emit(self, record): if not patchset: raise ConanException("Failed to parse patch: %s" % (patch_file if patch_file else "string")) + # account for new and deleted files, upstream dep won't fix them + items = [] + for p in patchset: + source = p.source.decode("utf-8") + if source.startswith("a/"): + source = source[2:] + target = p.target.decode("utf-8") + if target.startswith("b/"): + target = target[2:] + if "dev/null" in source: + if base_path: + target = os.path.join(base_path, target) + hunks = [s.decode("utf-8") for s in p.hunks[0].text] + new_file = "".join(hunk[1:] for hunk in hunks) + save(target, new_file) + elif "dev/null" in target: + if base_path: + source = os.path.join(base_path, source) + os.unlink(source) + else: + items.append(p) + patchset.items = items + if not patchset.apply(root=base_path, strip=strip): raise ConanException("Failed to apply patch: %s" % patch_file) @@ -199,7 +223,7 @@ def replace_prefix_in_pc_file(pc_file, new_prefix): def unix_path(path): """"Used to translate windows paths to MSYS unix paths like - c/users/path/to/file""" + c/users/path/to/file. Not working in a regular console or MinGW!""" pattern = re.compile(r'([a-z]):\\', re.IGNORECASE) return pattern.sub('/\\1/', path).replace('\\', '/').lower() @@ -209,7 +233,7 @@ def collect_libs(conanfile, folder="lib"): return [] lib_folder = os.path.join(conanfile.package_folder, folder) if not os.path.exists(lib_folder): - conanfile.output.warn("Lib folder doesn't exist, can't collect libraries") + conanfile.output.warn("Lib folder doesn't exist, can't collect libraries: {0}".format(lib_folder)) return [] files = os.listdir(lib_folder) result = [] @@ -219,4 +243,13 @@ def collect_libs(conanfile, folder="lib"): if ext != ".lib" and name.startswith("lib"): name = name[3:] result.append(name) - return result \ No newline at end of file + return result + + +def which(filename): + """ same affect as posix which command or shutil.which from python3 """ + for path in os.environ["PATH"].split(os.pathsep): + fullname = os.path.join(path, filename) + if os.path.exists(fullname) and os.access(fullname, os.X_OK): + return os.path.join(path, filename) + return None diff --git a/conans/client/tools/net.py b/conans/client/tools/net.py index 32dd3416a33..682f18daab1 100644 --- a/conans/client/tools/net.py +++ b/conans/client/tools/net.py @@ -3,17 +3,25 @@ import os from conans.client.output import ConanOutput from conans.client.rest.uploader_downloader import Downloader -from conans.client.tools.files import unzip +from conans.client.tools.files import unzip, check_md5, check_sha1, check_sha256 from conans.errors import ConanException _global_requester = None -def get(url): - """ high level downloader + unziper + delete temporary zip +def get(url, md5='', sha1='', sha256=''): + """ high level downloader + unzipper + (optional hash checker) + delete temporary zip """ filename = os.path.basename(url) download(url, filename) + + if md5: + check_md5(filename, md5) + if sha1: + check_sha1(filename, sha1) + if sha256: + check_sha256(filename, sha256) + unzip(filename) os.unlink(filename) @@ -37,12 +45,14 @@ def ftp_download(ip, filename, login='', password=''): pass -def download(url, filename, verify=True, out=None, retry=2, retry_wait=5): +def download(url, filename, verify=True, out=None, retry=2, retry_wait=5, overwrite=False, + auth=None, headers=None): out = out or ConanOutput(sys.stdout, True) if verify: # We check the certificate using a list of known verifiers import conans.client.rest.cacert as cacert verify = cacert.file_path downloader = Downloader(_global_requester, out, verify=verify) - downloader.download(url, filename, retry=retry, retry_wait=retry_wait) + downloader.download(url, filename, retry=retry, retry_wait=retry_wait, overwrite=overwrite, + auth=auth, headers=headers) out.writeln("") diff --git a/conans/client/tools/oss.py b/conans/client/tools/oss.py index 84a77c965d4..4be610b7506 100644 --- a/conans/client/tools/oss.py +++ b/conans/client/tools/oss.py @@ -100,6 +100,10 @@ def with_yum(self): ("centos", "redhat", "fedora", "pidora", "scientific", "xenserver", "amazon", "oracle", "rhel") + @property + def with_pacman(self): + return self.is_linux and self.linux_distro == "arch" + @staticmethod def get_win_os_version(): """ @@ -216,8 +220,6 @@ def cross_building(settings, self_os=None, self_arch=None): os_setting = settings.get_safe("os") arch_setting = settings.get_safe("arch") platform_os = {"Darwin": "Macos"}.get(self_os, self_os) - if self_os == os_setting and self_arch == "x86_64" and arch_setting == "x86": - return False # not really considered cross if os_setting and platform_os != os_setting: return True diff --git a/conans/client/tools/system_pm.py b/conans/client/tools/system_pm.py index 70acc43ca69..c8b5d163787 100644 --- a/conans/client/tools/system_pm.py +++ b/conans/client/tools/system_pm.py @@ -11,6 +11,8 @@ class SystemPackageTool(object): def __init__(self, runner=None, os_info=None, tool=None): env_sudo = os.environ.get("CONAN_SYSREQUIRES_SUDO", None) self._sudo = (env_sudo != "False" and env_sudo != "0") + if env_sudo is None and os.name == 'posix' and os.geteuid() == 0: + self._sudo = False os_info = os_info or OSInfo() self._is_up_to_date = False self._tool = tool or self._create_tool(os_info) @@ -23,6 +25,8 @@ def _create_tool(os_info): return AptTool() elif os_info.with_yum: return YumTool() + elif os_info.with_pacman: + return PacManTool() elif os_info.is_macos: return BrewTool() elif os_info.is_freebsd: @@ -73,7 +77,7 @@ def update(self): pass def install(self, package_name): - _global_output.warn("Only available for linux with apt-get or yum or OSx with brew or " + _global_output.warn("Only available for linux with apt-get, yum, or pacman or OSx with brew or " "FreeBSD with pkg or Solaris with pkgutil") def installed(self, package_name): @@ -151,6 +155,17 @@ def installed(self, package_name): exit_code = self._runner('choco search --local-only --exact %s | findstr /c:"1 packages installed."' % package_name, None) return exit_code == 0 +class PacManTool(object): + def update(self): + _run(self._runner, "%spacman -Syyu --noconfirm" % self._sudo_str) + + def install(self, package_name): + _run(self._runner, "%spacman -S --noconfirm %s" % (self._sudo_str, package_name)) + + def installed(self, package_name): + exit_code = self._runner("pacman -Qi %s" % package_name, None) + return exit_code == 0 + def _run(runner, command, accepted_returns=None): accepted_returns = accepted_returns or [0, ] diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py index f15c5182435..dea948d02a6 100644 --- a/conans/client/tools/win.py +++ b/conans/client/tools/win.py @@ -5,23 +5,24 @@ from conans.client.tools.env import environment_append from conans.client.tools.files import unix_path +from conans.client.tools.oss import cpu_count from conans.errors import ConanException _global_output = None def msvc_build_command(settings, sln_path, targets=None, upgrade_project=True, build_type=None, - arch=None): + arch=None, parallel=True): """ Do both: set the environment variables and call the .sln build """ vcvars = vcvars_command(settings) - build = build_sln_command(settings, sln_path, targets, upgrade_project, build_type, arch) + build = build_sln_command(settings, sln_path, targets, upgrade_project, build_type, arch, parallel) command = "%s && %s" % (vcvars, build) return command def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, build_type=None, - arch=None): + arch=None, parallel=True): """ Use example: build_command = build_sln_command(self.settings, "myfile.sln", targets=["SDL2_image"]) @@ -44,6 +45,9 @@ def build_sln_command(settings, sln_path, targets=None, upgrade_project=True, bu elif "ARM" in arch.upper(): command += ' /p:Platform="ARM"' + if parallel: + command += ' /m:%s' % cpu_count() + if targets: command += " /target:%s" % ";".join(targets) return command @@ -75,9 +79,9 @@ def vs_installation_path(version): return vs_installation_path._cached[version] -def vcvars_command(settings): - arch_setting = settings.get_safe("arch") - compiler_version = settings.get_safe("compiler.version") +def vcvars_command(settings, arch=None, compiler_version=None): + arch_setting = arch or settings.get_safe("arch") + compiler_version = compiler_version or settings.get_safe("compiler.version") if not compiler_version: raise ConanException("compiler.version setting required for vcvars not defined") @@ -102,6 +106,9 @@ def vcvars_command(settings): else: raise ConanException("VS2017 '%s' variable not defined, " "and vswhere didn't find it" % env_var) + if not os.path.isdir(vs_path): + _global_output.warn('VS variable %s points to the non-existing path "%s",' + 'please check that you have set it correctly' % (env_var, vs_path)) vcvars_path = os.path.join(vs_path, "../../VC/Auxiliary/Build/vcvarsall.bat") command = ('set "VSCMD_START_DIR=%%CD%%" && ' 'call "%s" %s' % (vcvars_path, param)) @@ -110,6 +117,9 @@ def vcvars_command(settings): vs_path = os.environ[env_var] except KeyError: raise ConanException("VS '%s' variable not defined. Please install VS" % env_var) + if not os.path.isdir(vs_path): + _global_output.warn('VS variable %s points to the non-existing path "%s",' + 'please check that you have set it correctly' % (env_var, vs_path)) vcvars_path = os.path.join(vs_path, "../../VC/vcvarsall.bat") command = ('call "%s" %s' % (vcvars_path, param)) diff --git a/conans/client/uploader.py b/conans/client/uploader.py index 87fdc8231bd..e9f54c4b5d8 100644 --- a/conans/client/uploader.py +++ b/conans/client/uploader.py @@ -2,11 +2,20 @@ import time from conans.errors import ConanException, NotFoundException -from conans.model.ref import PackageReference, is_a_reference, ConanFileReference +from conans.model.ref import PackageReference, ConanFileReference from conans.util.log import logger from conans.client.loader_parse import load_conanfile_class +def is_a_reference(ref): + try: + ConanFileReference.loads(ref) + return "*" not in ref # If is a pattern, it is not a reference + except ConanException: + pass + return False + + class ConanUploader(object): def __init__(self, paths, user_io, remote_proxy, search_manager): diff --git a/conans/model/build_info.py b/conans/model/build_info.py index fed762aa936..1ecc1e7d4c8 100644 --- a/conans/model/build_info.py +++ b/conans/model/build_info.py @@ -32,6 +32,8 @@ def __init__(self): self._bin_paths = None self._build_paths = None self._res_paths = None + self.version = None # Version of the conan package + self.description = None # Description of the conan package def _filter_paths(self, paths): abs_paths = [os.path.join(self.rootpath, p) diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 2a4db5fcdd7..3eab88112ee 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -1,3 +1,5 @@ +import copy + from conans.model.options import Options, PackageOptions, OptionsValues from conans.model.requires import Requirements from conans.model.build_info import DepsCppInfo @@ -6,7 +8,7 @@ from conans.model.env_info import DepsEnvInfo, EnvValues import os -from conans.model.user_info import UserDepsInfo +from conans.model.user_info import DepsUserInfo from conans.paths import RUN_LOG_NAME @@ -112,7 +114,8 @@ def __init__(self, output, runner, settings, conanfile_directory, user=None, cha # user declared variables self.user_info = None - self.deps_user_info = UserDepsInfo() # Keys are the package names, and the values a dict with the vars + # Keys are the package names, and the values a dict with the vars + self.deps_user_info = DepsUserInfo() self.copy = None # initialized at runtime @@ -121,8 +124,7 @@ def __init__(self, output, runner, settings, conanfile_directory, user=None, cha # something that can run commands, as os.sytem self._runner = runner - self._conanfile_directory = conanfile_directory - self.package_folder = None # Assigned at runtime + self.conanfile_directory = conanfile_directory self._scope = None # user specified env variables @@ -133,11 +135,21 @@ def __init__(self, output, runner, settings, conanfile_directory, user=None, cha # Are we in local cache? Suggest a better name self.in_local_cache = False + # Init a description + self.description = None + @property def env(self): - simple, multiple = self._env_values.env_dicts(self.name) - simple.update(multiple) - return simple + """Apply the self.deps_env_info into a copy of self._env_values (will prioritize the + self._env_values, user specified from profiles or -e first, then inherited)""" + # Cannot be lazy cached, because it's called in configure node, and we still don't have + # the deps_env_info objects available + tmp_env_values = self._env_values.copy() + tmp_env_values.update(self.deps_env_info) + + ret, multiple = tmp_env_values.env_dicts(self.name) + ret.update(multiple) + return ret @property def channel(self): @@ -170,10 +182,6 @@ def scope(self): def scope(self, value): self._scope = value - @property - def conanfile_directory(self): - return self._conanfile_directory - @property def build_policy_missing(self): return self.build_policy == "missing" diff --git a/conans/model/env_info.py b/conans/model/env_info.py index 5530db4c92b..b9ef8fd1b58 100644 --- a/conans/model/env_info.py +++ b/conans/model/env_info.py @@ -1,5 +1,5 @@ import copy -import os +import re from collections import OrderedDict, defaultdict from conans.errors import ConanException @@ -24,6 +24,11 @@ class EnvValues(object): def __init__(self): self._data = defaultdict(dict) + def copy(self): + ret = EnvValues() + ret._data = copy.deepcopy(self._data) + return ret + @staticmethod def load_value(the_value): if the_value.startswith("[") and the_value.endswith("]"): @@ -185,8 +190,6 @@ def __getattr__(self, name): attr = self._values_.get(name) if not attr: self._values_[name] = [] - elif not isinstance(attr, list): - self._values_[name] = [attr] return self._values_[name] def __setattr__(self, name, value): @@ -206,28 +209,6 @@ def __init__(self): super(DepsEnvInfo, self).__init__() self._dependencies_ = OrderedDict() - def dumps(self): - result = [] - - for var, values in sorted(self.vars.items()): - result.append("[%s]" % var) - if isinstance(values, list): - result.extend(values) - else: - result.append(values) - result.append("") - - for name, env_info in sorted(self._dependencies_.items()): - for var, values in sorted(env_info.vars.items()): - result.append("[%s:%s]" % (name, var)) - if isinstance(values, list): - result.extend(values) - else: - result.append(values) - result.append("") - - return os.linesep.join(result) - @property def dependencies(self): return self._dependencies_.items() @@ -245,7 +226,7 @@ def update(self, dep_env_info, pkg_name): def merge_lists(seq1, seq2): return [s for s in seq1 if s not in seq2] + seq2 - # With vars if its setted the keep the setted value + # With vars if its set the keep the set value for varname, value in dep_env_info.vars.items(): if varname not in self.vars: self.vars[varname] = value @@ -262,4 +243,42 @@ def update_deps_env_info(self, dep_env_info): for pkg_name, env_info in dep_env_info.dependencies: self.update(env_info, pkg_name) + @staticmethod + def loads(text): + ret = DepsEnvInfo() + lib_name = None + env_info = None + for line in text.splitlines(): + if not lib_name and not line.startswith("[ENV_"): + raise ConanException("Error, invalid file format reading env info variables") + elif line.startswith("[ENV_"): + if env_info: + ret.update(env_info, lib_name) + lib_name = line[5:-1] + env_info = EnvInfo() + else: + var_name, value = line.split("=", 1) + if value[0] == "[" and value[-1] == "]": + # Take all the items between quotes + values = re.findall('"([^"]*)"', value[1:-1]) + for val in values: + getattr(env_info, var_name).append(val) + else: + setattr(env_info, var_name, value) # peel quotes + if env_info: + ret.update(env_info, lib_name) + return ret + + def dumps(self): + sections = [] + for name, env_info in self._dependencies_.items(): + sections.append("[ENV_%s]" % name) + for var, values in sorted(env_info.vars.items()): + tmp = "%s=" % var + if isinstance(values, list): + tmp += "[%s]" % ",".join(['"%s"' % val for val in values]) + else: + tmp += '%s' % values + sections.append(tmp) + return "\n".join(sections) diff --git a/conans/model/options.py b/conans/model/options.py index b3b7843f20e..48001896070 100644 --- a/conans/model/options.py +++ b/conans/model/options.py @@ -2,6 +2,7 @@ from conans.errors import ConanException import yaml import six +import fnmatch _falsey_options = ["false", "none", "0", "off", ""] @@ -413,7 +414,11 @@ def values(self, vals): self._check_field(name) self._data[name].value = value - def propagate_upstream(self, package_values, down_ref, own_ref, output): + def propagate_upstream(self, package_values, down_ref, own_ref, output, ignore_unknown=False): + """ ignore_unknown: do not raise Exception if the given option doesn't exist in this package. + Useful for pattern defined options like "-o *:shared=True", for packages + not defining the "shared" options, they will not fail + """ if not package_values: return @@ -429,9 +434,14 @@ def propagate_upstream(self, package_values, down_ref, own_ref, output): "but it was already assigned to %s by %s" % (down_ref, own_ref, name, value, modified_value, modified_ref)) else: - self._modified[name] = (value, down_ref) - self._check_field(name) - self._data[name].value = value + if ignore_unknown: + if name in self._data: + self._data[name].value = value + self._modified[name] = (value, down_ref) + else: + self._check_field(name) + self._data[name].value = value + self._modified[name] = (value, down_ref) class Options(object): @@ -495,7 +505,13 @@ def propagate_upstream(self, down_package_values, down_ref, own_ref, output): assert isinstance(down_package_values, dict) option_values = down_package_values.get(own_ref.name) - self._package_options.propagate_upstream(option_values, down_ref, own_ref, output) + self._package_options.propagate_upstream(option_values, down_ref, own_ref, output, ignore_unknown=False) + if not option_values: + for package_pattern, package_option_values in down_package_values.items(): + if own_ref.name != package_pattern and fnmatch.fnmatch(own_ref.name, package_pattern): + self._package_options.propagate_upstream(package_option_values, down_ref, own_ref, output, + ignore_unknown=True) + for name, option_values in sorted(list(down_package_values.items())): if name != own_ref.name: pkg_values = self._deps_package_values.setdefault(name, PackageOptionValues()) diff --git a/conans/model/ref.py b/conans/model/ref.py index 194d82cdba2..eb9f4523c8c 100644 --- a/conans/model/ref.py +++ b/conans/model/ref.py @@ -95,12 +95,3 @@ def loads(text): def __repr__(self): return "%s:%s" % (self.conan, self.package_id) - - -def is_a_reference(ref): - try: - ConanFileReference.loads(ref) - return True - except: - pass - return False diff --git a/conans/model/settings.py b/conans/model/settings.py index 38f83fe7b82..152de375093 100644 --- a/conans/model/settings.py +++ b/conans/model/settings.py @@ -54,6 +54,18 @@ def copy(self): result._definition = {k: v.copy() for k, v in self._definition.items()} return result + def copy_values(self): + if self._value is None and "None" not in self._definition: + return None + + result = SettingsItem({}, name=self._name) + result._value = self._value + if self.is_final: + result._definition = self._definition[:] + else: + result._definition = {k: v.copy_values() for k, v in self._definition.items()} + return result + @property def is_final(self): return not isinstance(self._definition, dict) @@ -194,6 +206,16 @@ def copy(self): result._data[k] = v.copy() return result + def copy_values(self): + """ deepcopy, recursive + """ + result = Settings({}, name=self._name, parent_value=self._parent_value) + for k, v in self._data.items(): + value = v.copy_values() + if value is not None: + result._data[k] = value + return result + @staticmethod def loads(text): return Settings(yaml.load(text) or {}) diff --git a/conans/model/user_info.py b/conans/model/user_info.py index 3348c520336..390be9ac4f8 100644 --- a/conans/model/user_info.py +++ b/conans/model/user_info.py @@ -26,6 +26,6 @@ def vars(self): return self._values_ -class UserDepsInfo(defaultdict): +class DepsUserInfo(defaultdict): def __init__(self): - super(UserDepsInfo, self).__init__(UserInfo) + super(DepsUserInfo, self).__init__(UserInfo) diff --git a/conans/paths.py b/conans/paths.py index eb028b3c8fc..bc8921fa70f 100644 --- a/conans/paths.py +++ b/conans/paths.py @@ -4,15 +4,43 @@ import platform from conans.errors import ConanException from conans.util.files import rmdir - -if platform.system() == "Windows": - from conans.util.windows import path_shortener, rm_conandir, conan_expand_user +import sys + + +def _check_long_paths_support(): + if platform.system() != "Windows": + return True + if sys.version_info < (3, 6): + return False + from six.moves import winreg + try: + hKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, + r"SYSTEM\CurrentControlSet\Control\FileSystem") + result = winreg.QueryValueEx(hKey, "LongPathsEnabled") + key_value = result[0] + return key_value == 1 + except EnvironmentError: + return False + finally: + winreg.CloseKey(hKey) + return False + + +long_paths_support = _check_long_paths_support() + +if not long_paths_support: + from conans.util.windows import path_shortener, rm_conandir else: def path_shortener(x, _): return x conan_expand_user = os.path.expanduser rm_conandir = rmdir +if platform.system() == "Windows": + from conans.util.windows import conan_expand_user +else: + conan_expand_user = os.path.expanduser + EXPORT_FOLDER = "export" EXPORT_SRC_FOLDER = "export_source" diff --git a/conans/server/rest/controllers/users_controller.py b/conans/server/rest/controllers/users_controller.py index 44539dd897f..653fc53859b 100644 --- a/conans/server/rest/controllers/users_controller.py +++ b/conans/server/rest/controllers/users_controller.py @@ -1,6 +1,7 @@ from conans.server.rest.controllers.controller import Controller -from conans.errors import AuthenticationException, ForbiddenException +from conans.errors import AuthenticationException from conans.server.service.user_service import UserService +from bottle import response class UsersController(Controller): @@ -20,6 +21,7 @@ def authenticate(http_basic_credentials): token = user_service.authenticate(http_basic_credentials.user, http_basic_credentials.password) + response.content_type = 'text/plain' return token @app.route(self.route + '/check_credentials', method=["GET"]) @@ -28,4 +30,5 @@ def check_credentials(auth_user): is raised from Bottle plugin""" if not auth_user: raise AuthenticationException("Logged user needed!") + response.content_type = 'text/plain' return auth_user diff --git a/conans/test/command/build_test.py b/conans/test/command/build_test.py index a7f90a37b10..b4f2ca5793c 100644 --- a/conans/test/command/build_test.py +++ b/conans/test/command/build_test.py @@ -1,10 +1,9 @@ from conans.test.utils.tools import TestClient import unittest -from conans.paths import CONANFILE +from conans.paths import CONANFILE, BUILD_INFO from conans.model.ref import PackageReference import os -from conans.util.files import load - +from conans.util.files import load, mkdir conanfile_scope_env = """ from conans import ConanFile @@ -37,19 +36,19 @@ class ConanBuildTest(unittest.TestCase): def build_error_test(self): """ If not using -g txt generator, and build() requires self.deps_cpp_info, - or self.deps_user_info it will fail + or self.deps_user_info it wont fail because now it's automatic """ client = TestClient() client.save({CONANFILE: conanfile_dep}) client.run("export lasote/testing") client.save({CONANFILE: conanfile_scope_env}, clean_first=True) client.run("install --build=missing") - error = client.run("build", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: PROJECT: Error in build() method, line 9", client.user_io.out) - self.assertIn("self.deps_cpp_info not defined", client.user_io.out) + + client.run("build .") # We do not need to specify -g txt anymore + self.assertTrue(os.path.exists(os.path.join(client.current_folder, BUILD_INFO))) conanfile_user_info = """ +import os from conans import ConanFile class AConan(ConanFile): @@ -57,14 +56,14 @@ class AConan(ConanFile): generators = "cmake" def build(self): - self.deps_user_info.VAR + self.deps_user_info + self.deps_env_info + assert(self.build_folder == os.getcwd()) + assert(hasattr(self, "package_folder")) """ client.save({CONANFILE: conanfile_user_info}, clean_first=True) client.run("install --build=missing") - error = client.run("build", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: PROJECT: Error in build() method, line 9", client.user_io.out) - self.assertIn("self.deps_user_info not defined", client.user_io.out) + client.run("build .") def build_test(self): """ Try to reuse variables loaded from txt generator => deps_cpp_info @@ -74,9 +73,9 @@ def build_test(self): client.run("export lasote/testing") client.save({CONANFILE: conanfile_scope_env}, clean_first=True) - client.run("install --build=missing -g txt") + client.run("install --build=missing") - client.run("build") + client.run("build .") ref = PackageReference.loads("Hello/0.1@lasote/testing:" "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") package_folder = client.paths.package(ref).replace("\\", "/") @@ -85,6 +84,83 @@ def build_test(self): self.assertIn("Project: HELLO INCLUDE PATHS: %s/include" % package_folder, client.user_io.out) + def build_different_folders_test(self): + conanfile = """ +import os +from conans import ConanFile + +class AConan(ConanFile): + generators = "cmake" + + def build(self): + self.output.warn("Build folder=>%s" % self.build_folder) + self.output.warn("Src folder=>%s" % self.source_folder) + self.output.warn("Package folder=>%s" % self.package_folder) + assert(os.path.exists(self.build_folder)) + assert(os.path.exists(self.source_folder)) + # package_folder will be created manually or by the CMake helper when local invocation + assert(not os.path.exists(self.package_folder)) +""" + + client = TestClient() + client.save({CONANFILE: conanfile}) + with client.chdir("build1"): + client.run("install ..") + # Try relative to cwd + client.run("build . --build_folder build2 --install-folder build1 " + "--package_folder build1/pkg") + self.assertIn("Build folder=>%s" % os.path.join(client.current_folder, "build2"), + client.out) + self.assertIn("Package folder=>%s" % os.path.join(client.current_folder, "build1", "pkg"), + client.out) + self.assertIn("Src folder=>%s" % client.current_folder, client.out) + + # Try default package folder + client.run("build . --build_folder build1 --package_folder package1") + self.assertIn("Build folder=>%s" % os.path.join(client.current_folder, "build1"), + client.out) + self.assertIn("Package folder=>%s" % os.path.join(client.current_folder, "package"), + client.out) + self.assertIn("Src folder=>%s" % client.current_folder, client.out) + + # Try absolute package folder + client.run("build . --build-folder build1 --package_folder '%s'" % + os.path.join(client.current_folder, "mypackage")) + self.assertIn("Build folder=>%s" % os.path.join(client.current_folder, "build1"), + client.out) + self.assertIn("Package folder=>%s" % os.path.join(client.current_folder, "mypackage"), + client.out) + self.assertIn("Src folder=>%s" % client.current_folder, client.out) + + # Try absolute build and relative package + conanfile_dir = client.current_folder + bdir = os.path.join(client.current_folder, "other/mybuild") + with client.chdir(bdir): + client.run("install '%s'" % conanfile_dir) + client.run("build . --build_folder '%s' --package_folder relpackage" % bdir) + + self.assertIn("Build folder=>%s" % os.path.join(client.current_folder, "other/mybuild"), + client.out) + self.assertIn("Package folder=>%s" % os.path.join(client.current_folder, "relpackage"), + client.out) + self.assertIn("Src folder=>%s" % client.current_folder, client.out) + + # Try different source + with client.chdir("other/build"): + client.run("install ../..") + error = client.run("build . --source_folder '%s' --build-folder other/build" % + os.path.join(client.current_folder, "mysrc"), ignore_error=True) + self.assertTrue(error) # src is not created automatically, it makes no sense + mkdir(os.path.join(client.current_folder, "mysrc")) + + client.run("build . --source_folder '%s' --build_folder other/build" + % os.path.join(client.current_folder, "mysrc")) + self.assertIn("Build folder=>%s" % os.path.join(client.current_folder, "other", "build"), + client.out) + self.assertIn("Package folder=>%s" % os.path.join(client.current_folder, "other", "build"), + client.out) + self.assertIn("Src folder=>%s" % os.path.join(client.current_folder, "mysrc"), client.out) + def build_dots_names_test(self): """ Try to reuse variables loaded from txt generator => deps_cpp_info """ @@ -109,8 +185,9 @@ def build(self): self.output.info("HELLO ROOT PATH: %s" % self.deps_cpp_info["Hello-Tools"].rootpath) """ client.save({CONANFILE: conanfile_scope_env}, clean_first=True) - client.run("install --build=missing -g txt") - client.run("build") + client.run("install --build=missing") + client.run("build .") + self.assertIn("Hello.Pkg/0.1/lasote/testing", client.out) self.assertIn("Hello-Tools/0.1/lasote/testing", client.out) @@ -136,10 +213,52 @@ def build(self): "CMakeLists.txt": cmake, "header.h": "my header h!!"}) client.run("install") - error = client.run("build", ignore_error=True) - self.assertTrue(error) - self.assertIn("CMAKE_INSTALL_PREFIX not defined for 'cmake.install()'", - client.user_io.out) - client.run("build -pf=mypkg") - header = load(os.path.join(client.current_folder, "mypkg/include/header.h")) + client.run("build .") # Won't fail, by default the package_folder is build_folder/package + header = load(os.path.join(client.current_folder, "package/include/header.h")) self.assertEqual(header, "my header h!!") + + client.save({CONANFILE: conanfile, + "CMakeLists.txt": cmake, + "header.h": "my header3 h!!"}, clean_first=True) + client.run("install .") + client.run("build -pf=mypkg .") + header = load(os.path.join(client.current_folder, "mypkg/include/header.h")) + self.assertEqual(header, "my header3 h!!") + + client.save({CONANFILE: conanfile, + "CMakeLists.txt": cmake, + "header.h": "my header2 h!!"}, clean_first=True) + with client.chdir("build"): + client.run("install ..") + client.run("build . -pf=mypkg -bf=build") + header = load(os.path.join(client.current_folder, "mypkg/include/header.h")) + self.assertEqual(header, "my header2 h!!") + + def build_with_deps_env_info_test(self): + client = TestClient() + conanfile = """ +from conans import ConanFile, CMake + +class AConan(ConanFile): + name = "lib" + version = "1.0" + + def package_info(self): + self.env_info.MYVAR = "23" + +""" + client.save({CONANFILE: conanfile}) + client.run("export lasote/stable") + + conanfile = """ +from conans import ConanFile + +class AConan(ConanFile): + requires = "lib/1.0@lasote/stable" + + def build(self): + assert(self.deps_env_info["lib"].MYVAR == "23") +""" + client.save({CONANFILE: conanfile}, clean_first=True) + client.run("install . --build missing") + client.run("build .") diff --git a/conans/test/command/conan_get_test.py b/conans/test/command/conan_get_test.py index a5c74c5d6ba..41899302d92 100644 --- a/conans/test/command/conan_get_test.py +++ b/conans/test/command/conan_get_test.py @@ -75,7 +75,6 @@ def test_get_local(self): [recipe_hash] dcb3ad84f4e46fb3da40dbbea0017094 -[env] """, self.client.user_io.out) # List package dir diff --git a/conans/test/command/create_test.py b/conans/test/command/create_test.py index af041a01fd2..6d5a0d8b0d8 100644 --- a/conans/test/command/create_test.py +++ b/conans/test/command/create_test.py @@ -4,13 +4,45 @@ class CreateTest(unittest.TestCase): + def transitive_same_name_test(self): + # https://github.com/conan-io/conan/issues/1366 + client = TestClient() + conanfile = ''' +from conans import ConanFile + +class HelloConan(ConanFile): + name = "HelloBar" + version = "0.1" +''' + test_package = ''' +from conans import ConanFile + +class HelloTestConan(ConanFile): + requires = "HelloBar/0.1@lasote/testing" + def test(self): + pass +''' + client.save({"conanfile.py": conanfile, "test_package/conanfile.py": test_package}) + client.run("create lasote/testing") + self.assertIn("HelloBar/0.1@lasote/testing: WARN: Forced build from source", + client.user_io.out) + client.save({"conanfile.py": conanfile.replace("HelloBar", "Hello") + + " requires='HelloBar/0.1@lasote/testing'", + "test_package/conanfile.py": test_package.replace("HelloBar", "Hello")}) + client.run("create lasote/stable") + self.assertNotIn("HelloBar/0.1@lasote/testing: WARN: Forced build from source", + client.user_io.out) + def create_test(self): client = TestClient() - client.save({"conanfile.py": """from conans import ConanFile + client.save({"conanfile.py": """ +import os +from conans import ConanFile class MyPkg(ConanFile): def source(self): assert(self.version=="0.1") assert(self.name=="Pkg") + def configure(self): assert(self.version=="0.1") assert(self.name=="Pkg") @@ -42,6 +74,33 @@ def package_info(self): self.assertIn("Invalid parameter 'lasote', specify the full reference or user/channel", client.out) + def create_werror_test(self): + client = TestClient() + client.save({"conanfile.py": """from conans import ConanFile +class Pkg(ConanFile): + pass + """}) + client.run("export LibA/0.1@user/channel") + client.run("export LibA/0.2@user/channel") + client.save({"conanfile.py": """from conans import ConanFile +class Pkg(ConanFile): + requires = "LibA/0.1@user/channel" + """}) + client.run("export LibB/0.1@user/channel") + client.save({"conanfile.py": """from conans import ConanFile +class Pkg(ConanFile): + requires = "LibA/0.2@user/channel" + """}) + client.run("export LibC/0.1@user/channel") + client.save({"conanfile.py": """from conans import ConanFile +class Pkg(ConanFile): + requires = "LibB/0.1@user/channel", "LibC/0.1@user/channel" + """}) + error = client.run("create Consumer/0.1@lasote/testing --werror", ignore_error=True) + self.assertTrue(error) + self.assertIn("ERROR: Conflict in LibC/0.1@user/channel", + client.out) + def test_error_create_name_version(self): client = TestClient() conanfile = """ @@ -76,6 +135,31 @@ class MyPkg(ConanFile): self.assertIn("Invalid parameter 'lasote', specify the full reference or user/channel", client.out) + def create_in_subfolder_test(self): + client = TestClient() + client.save({"subfolder/conanfile.py": """from conans import ConanFile +class MyPkg(ConanFile): + name = "Pkg" + version = "0.1" +"""}) + client.run("create lasote/channel --cwd subfolder") + self.assertIn("Pkg/0.1@lasote/channel: Generating the package", client.out) + client.run("search") + self.assertIn("Pkg/0.1@lasote/channel", client.out) + + def create_in_subfolder_with_different_name_test(self): + # Now with a different name + client = TestClient() + client.save({"subfolder/CustomConanFile.py": """from conans import ConanFile +class MyPkg(ConanFile): + name = "Pkg" + version = "0.1" +"""}) + client.run("create lasote/channel --cwd subfolder --file CustomConanFile.py") + self.assertIn("Pkg/0.1@lasote/channel: Generating the package", client.out) + client.run("search") + self.assertIn("Pkg/0.1@lasote/channel", client.out) + def create_test_package_test(self): client = TestClient() client.save({"conanfile.py": """from conans import ConanFile diff --git a/conans/test/command/package_files_test.py b/conans/test/command/export_pkg_test.py similarity index 52% rename from conans/test/command/package_files_test.py rename to conans/test/command/export_pkg_test.py index 273bd1ab23c..cf26a19b1d7 100644 --- a/conans/test/command/package_files_test.py +++ b/conans/test/command/export_pkg_test.py @@ -1,12 +1,13 @@ import unittest -from conans.paths import CONANFILE + +from conans.client import tools +from conans.paths import CONANFILE, long_paths_support from conans.test.utils.tools import TestClient from conans.model.ref import ConanFileReference, PackageReference from conans.util.files import load import os from conans.test.utils.conanfile import TestConanFile from nose_parameterized import parameterized -import platform class PackageFilesTest(unittest.TestCase): @@ -20,18 +21,20 @@ class TestConan(ConanFile): name = "Hello" version = "0.1" settings = "os" + + def package(self): + self.copy("*") """ if short_paths: conanfile += " short_paths = True" client.save({CONANFILE: conanfile}) client.run("export lasote/stable") - - client.save({"include/header.h": "//Windows header"}, clean_first=True) - client.run("package_files Hello/0.1@lasote/stable -s os=Windows") + client.save({"include/header.h": "//Windows header"}) + client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows") conan_ref = ConanFileReference.loads("Hello/0.1@lasote/stable") win_package_ref = PackageReference(conan_ref, "3475bd55b91ae904ac96fde0f106a136ab951a5e") package_folder = client.client_cache.package(win_package_ref, short_paths=short_paths) - if short_paths and platform.system() == "Windows": + if short_paths and not long_paths_support: self.assertEqual(load(os.path.join(client.client_cache.package(win_package_ref), ".conan_link")), package_folder) @@ -44,15 +47,52 @@ class TestConan(ConanFile): client.user_io.out) # Now repeat - client.save({"include/header.h": "//Windows header2"}, clean_first=True) - err = client.run("package_files Hello/0.1@lasote/stable -s os=Windows", ignore_error=True) + client.save({CONANFILE: conanfile}, clean_first=True) + client.save({"include/header.h": "//Windows header2"}) + err = client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows", + ignore_error=True) + self.assertIn("Package already exists. Please use --force, -f to overwrite it", + client.user_io.out) + self.assertTrue(err) + + # Will fail because it finds the info files in the curdir. + client.run("install . -s os=Windows") + err = client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows -f", ignore_error=True) + self.assertTrue(err) + self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.out) + + client.save({CONANFILE: conanfile, "include/header.h": "//Windows header2"}, + clean_first=True) + client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows -f") + self.assertEqual(load(os.path.join(package_folder, "include/header.h")), + "//Windows header2") + + # Now use --install-folder and avoid the -s os=Windows, should fail without -f + client.run("install . --install-folder=inst -s os=Windows") + err = client.run("export-pkg . Hello/0.1@lasote/stable -if inst", ignore_error=True) self.assertTrue(err) self.assertIn("Package already exists. Please use --force, -f to overwrite it", client.user_io.out) - client.run("package_files Hello/0.1@lasote/stable -s os=Windows -f") + client.run("export-pkg . Hello/0.1@lasote/stable -if inst -f") self.assertEqual(load(os.path.join(package_folder, "include/header.h")), "//Windows header2") + # Try to specify a setting and the install folder + error = client.run("export-pkg . Hello/0.1@lasote/stable -if inst -s os=Linux", ignore_error=True) + self.assertTrue(error) + self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.user_io.out) + + error = client.run("export-pkg . Hello/0.1@lasote/stable -if inst -f --profile=default", + ignore_error=True) + self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.user_io.out) + self.assertTrue(error) + + # Try to specify a install folder with no files + error = client.run("export-pkg . Hello/0.1@lasote/stable -if fake", ignore_error=True) + self.assertTrue(error) + self.assertIn("The specified --install-folder doesn't contain 'conaninfo.txt' and " + "'conanbuildinfo.txt' files", client.user_io.out) + def _consume(self, client, install_args): consumer = """ from conans import ConanFile @@ -67,11 +107,16 @@ class TestConan(ConanFile): def test_new(self): client = TestClient() client.run("new Hello/0.1 --bare") - client.run("export lasote/stable") - client.save({"lib/libmycoollib.a": ""}, clean_first=True) + client.save({"lib/libmycoollib.a": ""}) settings = ('-s os=Windows -s compiler=gcc -s compiler.version=4.9 ' '-s compiler.libcxx=libstdc++ -s build_type=Release -s arch=x86') - client.run("package_files Hello/0.1@lasote/stable %s" % settings) + 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!", + client.out) # --bare include a now mandatory package() method! + + self.assertIn("Copied 1 '.a' files: libmycoollib.a", client.out) self._consume(client, settings + " -g cmake") cmakeinfo = load(os.path.join(client.current_folder, "conanbuildinfo.cmake")) @@ -95,15 +140,13 @@ def package(self): self.copy("*.h", src="include", dst="inc") self.copy("*.lib", src="lib", dst="lib") """ - client.save({CONANFILE: conanfile}) - client.run("export lasote/stable") - - client.save({"include/header.h": "//Windows header", + client.save({CONANFILE: conanfile, + "include/header.h": "//Windows header", "include/header.txt": "", "libs/what": "", "lib/hello.lib": "My Lib", "lib/bye.txt": ""}, clean_first=True) - client.run("package_files Hello/0.1@lasote/stable -s os=Windows --build_folder=.") + client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows --build-folder=.") conan_ref = ConanFileReference.loads("Hello/0.1@lasote/stable") package_ref = PackageReference(conan_ref, "3475bd55b91ae904ac96fde0f106a136ab951a5e") package_folder = client.client_cache.package(package_ref) @@ -114,6 +157,31 @@ def package(self): self.assertEqual(os.listdir(lib), ["hello.lib"]) self.assertEqual(load(os.path.join(lib, "hello.lib")), "My Lib") + def test_no_source_folder(self): + client = TestClient() + conanfile = """ +from conans import ConanFile +class TestConan(ConanFile): + name = "Hello" + version = "0.1" + settings = "os" + + def package(self): + self.copy("*.lib", dst="lib", keep_path=False) +""" + client.save({CONANFILE: conanfile, + "rootfile.lib": "contents", + "build/lib/hello.lib": "My Lib"}) + client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows --build_folder=build") + conan_ref = ConanFileReference.loads("Hello/0.1@lasote/stable") + package_ref = PackageReference(conan_ref, "3475bd55b91ae904ac96fde0f106a136ab951a5e") + package_folder = client.client_cache.package(package_ref) + rootfile_path = os.path.join(package_folder, "lib", "rootfile.lib") + self.assertFalse(os.path.exists(rootfile_path)) + + hello_path = os.path.join(package_folder, "lib", "hello.lib") + self.assertTrue(os.path.exists(hello_path)) + def test_build_source_folders(self): client = TestClient() conanfile = """ @@ -127,15 +195,13 @@ def package(self): self.copy("*.h", src="include", dst="inc") self.copy("*.lib", src="lib", dst="lib") """ - client.save({CONANFILE: conanfile}) - client.run("export lasote/stable") - - client.save({"src/include/header.h": "//Windows header", + client.save({CONANFILE: conanfile, + "src/include/header.h": "//Windows header", "src/include/header.txt": "", "build/libs/what": "", "build/lib/hello.lib": "My Lib", - "build/lib/bye.txt": ""}, clean_first=True) - client.run("package_files Hello/0.1@lasote/stable -s os=Windows --build_folder=build " + "build/lib/bye.txt": ""}) + client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows --build_folder=build " "--source_folder=src") conan_ref = ConanFileReference.loads("Hello/0.1@lasote/stable") package_ref = PackageReference(conan_ref, "3475bd55b91ae904ac96fde0f106a136ab951a5e") @@ -147,19 +213,46 @@ def package(self): self.assertEqual(os.listdir(lib), ["hello.lib"]) self.assertEqual(load(os.path.join(lib, "hello.lib")), "My Lib") - def test_paths(self): + def test_partial_references(self): client = TestClient() - client.run("new Hello/0.1 --bare") - client.run("export lasote/stable") - client.save({"Release_x86/lib/libmycoollib.a": ""}, clean_first=True) - settings = ('-s os=Windows -s compiler=gcc -s compiler.version=4.9 ' - '-s compiler.libcxx=libstdc++ -s build_type=Release -s arch=x86') - client.run("package_files Hello/0.1@lasote/stable %s -pf=Release_x86" % settings) - self._consume(client, settings + " -g cmake") + conanfile = """ +from conans import ConanFile +class TestConan(ConanFile): + name = "Hello" + version = "0.1" + settings = "os" - cmakeinfo = load(os.path.join(client.current_folder, "conanbuildinfo.cmake")) - self.assertIn("set(CONAN_LIBS_HELLO mycoollib)", cmakeinfo) - self.assertIn("set(CONAN_LIBS mycoollib ${CONAN_LIBS})", cmakeinfo) + def package(self): + self.copy("*") +""" + # 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) + + # Specify different name or version is not working + error = client.run("export-pkg . lib/1.0@conan/stable -f", ignore_error=True) + self.assertTrue(error) + self.assertIn("Specified name/version doesn't match with the name/version " + "in the conanfile", client.out) + + error = client.run("export-pkg . Hello/1.1@conan/stable -f", ignore_error=True) + self.assertTrue(error) + self.assertIn("Specified name/version doesn't match with the name/version " + "in the conanfile", client.out) + + conanfile = """ +from conans import ConanFile +class TestConan(ConanFile): + settings = "os" + + def package(self): + self.copy("*") +""" + # 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) def test_with_deps(self): client = TestClient() @@ -170,13 +263,14 @@ def test_with_deps(self): conanfile = TestConanFile(name="Hello1", requires=["Hello/0.1@lasote/stable"]) conanfile = str(conanfile) + """ def package_info(self): self.cpp_info.libs = self.collect_libs() + def package(self): + self.copy("*") """ client.save({"conanfile.py": conanfile}, clean_first=True) - client.run("export lasote/stable") - client.save({"Release_x86/lib/libmycoollib.a": ""}, clean_first=True) + client.save({"Release_x86/lib/libmycoollib.a": ""}) settings = ('-s os=Windows -s compiler=gcc -s compiler.version=4.9 ' '-s compiler.libcxx=libstdc++ -s build_type=Release -s arch=x86') - client.run("package_files Hello1/0.1@lasote/stable %s -pf=Release_x86" % settings) + client.run("export-pkg . Hello1/0.1@lasote/stable %s -bf=Release_x86" % settings) # consumer consumer = """ diff --git a/conans/test/command/help_test.py b/conans/test/command/help_test.py index 2924b81a747..498d14caab8 100644 --- a/conans/test/command/help_test.py +++ b/conans/test/command/help_test.py @@ -8,7 +8,7 @@ class BasicClientTest(unittest.TestCase): def help_test(self): client = TestClient() client.run("") - self.assertIn('Conan commands. Type $conan "command" -h', client.out) + self.assertIn('Conan commands. Type "conan -h" for help', client.out) client.run("--version") self.assertIn("Conan version %s" % __version__, client.out) diff --git a/conans/test/command/imports_test.py b/conans/test/command/imports_test.py index 550ff93fb65..6e301562b62 100644 --- a/conans/test/command/imports_test.py +++ b/conans/test/command/imports_test.py @@ -2,7 +2,7 @@ from conans.test.utils.tools import TestClient import os from conans.client.importer import IMPORTS_MANIFESTS -from conans.util.files import load +from conans.util.files import load, mkdir from conans.model.manifest import FileTreeManifest from conans.test.utils.test_files import temp_folder @@ -111,13 +111,13 @@ def imports_error_test(self): self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) - error = self.client.run("imports", ignore_error=True) - self.assertTrue(error) - self.assertIn("conanbuildinfo.txt file not found", self.client.user_io.out) + error = self.client.run("imports .") # Automatic conanbuildinfo.txt + self.assertFalse(error) + self.assertNotIn("conanbuildinfo.txt file not found", self.client.user_io.out) def install_manifest_test(self): self.client.save({"conanfile.txt": test1}, clean_first=True) - self.client.run("install -g txt") + self.client.run("install") self.assertIn("imports(): Copied 2 '.txt' files", self.client.user_io.out) self.assertIn("file1.txt", os.listdir(self.client.current_folder)) self.assertIn("file2.txt", os.listdir(self.client.current_folder)) @@ -125,37 +125,48 @@ def install_manifest_test(self): def install_manifest_without_install_test(self): self.client.save({"conanfile.txt": test1}, clean_first=True) - tmp_folder = temp_folder() - self.client.run('imports -d "%s"' % tmp_folder, ignore_error=True) - self.assertIn("You can generate it using 'conan install -g txt'", self.client.user_io.out) + self.client.run('imports . ', ignore_error=True) + self.assertIn("You can generate it using 'conan install'", self.client.user_io.out) def install_dest_test(self): self.client.save({"conanfile.txt": test1}, clean_first=True) - self.client.run("install --no-imports -g txt") + self.client.run("install --no-imports") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) - self.client.run("imports -d myfolder") + self.client.run("imports . -imf myfolder") files = os.listdir(os.path.join(self.client.current_folder, "myfolder")) self.assertIn("file1.txt", files) self.assertIn("file2.txt", files) + def imports_build_folder_test(self): + self.client.save({"conanfile.txt": test1}, clean_first=True) + tmp = self.client.current_folder + self.client.current_folder = os.path.join(self.client.current_folder, "build") + mkdir(self.client.current_folder) + self.client.run("install .. --no-imports") + self.client.current_folder = tmp + self.client.run("imports . --install-folder=build --import-folder=.") + files = os.listdir(self.client.current_folder) + self.assertIn("file1.txt", files) + self.assertIn("file2.txt", files) + def install_abs_dest_test(self): self.client.save({"conanfile.txt": test1}, clean_first=True) - self.client.run("install --no-imports -g txt") + self.client.run("install --no-imports") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) tmp_folder = temp_folder() - self.client.run('imports -d "%s"' % tmp_folder) + self.client.run('imports . -imf "%s"' % tmp_folder) files = os.listdir(tmp_folder) self.assertIn("file1.txt", files) self.assertIn("file2.txt", files) def undo_install_manifest_test(self): self.client.save({"conanfile.txt": test1}, clean_first=True) - self.client.run("install -g txt") - self.client.run("imports --undo") + self.client.run("install") + self.client.run("imports . --undo") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) self.assertNotIn(IMPORTS_MANIFESTS, os.listdir(self.client.current_folder)) @@ -176,7 +187,7 @@ def imports_test(self): self.client.run("install --no-imports -g txt") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) - self.client.run("imports") + self.client.run("imports .") self.assertIn("imports(): Copied 2 '.txt' files", self.client.user_io.out) self.assertIn("file1.txt", os.listdir(self.client.current_folder)) self.assertIn("file2.txt", os.listdir(self.client.current_folder)) @@ -186,20 +197,20 @@ def imports_filename_test(self): self.client.save({"conanfile.txt": test1, "conanfile.py": test2, "conanfile2.py": test3}, clean_first=True) - self.client.run("install --no-imports -g txt") + self.client.run("install --no-imports") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) - self.client.run("imports -f=conanfile2.py") + self.client.run("imports . -f=conanfile2.py") self.assertNotIn("file1.txt", os.listdir(self.client.current_folder)) self.assertIn("file2.txt", os.listdir(self.client.current_folder)) os.unlink(os.path.join(self.client.current_folder, "file2.txt")) - self.client.run("imports") + self.client.run("imports .") self.assertIn("file1.txt", os.listdir(self.client.current_folder)) self.assertNotIn("file2.txt", os.listdir(self.client.current_folder)) os.unlink(os.path.join(self.client.current_folder, "file1.txt")) - self.client.run("imports -f conanfile.txt") + self.client.run("imports . -f conanfile.txt") self.assertIn("file1.txt", os.listdir(self.client.current_folder)) self.assertIn("file2.txt", os.listdir(self.client.current_folder)) diff --git a/conans/test/command/info_folders_test.py b/conans/test/command/info_folders_test.py index 0339adafba1..aa914a2dc2f 100644 --- a/conans/test/command/info_folders_test.py +++ b/conans/test/command/info_folders_test.py @@ -5,7 +5,7 @@ from conans import tools from conans.test.utils.tools import TestClient from conans.test.utils.test_files import temp_folder -from conans.paths import CONANFILE +from conans.paths import CONANFILE, long_paths_support from conans.model.ref import ConanFileReference, PackageReference import re @@ -110,17 +110,23 @@ def test_single_field(self): self.assertNotIn("package", output) def test_short_paths(self): - if platform.system() == "Windows": - folder = temp_folder(False) - short_folder = os.path.join(folder, ".cn") - with tools.environment_append({"CONAN_USER_HOME_SHORT": short_folder}): - client = TestClient(base_folder=folder) - client.save({CONANFILE: conanfile_py.replace("False", "True")}) - client.run("export %s" % self.user_channel) - client.run("info --paths %s" % (self.conan_ref)) - base_path = os.path.join("MyPackage", "0.1.0", "myUser", "testing") - output = client.user_io.out - self.assertIn(os.path.join(base_path, "export"), output) + folder = temp_folder(False) + short_folder = os.path.join(folder, ".cn") + + with tools.environment_append({"CONAN_USER_HOME_SHORT": short_folder}): + client = TestClient(base_folder=folder) + client.save({CONANFILE: conanfile_py.replace("False", "True")}) + client.run("export %s" % self.user_channel) + client.run("info --paths %s" % (self.conan_ref)) + base_path = os.path.join("MyPackage", "0.1.0", "myUser", "testing") + output = client.user_io.out + self.assertIn(os.path.join(base_path, "export"), output) + if long_paths_support: + self.assertIn(os.path.join(base_path, "source"), output) + self.assertIn(os.path.join(base_path, "build"), output) + self.assertIn(os.path.join(base_path, "package"), output) + self.assertNotIn(short_folder, output) + else: self.assertNotIn(os.path.join(base_path, "source"), output) self.assertNotIn(os.path.join(base_path, "build"), output) self.assertNotIn(os.path.join(base_path, "package"), output) @@ -129,15 +135,16 @@ def test_short_paths(self): self.assertIn("build_folder: %s" % short_folder, output) self.assertIn("package_folder: %s" % short_folder, output) - # Ensure that the inner folders are not created (that could affect - # pkg creation flow - ref = ConanFileReference.loads(self.conan_ref) - id_ = re.search('ID:\s*([a-z0-9]*)', str(client.user_io.out)).group(1) - pkg_ref = PackageReference(ref, id_) - for path in (client.client_cache.source(ref, True), - client.client_cache.build(pkg_ref, True), - client.client_cache.package(pkg_ref, True)): - self.assertFalse(os.path.exists(path)) + # Ensure that the inner folders are not created (that could affect + # pkg creation flow + ref = ConanFileReference.loads(self.conan_ref) + id_ = re.search('ID:\s*([a-z0-9]*)', str(client.user_io.out)).group(1) + pkg_ref = PackageReference(ref, id_) + for path in (client.client_cache.source(ref, True), + client.client_cache.build(pkg_ref, True), + client.client_cache.package(pkg_ref, True)): + self.assertFalse(os.path.exists(path)) + if not long_paths_support: # The parent has been created for .conan_link self.assertTrue(os.path.exists(os.path.dirname(path))) def test_direct_conanfile(self): diff --git a/conans/test/command/info_test.py b/conans/test/command/info_test.py index ad20a8ad9c4..613d8aad1a1 100644 --- a/conans/test/command/info_test.py +++ b/conans/test/command/info_test.py @@ -183,11 +183,12 @@ class MyTest(ConanFile): self.client.save({"subfolder/conanfile.py": conanfile}) self.client.run("export --path ./subfolder lasote/testing") - self.client.run("info --cwd ./subfolder") + self.client.run("info ./subfolder") self.assertIn("Pkg/0.1@PROJECT", self.client.user_io.out) - self.client.run("info --cwd ./subfolder --build_order Pkg/0.1@lasote/testing --json=jsonfile.txt") - path = os.path.join(self.client.current_folder, "subfolder", "jsonfile.txt") + self.client.run("info ./subfolder --build_order " + "Pkg/0.1@lasote/testing --json=jsonfile.txt") + path = os.path.join(self.client.current_folder, "jsonfile.txt") self.assertTrue(os.path.exists(path)) def reuse_test(self): diff --git a/conans/test/command/install_cwd_test.py b/conans/test/command/install_cwd_test.py index 99c0c8e3ccc..9a48ade5269 100644 --- a/conans/test/command/install_cwd_test.py +++ b/conans/test/command/install_cwd_test.py @@ -28,5 +28,5 @@ def build(self): def test_install_cwd(self): self.client.save({CONANFILE_TXT: "[requires]MyLib/0.1@lasote/stable"}, clean_first=True) os.mkdir(os.path.join(self.client.current_folder, "new")) - self.client.run("install ../ --cwd new -g cmake") + self.client.run("install . --install-folder new -g cmake") self.assertTrue(os.path.join(self.client.current_folder, "new", "conanbuildinfo.cmake")) diff --git a/conans/test/command/install_test.py b/conans/test/command/install_test.py index 44217423c00..dbdb81a0f5d 100644 --- a/conans/test/command/install_test.py +++ b/conans/test/command/install_test.py @@ -19,6 +19,21 @@ def setUp(self): self.settings = ("-s os=Windows -s compiler='Visual Studio' -s compiler.version=12 " "-s arch=x86 -s compiler.runtime=MD") + def install_package_folder_test(self): + # Make sure a simple conan install doesn't fire package_info() so self.package_folder breaks + client = TestClient() + client.save({"conanfile.py": """from conans import ConanFile +import os +class Pkg(ConanFile): + def package_info(self): + self.dummy_doesnt_exist_not_break + self.output.info("Hello") + self.env_info.PATH = os.path.join(self.package_folder, "bin") +"""}) + client.run("install .") + self.assertNotIn("Hello", client.out) + self.assertIn("PROJECT: Generated conaninfo.txt", client.out) + def _create(self, number, version, deps=None, export=True, no_config=False): files = cpp_hello_conan_files(number, version, deps, build=False, config=not no_config) @@ -262,11 +277,25 @@ class TestConan(ConanFile): client.run("export lasote/stable") client.save({"conanfile.txt": "[requires]\nHello/0.1@lasote/stable"}, clean_first=True) - client.run("install .. --build=missing -s os=Windows -c=win_dir") - client.run("install .. --build=missing -s os=Macos -c=os_dir") + client.run("install . --build=missing -s os=Windows --install-folder=win_dir") + client.run("install . --build=missing -s os=Macos --install-folder=os_dir") conaninfo = load(os.path.join(client.current_folder, "win_dir/conaninfo.txt")) self.assertIn("os=Windows", conaninfo) self.assertNotIn("os=Macos", conaninfo) conaninfo = load(os.path.join(client.current_folder, "os_dir/conaninfo.txt")) self.assertNotIn("os=Windows", conaninfo) self.assertIn("os=Macos", conaninfo) + + def install_reference_not_conanbuildinfo_test(self): + conanfile = """from conans import ConanFile +class TestConan(ConanFile): + name = "Hello" + version = "0.1" + settings = "os" +""" + client = TestClient() + client.save({"conanfile.py": conanfile}) + client.run("create conan/stable") + client.save({}, clean_first=True) + client.run("install Hello/0.1@conan/stable") + self.assertFalse(os.path.exists(os.path.join(client.current_folder, "conanbuildinfo.txt"))) diff --git a/conans/test/command/package_test.py b/conans/test/command/package_test.py index ad8acaebe2c..0879757b4e6 100644 --- a/conans/test/command/package_test.py +++ b/conans/test/command/package_test.py @@ -1,105 +1,54 @@ import unittest + +from conans import tools from conans.test.utils.tools import TestClient -from conans.model.ref import ConanFileReference, PackageReference import os from conans.paths import CONANFILE -from conans.util.files import mkdir, load +from conans.util.files import load from conans.test.utils.test_files import temp_folder from nose_parameterized import parameterized -class PackageCommandTest(unittest.TestCase): - - def bad_reference_error_test(self): - client = TestClient() - error = client.run("package whatever@user/channel", ignore_error=True) - self.assertTrue(error) - self.assertIn("Wrong package recipe reference", client.out) +class PackageLocalCommandTest(unittest.TestCase): - def unexisting_reference_error_test(self): + def package_with_destination_test(self): client = TestClient() - error = client.run("package whatever/1.0@user/channel", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: Package recipe 'whatever/1.0@user/channel' does not exist", - client.out) - def package_errors_test(self): + def prepare_for_package(the_client): + the_client.save({"src/header.h": "contents"}, clean_first=True) + the_client.run("new lib/1.0 -s") + + # don't need real build + tools.replace_in_file(os.path.join(client.current_folder, + "conanfile.py"), "cmake =", + "return\n#") + the_client.run("install . --install-folder build") + the_client.run("build . --build-folder build2 --install-folder build") + + # In current dir subdir + prepare_for_package(client) + client.run("package . --build-folder build2 --install-folder build --package_folder=subdir") + self.assertTrue(os.path.exists(os.path.join(client.current_folder, "subdir"))) + + # Default path + prepare_for_package(client) + client.run("package . --build-folder build") + self.assertTrue(os.path.exists(os.path.join(client.current_folder, "build", "package"))) + + # Abs path + prepare_for_package(client) + pf = os.path.join(client.current_folder, "mypackage/two") + client.run("package . --build-folder build --package_folder='%s'" % pf) + self.assertTrue(os.path.exists(os.path.join(client.current_folder, "mypackage", "two"))) + + def package_with_reference_errors_test(self): client = TestClient() - - conanfile_template = """ -from conans import ConanFile - -class MyConan(ConanFile): - name = "MyLib" - version = "0.1" -""" - client.save({CONANFILE: conanfile_template}) - client.run("export lasote/stable") error = client.run("package MyLib/0.1@lasote/stable", ignore_error=True) self.assertTrue(error) - self.assertIn("ERROR: MyLib/0.1@lasote/stable: Package has not been built in local cache", + self.assertIn("conan package' doesn't accept a reference anymore", client.out) - error = client.run("package MyLib/0.1@lasote/stable 1234", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: MyLib/0.1@lasote/stable: Package binary '1234' folder doesn't exist", - client.out) - - def package_test(self): - """Use 'conan package' command to repackage a generated package (without build it)""" - client = TestClient() - conanfile_template = """ -from conans import ConanFile - -class MyConan(ConanFile): - name = "MyLib" - version = "0.1" - exports = '*' - - def package(self): - self.copy(pattern="*.h", dst="include", keep_path=False) - #self.copy(pattern="*.a", dst="lib", keep_path=False) -""" - - client.save({"lib/file1.a": "foo", - "include/file.h": "foo", - CONANFILE: conanfile_template}) - client.run("export lasote/stable") - - # Build and package conan file - conan_reference = ConanFileReference.loads("MyLib/0.1@lasote/stable") - client.run("install %s --build missing" % str(conan_reference)) - package_id = "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9" - package_path = client.paths.package(PackageReference(conan_reference, package_id)) - # Verify the headers are there but lib doesn't - self.assertTrue(os.path.exists(os.path.join(package_path, "include", "file.h"))) - self.assertFalse(os.path.exists(os.path.join(package_path, "lib", "file1.a"))) - - # Fix conanfile and re-package - client.save({CONANFILE: conanfile_template.replace("#", "")}) - client.run("export lasote/stable") - # Build and package conan file - client.run("package %s %s" % (conan_reference, package_id)) - self.assertIn("MyLib/0.1@lasote/stable: " - "Re-packaging 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.user_io.out) - self.assertTrue(os.path.exists(os.path.join(package_path, "include", "file.h"))) - self.assertTrue(os.path.exists(os.path.join(package_path, "lib", "file1.a"))) - - # Fix again conanfile and re-package, repackaging ALL binaries - client.save({CONANFILE: conanfile_template.replace("self.copy", "pass #")}) - client.run("export lasote/stable") - # Build and package conan file - client.run("package %s" % str(conan_reference)) - self.assertIn("MyLib/0.1@lasote/stable: " - "Re-packaging 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.user_io.out) - self.assertFalse(os.path.exists(os.path.join(package_path, "include", "file.h"))) - self.assertFalse(os.path.exists(os.path.join(package_path, "lib", "file1.a"))) - - -class PackageLocalCommandTest(unittest.TestCase): - - @parameterized.expand([(False, ), (True, )]) - def local_package_test(self, child_folder): + def local_package_test(self): client = TestClient() conanfile_template = """ from conans import ConanFile @@ -112,13 +61,8 @@ def package(self): CONANFILE: conanfile_template}) client.run("install") recipe_folder = client.current_folder - if child_folder: - package_folder = os.path.join(client.current_folder, "package") - os.makedirs(package_folder) - else: - package_folder = temp_folder() - client.current_folder = package_folder client.run('package "%s"' % recipe_folder) + package_folder = os.path.join(client.current_folder, "package") content = load(os.path.join(package_folder, "include/file.h")) self.assertEqual(content, "foo") self.assertEqual(sorted(os.listdir(package_folder)), @@ -126,7 +70,7 @@ def package(self): self.assertEqual(os.listdir(os.path.join(package_folder, "include")), ["file.h"]) @parameterized.expand([(False, ), (True, )]) - def local_package_build_test(self, child_folder): + def local_package_build_test(self, default_folder): client = TestClient() conanfile_template = """ from conans import ConanFile @@ -144,24 +88,28 @@ def package(self): client.current_folder = os.path.join(client.current_folder, "build") client.run("install ..") - if child_folder: - package_folder = os.path.join(recipe_folder, "package") - os.makedirs(package_folder) - client.current_folder = package_folder - client.run('package .. --build_folder=../build') + if default_folder: + package_folder = os.path.join(client.current_folder, "package") + client.run('package .. --build-folder=.') + self.assertEqual(sorted(os.listdir(package_folder)), + sorted(["include", "lib", "conaninfo.txt", "conanmanifest.txt"])) else: package_folder = temp_folder() client.current_folder = package_folder - client.run('package "{0}" --build_folder="{0}/build"'.format(recipe_folder)) + build_folder = os.path.join(recipe_folder, "build") + client.run('package "{0}" --build_folder="{2}"' + ' --package_folder="{1}"'.format(recipe_folder, package_folder, build_folder)) + self.assertEqual(sorted(os.listdir(package_folder)), + sorted(["include", "lib", "conaninfo.txt", + "conanmanifest.txt"])) + 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"])) self.assertEqual(os.listdir(os.path.join(package_folder, "include")), ["file.h"]) self.assertEqual(os.listdir(os.path.join(package_folder, "lib")), ["mypkg.lib"]) @parameterized.expand([(False, ), (True, )]) - def local_package_source_test(self, child_folder): + def local_package_source_test(self, default_folder): client = TestClient() conanfile_template = """ from conans import ConanFile @@ -179,47 +127,17 @@ def package(self): client.current_folder = os.path.join(client.current_folder, "build") client.run("install ..") - if child_folder: - package_folder = os.path.join(recipe_folder, "package") - os.makedirs(package_folder) - client.current_folder = package_folder - client.run('package .. --build_folder=../build --source_folder=../src') + if default_folder: + package_folder = os.path.join(client.current_folder, "package") + client.run('package .. --build_folder=. --source_folder=../src ') else: package_folder = temp_folder() - client.current_folder = package_folder - client.run('package "{0}" --build_folder="{0}/build" --source_folder="{0}/src"'. - format(recipe_folder)) + client.run('package "{0}" --build_folder="{0}/build" ' + '--package_folder="{1}" --source_folder="{0}/src"'. + format(recipe_folder, package_folder)) 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"])) self.assertEqual(os.listdir(os.path.join(package_folder, "include")), ["file.h"]) self.assertEqual(os.listdir(os.path.join(package_folder, "lib")), ["mypkg.lib"]) - - def local_flow_test(self): - """Use 'conan package' to process locally the package method""" - client = TestClient() - conanfile_template = """ -from conans import ConanFile - -class MyConan(ConanFile): - def package(self): - self.copy(pattern="*.h", dst="include", src="include") -""" - files = {"include/file.h": "foo", - CONANFILE: conanfile_template} - - client.save(files) - origin_folder = client.current_folder - client.run("install -g txt") - client.run("source") - client.run("build") - error = client.run("package .", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: Cannot 'conan package' to the build folder", client.user_io.out) - package_folder = os.path.join(origin_folder, "package") - mkdir(package_folder) - client.current_folder = package_folder - client.run('package .. --build_folder=..') - content = load(os.path.join(client.current_folder, "include/file.h")) - self.assertEqual(content, "foo") diff --git a/conans/test/command/profile_test.py b/conans/test/command/profile_test.py index cdcf9b1dcc2..302591e9e03 100644 --- a/conans/test/command/profile_test.py +++ b/conans/test/command/profile_test.py @@ -19,6 +19,8 @@ def list_test(self): create_profile(client.client_cache.profiles_path, "profile3") create_profile(client.client_cache.profiles_path, "profile1") create_profile(client.client_cache.profiles_path, "profile2") + # Make sure local folder doesn't interact with profiles + os.mkdir(os.path.join(client.current_folder, "profile3")) client.run("profile list") self.assertEqual(list(["profile1", "profile2", "profile3"]), list(str(client.user_io.out).splitlines())) diff --git a/conans/test/command/source_test.py b/conans/test/command/source_test.py index 2084897a7c1..ad51f14ff74 100644 --- a/conans/test/command/source_test.py +++ b/conans/test/command/source_test.py @@ -1,44 +1,42 @@ import unittest -from conans.paths import CONANFILE + +from conans.paths import CONANFILE, BUILD_INFO from conans.test.utils.tools import TestClient -from conans.util.files import load +from conans.util.files import load, mkdir import os class SourceTest(unittest.TestCase): - def basic_source_test(self): + def source_reference_test(self): + client = TestClient() + error = client.run("source lib/1.0@conan/stable", ignore_error=True) + self.assertTrue(error) + self.assertIn("'conan source' doesn't accept a reference anymore", client.out) + + def source_local_cwd_test(self): conanfile = ''' -from conans import ConanFile import os +from conans import ConanFile class ConanLib(ConanFile): name = "Hello" version = "0.1" def source(self): - assert(os.listdir(".") == []) # Not conanfile copied, clean source self.output.info("Running source!") + self.output.info("cwd=>%s" % os.getcwd()) ''' client = TestClient() client.save({CONANFILE: conanfile}) - client.run("export lasote/stable") - client.run("source Hello/0.1@lasote/stable") - self.assertIn("Hello/0.1@lasote/stable: Configuring sources", client.user_io.out) - self.assertIn("Hello/0.1@lasote/stable: Running source!", client.user_io.out) - - # The second call shouldn't have effect - client.run("source Hello/0.1@lasote/stable") - self.assertNotIn("Hello/0.1@lasote/stable: Configuring sources", client.user_io.out) - self.assertNotIn("Hello/0.1@lasote/stable: Running source!", client.user_io.out) - - # Forced should have effect - client.run("source Hello/0.1@lasote/stable --force") - self.assertIn("WARN: Forced removal of source folder", client.user_io.out) - self.assertIn("Hello/0.1@lasote/stable: Configuring sources", client.user_io.out) - self.assertIn("Hello/0.1@lasote/stable: Running source!", client.user_io.out) + subdir = os.path.join(client.current_folder, "subdir") + os.mkdir(subdir) + client.run("install . --install-folder subdir") + client.run("source . --install-folder subdir --source_folder subdir") + self.assertIn("PROJECT: Configuring sources", client.user_io.out) + self.assertIn("PROJECT: cwd=>%s" % subdir, client.user_io.out) - def source_local_cwd_test(self): + def local_source_src_not_exist_test(self): conanfile = ''' import os from conans import ConanFile @@ -48,16 +46,85 @@ class ConanLib(ConanFile): version = "0.1" def source(self): - self.output.info("Running source!") - self.output.info("cwd=>%s" % os.getcwd()) + pass ''' client = TestClient() client.save({CONANFILE: conanfile}) - subdir = os.path.join(client.current_folder, "subdir") - os.mkdir(subdir) - client.run("source .. --cwd subdir") - self.assertIn("PROJECT: Configuring sources", client.user_io.out) - self.assertIn("PROJECT: cwd=>%s" % subdir, client.user_io.out) + # Automatically created + client.run("source . --source_folder=src") + self.assertTrue(os.path.exists(os.path.join(client.current_folder, "src"))) + + def build_folder_no_exists_crash_test(self): + conanfile = ''' +import os +from conans import ConanFile + +class ConanLib(ConanFile): + name = "Hello" + version = "0.1" + + def source(self): + pass +''' + client = TestClient() + client.save({CONANFILE: conanfile}) + # Automatically created + error = client.run("source . --install-folder=missing_folder", ignore_error=True) + self.assertTrue(error) + self.assertIn("Specified info-folder doesn't exist", client.out) + + def build_folder_reading_infos_test(self): + conanfile = ''' +import os +from conans import ConanFile + +class ConanLib(ConanFile): + name = "Hello" + version = "0.1" + + def package_info(self): + self.cpp_info.cppflags.append("FLAG") + self.env_info.MYVAR = "foo" + self.user_info.OTHERVAR = "bar" +''' + client = TestClient() + client.save({CONANFILE: conanfile}) + client.run("export conan/testing") + + conanfile = ''' +import os +from conans import ConanFile +from conans.util.files import save + +class ConanLib(ConanFile): + + requires="Hello/0.1@conan/testing" + + def source(self): + assert(os.getcwd() == self.source_folder) + self.output.info("FLAG=%s" % self.deps_cpp_info["Hello"].cppflags[0]) + self.output.info("MYVAR=%s" % self.deps_env_info["Hello"].MYVAR) + self.output.info("OTHERVAR=%s" % self.deps_user_info["Hello"].OTHERVAR) + self.output.info("CURDIR=%s" % os.getcwd()) + +''' + # First, failing source() + client.save({CONANFILE: conanfile}, clean_first=True) + build_folder = os.path.join(client.current_folder, "build") + src_folder = os.path.join(client.current_folder, "src") + mkdir(build_folder) + mkdir(src_folder) + client.run("source . --install-folder='%s' --source_folder='%s'" % (build_folder, src_folder), + ignore_error=True) + self.assertIn("self.deps_cpp_info not defined.", client.out) + + client.run("install . --install-folder build --build ") + client.run("source . --install-folder='%s' --source_folder='%s'" % (build_folder, src_folder), + ignore_error=True) + self.assertIn("FLAG=FLAG", client.out) + self.assertIn("MYVAR=foo", client.out) + self.assertIn("OTHERVAR=bar", client.out) + self.assertIn("CURDIR=%s" % src_folder, client.out) def local_source_test(self): conanfile = ''' @@ -73,7 +140,8 @@ def source(self): ''' # First, failing source() client = TestClient() - client.save({CONANFILE: conanfile}) + client.save({CONANFILE: conanfile, + BUILD_INFO: ""}) client.run("source .", ignore_error=True) self.assertIn("PROJECT: Running source!", client.user_io.out) diff --git a/conans/test/command/upload_test.py b/conans/test/command/upload_test.py index d8fb0795f34..3fcc9974aff 100644 --- a/conans/test/command/upload_test.py +++ b/conans/test/command/upload_test.py @@ -65,6 +65,15 @@ def _client(self): client = TestClient(servers=self._servers, users={"default": [("lasote", "mypass")]}) return client + def pattern_upload_test(self): + client = self._client() + client.save({"conanfile.py": conanfile}) + client.run("create user/testing") + client.run("upload Hello0/*@user/testing --confirm --all") + self.assertIn("Uploading conanmanifest.txt", client.user_io.out) + self.assertIn("Uploading conan_package.tgz", client.user_io.out) + self.assertIn("Uploading conanfile.py", client.user_io.out) + def corrupt_upload_test(self): client = self._client() @@ -144,48 +153,6 @@ def upload_unmodified_recipe_test(self): client.out) self.assertIn("Recipe is up to date, upload skipped", client.out) - def upload_modified_package_test(self): - client = self._client() - - client.save({"conanfile.py": conanfile, - "hello.cpp": ""}) - client.run("create frodo/stable") - client.run("upload Hello0/1.2.1@frodo/stable --all") - - self.assertIn("Uploading conanmanifest.txt", client.out) - self.assertIn("Uploading conanfile.py", client.out) - self.assertIn("Uploading conan_sources.tgz", client.out) - self.assertIn("Uploaded conan recipe 'Hello0/1.2.1@frodo/stable' to 'default'", - client.out) - self.assertIn("Uploading conaninfo.txt", client.out) - self.assertIn("Uploading conan_package.tgz", client.out) - - client2 = self._client() - client2.save({"conanfile.py": conanfile, - "hello.cpp": ""}) - client2.run("create frodo/stable") - client2.save({"hello.cpp": "changed!"}, clean_first=True) - client2.run("package_files Hello0/1.2.1@frodo/stable -f") - client2.run("upload Hello0/1.2.1@frodo/stable --all") - self.assertIn("Recipe is up to date, upload skipped", client2.out) - self.assertNotIn("Uploading conanfile.py", client2.out) - self.assertNotIn("Uploading conan_sources.tgz", client2.out) - self.assertNotIn("Uploaded conan recipe 'Hello0/1.2.1@frodo/stable' to 'default'", - client2.out) - self.assertNotIn("Uploading conaninfo.txt", client2.out) # conaninfo NOT changed - self.assertIn("Uploading conan_package.tgz", client2.out) - - # first client tries to upload again - # packages are NOT checked for manifest date, they are always overwritten - client.run("upload Hello0/1.2.1@frodo/stable --all") - self.assertIn("Recipe is up to date, upload skipped", client.out) - self.assertNotIn("Uploading conanfile.py", client.out) - self.assertNotIn("Uploading conan_sources.tgz", client.out) - self.assertNotIn("Uploaded conan recipe 'Hello0/1.2.1@frodo/stable' to 'default'", - client.out) - self.assertNotIn("Uploading conaninfo.txt", client.out) # conaninfo NOT changed - self.assertIn("Uploading conan_package.tgz", client.out) - def upload_unmodified_package_test(self): client = self._client() diff --git a/conans/test/command/user_test.py b/conans/test/command/user_test.py index 230750b74a1..547da95ca50 100644 --- a/conans/test/command/user_test.py +++ b/conans/test/command/user_test.py @@ -93,3 +93,21 @@ class ConanLib(ConanFile): client.run("upload lib/0.1@lasote/stable") client.run("user") self.assertIn("Current 'default' user: lasote", client.user_io.out) + + def test_command_user_with_interactive_password(self): + test_server = TestServer() + servers = {"default": test_server} + conan = TestClient(servers=servers, users={"default": [("lasote", "mypass")]}) + conan.run('user -p -r default lasote') + self.assertIn('Please enter a password for "lasote" account:', conan.user_io.out) + conan.run("user") + self.assertIn("Current 'default' user: lasote", conan.user_io.out) + + def test_command_interactive_only(self): + test_server = TestServer() + servers = {"default": test_server} + conan = TestClient(servers=servers, users={"default": [("lasote", "mypass")]}) + conan.run('user -p') + self.assertIn('Please enter a password for "lasote" account:', conan.user_io.out) + conan.run("user") + self.assertIn("Current 'default' user: lasote", conan.user_io.out) diff --git a/conans/test/conanfile_extend_test.py b/conans/test/conanfile_extend_test.py index f16cb0cfe4a..e3581c56177 100644 --- a/conans/test/conanfile_extend_test.py +++ b/conans/test/conanfile_extend_test.py @@ -76,7 +76,7 @@ def requirements(self): self.assertIn("test_option=2", conaninfo) self.assertNotIn("otherlib/0.2@user/channel", conaninfo) self.assertNotIn("otherlib:otherlib_option=1", conaninfo) - client.run("build") + client.run("build .") self.assertIn("MyFlag False", client.user_io.out) client.run("info") self.assertIn("lib/0.1@user/channel", client.user_io.out) @@ -88,7 +88,7 @@ def requirements(self): self.assertIn("test_option=2", conaninfo) self.assertIn("otherlib/0.2@user/channel", conaninfo) self.assertIn("otherlib:otherlib_option=1", conaninfo) - client.run("build -f=conanfile_dev.py") + client.run("build . -f=conanfile_dev.py") self.assertIn("MyFlag True", client.user_io.out) client.run("info -f=conanfile_dev.py") self.assertIn("lib/0.1@user/channel", client.user_io.out) diff --git a/conans/test/functional/autotools_configure_test.py b/conans/test/functional/autotools_configure_test.py index c4a67f2aecf..b2d170fca51 100644 --- a/conans/test/functional/autotools_configure_test.py +++ b/conans/test/functional/autotools_configure_test.py @@ -1,16 +1,20 @@ +import platform import unittest from conans.client.configure_build_environment import AutoToolsBuildEnvironment from conans import tools +from conans.client.tools.oss import cpu_count from conans.test.utils.conanfile import MockConanfile, MockSettings +from conans.test.util.tools_test import RunnerMock class AutoToolsConfigureTest(unittest.TestCase): def _set_deps_info(self, conanfile): conanfile.deps_cpp_info.include_paths.append("path/includes") - conanfile.deps_cpp_info.include_paths.append("other/include/path") - conanfile.deps_cpp_info.lib_paths.append("one/lib/path") + conanfile.deps_cpp_info.include_paths.append("other\include\path") + # To test some path in win, to be used with MinGW make or MSYS etc + conanfile.deps_cpp_info.lib_paths.append("one\lib\path") conanfile.deps_cpp_info.libs.append("onelib") conanfile.deps_cpp_info.libs.append("twolib") conanfile.deps_cpp_info.defines.append("onedefinition") @@ -21,6 +25,22 @@ def _set_deps_info(self, conanfile): conanfile.deps_cpp_info.exelinkflags.append("exe_link_flag") conanfile.deps_cpp_info.sysroot = "/path/to/folder" + def test_mocked_methods(self): + + runner = RunnerMock() + conanfile = MockConanfile(MockSettings({}), runner) + ab = AutoToolsBuildEnvironment(conanfile) + ab.make(make_program="othermake") + self.assertEquals(runner.command_called, "othermake -j%s" % cpu_count()) + + with tools.environment_append({"CONAN_MAKE_PROGRAM": "mymake"}): + ab.make(make_program="othermake") + self.assertEquals(runner.command_called, "mymake -j%s" % cpu_count()) + + ab.make(args=["things"]) + things = "'things'" if platform.system() != "Windows" else "things" + self.assertEquals(runner.command_called, "make %s -j%s" % (things, cpu_count())) + def test_variables(self): # GCC 32 settings = MockSettings({"build_type": "Release", @@ -255,8 +275,8 @@ def get_values(this_os, this_arch, setting_os, setting_arch): self.assertFalse(target) build, host, target = get_values("Linux", "x86_64", "Linux", "x86") - self.assertFalse(build) - self.assertFalse(host) + self.assertEquals(build, "x86_64-linux-gnu") + self.assertEquals(host, "i686-linux-gnueabi") self.assertFalse(target) build, host, target = get_values("Linux", "x86_64", "Linux", "armv7hf") @@ -273,7 +293,7 @@ def get_values(this_os, this_arch, setting_os, setting_arch): build, host, target = get_values("Linux", "x86_64", "Android", "x86") self.assertEquals(build, "x86_64-linux-gnu") - self.assertEquals(host, "x86-linux-android") + self.assertEquals(host, "i686-linux-android") build, host, target = get_values("Linux", "x86_64", "Android", "x86_64") self.assertEquals(build, "x86_64-linux-gnu") @@ -314,8 +334,8 @@ def get_values(this_os, this_arch, setting_os, setting_arch): self.assertFalse(target) build, host, target = get_values("Windows", "x86_64", "Windows", "x86") - self.assertFalse(build) - self.assertFalse(host) + self.assertEquals(build, "x86_64-w64-mingw32") + self.assertEquals(host, "i686-w64-mingw32") self.assertFalse(target) build, host, target = get_values("Windows", "x86_64", "Linux", "armv7hf") diff --git a/conans/test/functional/cmake_test.py b/conans/test/functional/cmake_test.py index 564d9c50982..861e549906f 100644 --- a/conans/test/functional/cmake_test.py +++ b/conans/test/functional/cmake_test.py @@ -426,7 +426,6 @@ def test_verbose(self): settings.compiler = "Visual Studio" settings.compiler.version = "12" settings.arch = "x86" - settings.os = "Windows" conan_file = ConanFileMock() conan_file.settings = settings @@ -449,6 +448,23 @@ def test_verbose(self): del cmake.definitions["CMAKE_VERBOSE_MAKEFILE"] self.assertFalse(cmake.verbose) + def set_toolset_test(self): + settings = Settings.loads(default_settings_yml) + settings.os = "Windows" + settings.compiler = "Visual Studio" + settings.compiler.version = "15" + settings.arch = "x86" + + conan_file = ConanFileMock() + conan_file.settings = settings + + cmake = CMake(conan_file, toolset="v141") + self.assertIn('-T "v141"', cmake.command_line) + + with tools.environment_append({"CONAN_CMAKE_TOOLSET": "v141"}): + cmake = CMake(conan_file) + self.assertIn('-T "v141"', cmake.command_line) + @staticmethod def scape(args): pattern = "%s" if sys.platform == "win32" else r"'%s'" @@ -459,7 +475,7 @@ class ConanFileMock(ConanFile): def __init__(self, shared=None): self.command = None self.path = None - self._conanfile_directory = "." + self.conanfile_directory = "." self.source_folder = self.build_folder = "." self.settings = None self.deps_cpp_info = namedtuple("deps_cpp_info", "sysroot")("/path/to/sysroot") diff --git a/conans/test/functional/compile_helpers_test.py b/conans/test/functional/compile_helpers_test.py index fc93ee8656c..aec4de0a092 100644 --- a/conans/test/functional/compile_helpers_test.py +++ b/conans/test/functional/compile_helpers_test.py @@ -470,7 +470,7 @@ def build_with_profile_test(self): self.client.save({CONANFILE: conanfile_scope_env}, clean_first=True) self.client.run("install --build=missing --pr scopes_env") - self.client.run("build") + self.client.run("build .") self.assertRegexpMatches(str(self.client.user_io.out), "PATH=['\"]*/path/to/my/folder") self._assert_env_variable_printed("CC", "/path/tomy/gcc_build") self._assert_env_variable_printed("CXX", "/path/tomy/g++_build") @@ -486,4 +486,4 @@ def _create_profile(self, name, settings, scopes=None, env=None): profile.scopes = Scopes.from_list(["%s=%s" % (key, value) for key, value in scopes.items()]) for varname, value in env.items(): profile.env_values.add(varname, value) - save(os.path.join(self.client.client_cache.profiles_path, name), profile.dumps()) + save(os.path.join(self.client.client_cache.profiles_path, name), "include(default)\n" + profile.dumps()) diff --git a/conans/test/functional/conan_trace_file_test.py b/conans/test/functional/conan_trace_file_test.py index 6f0f8d6cf2f..81a018b4c72 100644 --- a/conans/test/functional/conan_trace_file_test.py +++ b/conans/test/functional/conan_trace_file_test.py @@ -104,7 +104,7 @@ def test_trace_actions(self): self.assertEquals(json.loads(actions[2])["name"], "export") self.assertEquals(json.loads(actions[3])["_action"], "COMMAND") - self.assertEquals(json.loads(actions[3])["name"], "install") + self.assertEquals(json.loads(actions[3])["name"], "install_reference") self.assertEquals(json.loads(actions[4])["_action"], "GOT_RECIPE_FROM_LOCAL_CACHE") self.assertEquals(json.loads(actions[4])["_id"], "Hello0/0.1@lasote/stable") diff --git a/conans/test/functional/create_package_test.py b/conans/test/functional/create_package_test.py index ce0d124c0b8..decbec13bc2 100644 --- a/conans/test/functional/create_package_test.py +++ b/conans/test/functional/create_package_test.py @@ -51,7 +51,7 @@ def complete_test(self): client.save(files, path=reg_folder) client.save({CONANFILE: myconan1, - CONANINFO: "//empty", + "infos/%s" % CONANINFO: "//empty", "include/no_copy/lib0.h": "NO copy", "include/math/lib1.h": "copy", "include/math/lib2.h": "copy", @@ -76,6 +76,7 @@ def complete_test(self): package_ref = PackageReference(conan_ref, "myfakeid") build_folder = client.paths.build(package_ref) package_folder = client.paths.package(package_ref) + install_folder = os.path.join(build_folder, "infos") shutil.copytree(reg_folder, build_folder) @@ -83,8 +84,8 @@ def complete_test(self): loader = ConanFileLoader(None, Settings(), Profile()) conanfile = loader.load_conan(conanfile_path, None) - create_package(conanfile, build_folder, build_folder, package_folder, output, - copy_info=True) + create_package(conanfile, build_folder, build_folder, package_folder, install_folder, + output, copy_info=True) # test build folder self.assertTrue(os.path.exists(build_folder)) diff --git a/conans/test/functional/default_profile_test.py b/conans/test/functional/default_profile_test.py index 8ea24c3f20d..972a3579156 100644 --- a/conans/test/functional/default_profile_test.py +++ b/conans/test/functional/default_profile_test.py @@ -10,6 +10,24 @@ class DefaultProfileTest(unittest.TestCase): + def conanfile_txt_incomplete_profile_test(self): + conanfile = '''from conans import ConanFile +class MyConanfile(ConanFile): + pass +''' + + client = TestClient() + save(client.client_cache.default_profile_path, "[env]\nValue1=A") + + client.save({CONANFILE: conanfile}) + client.run("create Pkg/0.1@lasote/stable") + self.assertIn("Pkg/0.1@lasote/stable: Package '5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9' " + "created", client.out) + + client.save({"conanfile.txt": "[requires]\nPkg/0.1@lasote/stable"}, clean_first=True) + client.run('install .') + self.assertIn("Pkg/0.1@lasote/stable: Already installed!", client.out) + def change_default_profile_test(self): br = ''' import os @@ -47,12 +65,11 @@ def test_profile_applied_ok(self): class BuildRequireConanfile(ConanFile): name = "br" - version = "0.1" + version = "1.0" settings = "os", "compiler", "arch" def package_info(self): - self.env_info.MYVAR="from_build_require" - + self.env_info.MyVAR="from_build_require" ''' client = TestClient() @@ -65,9 +82,6 @@ def package_info(self): compiler.runtime=MD arch=x86 -[env] -MyVAR=23 - [options] mypackage:option1=2 @@ -97,9 +111,21 @@ def configure(self): assert(self.settings.compiler.runtime=="MD") assert(self.settings.arch=="x86") assert(self.options.option1=="2") - assert(os.environ["MyVAR"], "from_build_require") + + def build(self): + # This has changed, the value from profile higher priority than build require + assert(os.environ["MyVAR"]=="%s") ''' - client.save({CONANFILE: cf}, clean_first=True) + + # First apply just the default profile, we should get the env MYVAR="from_build_require" + client.save({CONANFILE: cf % "from_build_require"}, clean_first=True) + client.run("export lasote/stable") + client.run('install mypackage/0.1@lasote/stable --build missing') + + # Then declare in the default profile the var, it should be prioritized from the br + default_profile_2 = default_profile + "\n[env]\nMyVAR=23" + save(client.client_cache.default_profile_path, default_profile_2) + client.save({CONANFILE: cf % "23"}, clean_first=True) client.run("export lasote/stable") client.run('install mypackage/0.1@lasote/stable --build missing') diff --git a/conans/test/functional/deploy_test.py b/conans/test/functional/deploy_test.py new file mode 100644 index 00000000000..731d1a78b20 --- /dev/null +++ b/conans/test/functional/deploy_test.py @@ -0,0 +1,70 @@ +import unittest + +from conans.test.utils.tools import TestClient +from conans.test.utils.test_files import temp_folder +import os +from conans.model.manifest import FileTreeManifest +from conans.util.files import load, mkdir +from nose_parameterized.parameterized import parameterized + + +class DeployTest(unittest.TestCase): + @parameterized.expand([(True, ), (False, )]) + def deploy_test(self, deploy_to_abs): + client = TestClient() + libconanfile = """from conans import ConanFile +from conans.tools import save +class Lib(ConanFile): + exports_sources = "*" + def build(self): + save("mylib.dll", "mydll") + def package(self): + self.copy("*") + def deploy(self): + self.output.info("Lib deploy()") +""" + client.save({"conanfile.py": libconanfile, + "License.md": "lib license", + "otherfile": ""}) + client.run("create Lib/0.1@user/testing") + self.assertNotIn("Lib deploy()", client.out) + + if deploy_to_abs: + dll_folder = temp_folder() + mkdir(dll_folder) + else: + dll_folder = "" + conanfile = """from conans import ConanFile +from conans.tools import save +class Pkg(ConanFile): + requires = "Lib/0.1@user/testing" + def build(self): + save("myapp.exe", "myexe") + def package(self): + self.copy("*") + def deploy(self): + self.output.info("Pkg deploy()") + self.copy("*.exe") + self.copy_deps("*.dll", dst="%s") +""" % dll_folder.replace("\\", "/") + client.save({"conanfile.py": conanfile}) + client.run("create Pkg/0.1@user/testing") + self.assertNotIn("deploy()", client.out) + client.current_folder = temp_folder() + client.run("install Pkg/0.1@user/testing") + self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.dll' files: mylib.dll", + client.out) + self.assertIn("Pkg/0.1@user/testing deploy(): Copied 1 '.exe' files: myapp.exe", + client.out) + deploy_manifest = FileTreeManifest.loads(load(os.path.join(client.current_folder, + "deploy_manifest.txt"))) + + app = os.path.join(client.current_folder, "myapp.exe") + if deploy_to_abs: + lib = os.path.join(dll_folder, "mylib.dll") + else: + lib = os.path.join(client.current_folder, "mylib.dll") + self.assertEqual(sorted([app, lib]), + sorted(deploy_manifest.file_sums.keys())) + self.assertEqual(load(app), "myexe") + self.assertEqual(load(lib), "mydll") diff --git a/conans/test/functional/exit_with_code_test.py b/conans/test/functional/exit_with_code_test.py index 743b88af8c0..c0c52600a5e 100644 --- a/conans/test/functional/exit_with_code_test.py +++ b/conans/test/functional/exit_with_code_test.py @@ -22,7 +22,7 @@ def build(self): client = TestClient() files = {CONANFILE: base} client.save(files) - client.run("install -g txt") - error_code = client.run("build", ignore_error=True) + client.run("install") + error_code = client.run("build .", ignore_error=True) self.assertEquals(error_code, 34) self.assertIn("Exiting with code: 34", client.user_io.out) diff --git a/conans/test/functional/folders_access_test.py b/conans/test/functional/folders_access_test.py new file mode 100644 index 00000000000..5a817e7b22e --- /dev/null +++ b/conans/test/functional/folders_access_test.py @@ -0,0 +1,226 @@ +import unittest + +import os + +from conans.test.utils.tools import TestClient +from conans.util.files import mkdir + +conanfile_parent = """ +from conans import ConanFile + + +class parentLib(ConanFile): + + name = "parent" + version = "1.0" + + def package_info(self): + self.cpp_info.cppflags.append("-myflag") + self.user_info.MyVar = "MyVarValue" + self.env_info.MyEnvVar = "MyEnvVarValue" + +""" + + +conanfile = """ +import os +from conans import ConanFile + +class AConan(ConanFile): + name = "lib" + version = "1.0" + + # To save the folders and check later if the folder is the same + copy_build_folder = None + copy_source_folder = None + copy_package_folder = None + + no_copy_source = %(no_copy_source)s + requires = "parent/1.0@conan/stable" + running_local_command = %(local_command)s + + def assert_in_local_cache(self): + if self.running_local_command: + assert(self.in_local_cache == False) + + def source(self): + assert(self.source_folder == os.getcwd()) + self.assert_in_local_cache() + + # Prevented to use them, it's dangerous, because the source is run only for the first + # config, so only the first build_folder/package_folder would be modified + assert(self.build_folder is None) + assert(self.package_folder is None) + + assert(self.source_folder is not None) + self.copy_source_folder = self.source_folder + + if %(source_with_infos)s: + self.assert_deps_infos() + + def assert_deps_infos(self): + assert(self.deps_user_info["parent"].MyVar == "MyVarValue") + assert(self.deps_cpp_info["parent"].cppflags[0] == "-myflag") + assert(self.deps_env_info["parent"].MyEnvVar == "MyEnvVarValue") + + def build(self): + assert(self.build_folder == os.getcwd()) + + self.assert_in_local_cache() + self.assert_deps_infos() + + if self.no_copy_source and self.in_local_cache: + assert(self.copy_source_folder == self.source_folder) # Only in install + assert(self.install_folder == self.build_folder) + else: + assert(self.source_folder == self.build_folder) + self.install_folder + + assert(self.package_folder is not None) + self.copy_build_folder = self.build_folder + + def package(self): + assert(self.build_folder == os.getcwd()) # Folder where we copy things to destination + + self.assert_in_local_cache() + self.assert_deps_infos() + + + if self.in_local_cache: + assert(self.copy_build_folder == self.build_folder) + + if self.no_copy_source and self.in_local_cache: + assert(self.copy_source_folder == self.source_folder) # Only in install + else: + assert(self.source_folder == self.build_folder) + + self.copy_package_folder = self.package_folder + + def package_info(self): + assert(self.package_folder == os.getcwd()) + assert(self.in_local_cache == True) + + if self.no_copy_source: + assert(self.copy_source_folder == self.source_folder) + else: + assert(self.source_folder == self.build_folder) + + assert(self.copy_package_folder == self.package_folder) + assert(self.copy_build_folder == self.build_folder) + + def imports(self): + assert(self.imports_folder == os.getcwd()) + +""" + + +class TestFoldersAccess(unittest.TestCase): + """"Tests the presence of self.source_folder, self.build_folder, self.package_folder + in the conanfile methods. Also the availability of the self.deps_cpp_info, self.deps_user_info + and self.deps_env_info. Also the 'in_local_cache' variable. """ + + def setUp(self): + self.client = TestClient() + self.client.save({"conanfile.py": conanfile_parent}) + self.client.run("export conan/stable") + + def source_local_command_test(self): + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("source .") + + c1 = conanfile % {"no_copy_source": True, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("source .") + + c1 = conanfile % {"no_copy_source": False, "source_with_infos": True, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + error = self.client.run("source .", ignore_error=True) + self.assertTrue(error) + self.assertIn("self.deps_user_info not defined. If you need it for a " + "local command run 'conan install'", self.client.out) + + # Now use infos to get the deps_cpp_info + self.client.run("install . --build missing") + self.client.run("source .") # Default folder, not needed to specify --install-folder + + # Install in different location + c1 = conanfile % {"no_copy_source": False, "source_with_infos": True, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + old_dir = self.client.current_folder + build_dir = os.path.join(self.client.current_folder, "build1") + mkdir(build_dir) + self.client.current_folder = build_dir + self.client.run("install .. ") + self.client.current_folder = old_dir + self.client.run("source . --install-folder=build1") + + def build_local_command_test(self): + + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + error = self.client.run("build .", ignore_error=True) + self.assertTrue(error) + self.assertIn("ERROR: conanbuildinfo.txt file not found", self.client.out) + + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("install . --build missing") + self.client.run("build .") + + c1 = conanfile % {"no_copy_source": True, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("install . --build missing") + self.client.run("build .") + + def package_local_command_test(self): + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + error = self.client.run("package .", ignore_error=True) + self.assertTrue(error) + self.assertIn("ERROR: conanbuildinfo.txt file not found", self.client.out) + + self.client.run("install . --build missing") + + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("install . --build missing") + self.client.run("package .") + + def imports_local_test(self): + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": True} + self.client.save({"conanfile.py": c1}, clean_first=True) + error = self.client.run("imports .", ignore_error=True) + self.assertTrue(error) + self.assertIn("ERROR: conanbuildinfo.txt file not found", self.client.out) + + def full_install_test(self): + c1 = conanfile % {"no_copy_source": False, "source_with_infos": False, + "local_command": False} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("create conan/stable --build") + + c1 = conanfile % {"no_copy_source": True, "source_with_infos": False, + "local_command": False} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("create conan/stable --build") + + c1 = conanfile % {"no_copy_source": False, "source_with_infos": True, + "local_command": False} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("create conan/stable --build") + + c1 = conanfile % {"no_copy_source": True, "source_with_infos": True, + "local_command": False} + self.client.save({"conanfile.py": c1}, clean_first=True) + self.client.run("create conan/stable --build") diff --git a/conans/test/functional/in_local_cache_test.py b/conans/test/functional/in_local_cache_test.py index 61971d51790..71059445463 100644 --- a/conans/test/functional/in_local_cache_test.py +++ b/conans/test/functional/in_local_cache_test.py @@ -33,7 +33,7 @@ def test_in_local_cache_flag(self): client = TestClient() client.save({CONANFILE: conanfile}) client.run("install .") - client.run("build") + client.run("build .") self.assertIn("build() IN LOCAL CACHE=> False", client.user_io.out) pack_folder = os.path.join(client.current_folder, "package") diff --git a/conans/test/functional/package_tester_test.py b/conans/test/functional/package_tester_test.py new file mode 100644 index 00000000000..f8a2f42cfc8 --- /dev/null +++ b/conans/test/functional/package_tester_test.py @@ -0,0 +1,64 @@ +import unittest + +from conans.client.package_tester import PackageTester +from conans.errors import ConanException +from conans.model.ref import ConanFileReference +from conans.model.requires import Requirement + + +class PackageTesterTest(unittest.TestCase): + + def unit_test_get_reference_to_test(self): + + obj = PackageTester(None, None) + + # No requires in the test_package/conanfile.py, specified parameters + requires = {} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # One require, with same info that we are passing + requires = {"lib": Requirement(ConanFileReference.loads("lib/1.0@user/channel"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # One require, with same info (Except user and channel) + requires = {"lib": Requirement(ConanFileReference.loads("lib/1.0@user2/channel2"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # One require, for a different library + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # Two requires, for different libraries + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2")), + "lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # Two requires, one matching + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2")), + "lib": Requirement(ConanFileReference.loads("lib/1.0@user2/channel2"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", "user", "channel") + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user/channel")) + + # Two requires, one matching, no specifing user/channel + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2")), + "lib": Requirement(ConanFileReference.loads("lib/1.0@user2/channel2"))} + ref = obj._get_reference_to_test(requires, "lib", "1.0", None, None) + self.assertEquals(ref, ConanFileReference.loads("lib/1.0@user2/channel2")) + + # Two requires, one matching, no specifing lib + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2")), + "lib": Requirement(ConanFileReference.loads("lib/1.0@user2/channel2"))} + with self.assertRaisesRegexp(ConanException, "Cannot deduce the reference to be tested"): + obj._get_reference_to_test(requires, None, None, None, None) + + # Library missmatching version + requires = {"lib2": Requirement(ConanFileReference.loads("lib2/1.0@user2/channel2")), + "lib": Requirement(ConanFileReference.loads("lib/1.0@user2/channel2"))} + with self.assertRaisesRegexp(ConanException, "he specified version doesn't match"): + obj._get_reference_to_test(requires, "lib", "1.2", None, None) + diff --git a/conans/test/functional/path_limit_test.py b/conans/test/functional/path_limit_test.py index a7eab70fba9..3dc38db68ce 100644 --- a/conans/test/functional/path_limit_test.py +++ b/conans/test/functional/path_limit_test.py @@ -3,10 +3,10 @@ from conans.util.files import load import os from conans.model.ref import PackageReference, ConanFileReference -import platform from conans.tools import environment_append from conans.test import CONAN_TEST_FOLDER import tempfile +from conans.paths import long_paths_support base = ''' @@ -70,9 +70,9 @@ def upload_test(self): client.run("search") self.assertIn("There are no packages", client.user_io.out) - for download in ("", "--all"): + for command in ("install", "download"): client2 = TestClient(servers=servers, users={"default": [("lasote", "mypass")]}) - client2.run("install lib/0.1@lasote/channel %s" % download) + client2.run("%s lib/0.1@lasote/channel" % command) reference = ConanFileReference.loads("lib/0.1@lasote/channel") export_folder = client2.client_cache.export(reference) export_files = os.listdir(export_folder) @@ -80,7 +80,7 @@ def upload_test(self): package_ref = PackageReference.loads("lib/0.1@lasote/channel:" "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") package_folder = client2.client_cache.package(package_ref, short_paths=None) - if platform.system() == "Windows": + if not long_paths_support: original_folder = client2.client_cache.package(package_ref) link = load(os.path.join(original_folder, ".conan_link")) self.assertEqual(link, package_folder) @@ -97,7 +97,7 @@ def export_source_test(self): client.save(files) client.run("export user/channel") conan_ref = ConanFileReference.loads("lib/0.1@user/channel") - if platform.system() == "Windows": + if not long_paths_support: source_folder = client.client_cache.export_sources(conan_ref) link_source = load(os.path.join(source_folder, ".conan_link")) self.assertTrue(os.path.exists(link_source)) @@ -111,31 +111,6 @@ def export_source_test(self): self.assertTrue(os.path.exists(link_source)) self.assertEqual(load(os.path.join(link_source + "/file0.txt")), "file0 content") - def source_test(self): - client = TestClient() - files = {"conanfile.py": base} - client.save(files) - client.run("export user/channel") - - conan_ref = ConanFileReference.loads("lib/0.1@user/channel") - client.run("source lib/0.1@user/channel") - self.assertIn("Configuring sources", client.user_io.out) - - if platform.system() == "Windows": - source_folder = client.client_cache.source(conan_ref) - link_source = load(os.path.join(source_folder, ".conan_link")) - self.assertTrue(os.path.exists(link_source)) - - # Nothing changes, so source is still there - client.run("export user/channel") - client.run("source lib/0.1@user/channel") - self.assertNotIn("Configuring sources", client.user_io.out) - - # But if we remove the source, it will retrieve sources again - client.run("remove lib/0.1@user/channel -s -f") - client.run("source lib/0.1@user/channel") - self.assertIn("Configuring sources", client.user_io.out) - def package_copier_test(self): client = TestClient() files = {"conanfile.py": base} @@ -151,7 +126,7 @@ def package_copier_test(self): client.run("search lib/0.1@memsharded/stable") self.assertIn("Package_ID: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.user_io.out) - if platform.system() == "Windows": + if not long_paths_support: conan_ref = ConanFileReference.loads("lib/0.1@lasote/channel") package_ref = PackageReference(conan_ref, "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") package_folder = client.client_cache.package(package_ref) @@ -177,7 +152,7 @@ def basic_test(self): file2 = load(os.path.join(package_folder, "myfile2.txt")) self.assertEqual("Hello2 extra path length", file2) - if platform.system() == "Windows": + if not long_paths_support: conan_ref = ConanFileReference.loads("lib/0.1@user/channel") source_folder = client.client_cache.source(conan_ref) link_source = load(os.path.join(source_folder, ".conan_link")) @@ -204,9 +179,7 @@ def basic_disabled_test(self): class ConanLib(ConanFile): short_paths = True ''' - files = {"conanfile.py": base} - client.save(files) - client.client_cache.conan_config.set_item("general.user_home_short", "None") + client.save({"conanfile.py": base}) client.run("create lib/0.1@user/channel") package_ref = PackageReference.loads("lib/0.1@user/channel:" @@ -218,16 +191,19 @@ class ConanLib(ConanFile): conan_ref = ConanFileReference.loads("lib/0.1@user/channel") source_folder = client.client_cache.source(conan_ref) - link_source = os.path.join(source_folder, ".conan_link") - self.assertFalse(os.path.exists(link_source)) - build_folder = client.client_cache.build(package_ref) - link_build = os.path.join(build_folder, ".conan_link") - self.assertFalse(os.path.exists(link_build)) - package_folder = client.client_cache.package(package_ref) + link_source = os.path.join(source_folder, ".conan_link") + link_build = os.path.join(build_folder, ".conan_link") link_package = os.path.join(package_folder, ".conan_link") - self.assertFalse(os.path.exists(link_package)) + if long_paths_support: + self.assertFalse(os.path.exists(link_source)) + self.assertFalse(os.path.exists(link_build)) + self.assertFalse(os.path.exists(link_package)) + else: + self.assertTrue(os.path.exists(link_source)) + self.assertTrue(os.path.exists(link_build)) + self.assertTrue(os.path.exists(link_package)) def failure_test(self): diff --git a/conans/test/functional/profile_loader_test.py b/conans/test/functional/profile_loader_test.py index 87e1c461333..c8d17e70a21 100644 --- a/conans/test/functional/profile_loader_test.py +++ b/conans/test/functional/profile_loader_test.py @@ -2,6 +2,7 @@ import unittest from collections import OrderedDict +from conans import tools from conans.model.profile import Profile from conans.model.ref import ConanFileReference @@ -12,12 +13,20 @@ from conans.model.env_info import EnvValues from conans.paths import CONANFILE from conans.test.utils.cpp_test_files import cpp_hello_conan_files -from conans.test.utils.profiles import create_profile +from conans.test.utils.profiles import create_profile as _create_profile from conans.test.utils.test_files import temp_folder from conans.test.utils.tools import TestClient from conans.util.files import save, load +def create_profile(folder, name, settings=None, scopes=None, package_settings=None, env=None, + package_env=None, options=None): + _create_profile(folder, name, settings, scopes, package_settings, env, package_env, options) + content = load(os.path.join(folder, name)) + content = "include(default)\n" + content + save(os.path.join(folder, name), content) + + class ProfileParserTest(unittest.TestCase): def test_parser(self): @@ -267,6 +276,7 @@ def bad_syntax_test(self): self.assertIn("'a value' is not a valid 'settings.os'", self.client.user_io.out) profile = ''' + include(default) [env] ENV_VAR = a value ''' @@ -274,7 +284,7 @@ def bad_syntax_test(self): self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) self._assert_env_variable_printed("ENV_VAR", "a value") - profile = ''' + profile = '''include(default) # Line with comments is not a problem [env] # Not even here @@ -334,6 +344,9 @@ def install_profile_settings_test(self): create_profile(self.client.client_cache.profiles_path, "vs_12_86", settings=profile_settings, package_settings={}) + tools.replace_in_file(self.client.client_cache.default_profile_path, + "compiler.libcxx", "#compiler.libcxx", strict=False) + self.client.save(files) self.client.run("export lasote/stable") self.client.run("install --build missing -pr vs_12_86") diff --git a/conans/test/functional/read_only_test.py b/conans/test/functional/read_only_test.py new file mode 100644 index 00000000000..dd5e03082e4 --- /dev/null +++ b/conans/test/functional/read_only_test.py @@ -0,0 +1,59 @@ +import unittest +from conans.test.utils.tools import TestClient, TestServer +from conans.util.files import save +import os +from conans.model.ref import ConanFileReference, PackageReference + + +class ReadOnlyTest(unittest.TestCase): + + def setUp(self): + self.test_server = TestServer() + client = TestClient(servers={"default": self.test_server}, + users={"default": [("lasote", "mypass")]}) + client.run("--version") + client.run("config set general.read_only_cache=True") + conanfile = """from conans import ConanFile +class MyPkg(ConanFile): + exports_sources = "*.h" + def package(self): + self.copy("*") +""" + client.save({"conanfile.py": conanfile, + "myheader.h": "my header"}) + client.run("create Pkg/0.1@lasote/channel") + self.client = client + + def basic_test(self): + ref = PackageReference(ConanFileReference.loads("Pkg/0.1@lasote/channel"), + "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") + path = os.path.join(self.client.client_cache.package(ref), "myheader.h") + with self.assertRaises(IOError): + save(path, "Bye World") + os.chmod(path, 0o777) + save(path, "Bye World") + + def remove_test(self): + self.client.run("search") + self.assertIn("Pkg/0.1@lasote/channel", self.client.out) + self.client.run("remove Pkg* -f") + self.assertNotIn("Pkg/0.1@lasote/channel", self.client.out) + + def upload_test(self): + self.client.run("upload * --all --confirm") + self.client.run("remove Pkg* -f") + self.client.run("install Pkg/0.1@lasote/channel") + self.basic_test() + + def upload_change_test(self): + self.client.run("upload * --all --confirm") + client = TestClient(servers={"default": self.test_server}, + users={"default": [("lasote", "mypass")]}) + client.run("install Pkg/0.1@lasote/channel") + ref = PackageReference(ConanFileReference.loads("Pkg/0.1@lasote/channel"), + "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") + path = os.path.join(client.client_cache.package(ref), "myheader.h") + with self.assertRaises(IOError): + save(path, "Bye World") + os.chmod(path, 0o777) + save(path, "Bye World") diff --git a/conans/test/functional/runner_test.py b/conans/test/functional/runner_test.py index 9edf99cd53d..f0d250326d9 100644 --- a/conans/test/functional/runner_test.py +++ b/conans/test/functional/runner_test.py @@ -13,7 +13,7 @@ def _install_and_build(self, conanfile_text, runner=None): self.assertFalse(os.path.exists(test_folder)) client.save(files) client.run("install") - client.run("build") + client.run("build .") return client def basic_test(self): @@ -119,7 +119,7 @@ def build(self): self.assertFalse(os.path.exists(test_folder)) client.save(files) client.run("install") - client.run("build") + client.run("build .") self.assertTrue(os.path.exists(test_folder)) def cwd_error_test(self): @@ -141,7 +141,7 @@ def build(self): self.assertFalse(os.path.exists(test_folder)) client.save(files) client.run("install") - error = client.run("build", ignore_error=True) + error = client.run("build .", ignore_error=True) self.assertTrue(error) self.assertIn("Error while executing 'mkdir test_folder'", client.user_io.out) self.assertFalse(os.path.exists(test_folder)) diff --git a/conans/test/functional/user_info_test.py b/conans/test/functional/user_info_test.py index cd510673c43..44055f26de4 100644 --- a/conans/test/functional/user_info_test.py +++ b/conans/test/functional/user_info_test.py @@ -65,6 +65,7 @@ def build(self): self.assertIn("[USER_LIB_D]%svar1=2" % os.linesep, txt_contents) # Now try local command with a consumer - client.run('install . --build -g txt') - client.run("build") + client.run('install . --build') + client.run("build .") + diff --git a/conans/test/generators/pkg_config_test.py b/conans/test/generators/pkg_config_test.py new file mode 100644 index 00000000000..372fc541783 --- /dev/null +++ b/conans/test/generators/pkg_config_test.py @@ -0,0 +1,57 @@ +import unittest + +from conans.client.generators.pkg_config import PkgConfigGenerator +from conans.model.settings import Settings +from conans.model.conan_file import ConanFile +from conans.model.build_info import CppInfo +from conans.model.ref import ConanFileReference + + +class PkgGeneratorTest(unittest.TestCase): + + def variables_setup_test(self): + + conanfile = ConanFile(None, None, Settings({}), None) + + ref = ConanFileReference.loads("MyPkg/0.1@lasote/stables") + cpp_info = CppInfo("dummy_root_folder1") + cpp_info.defines = ["MYDEFINE1"] + cpp_info.cflags.append("-Flag1=23") + cpp_info.version = "1.3" + cpp_info.description = "My cool description" + + conanfile.deps_cpp_info.update(cpp_info, ref.name) + ref = ConanFileReference.loads("MyPkg2/0.1@lasote/stables") + cpp_info = CppInfo("dummy_root_folder2") + cpp_info.defines = ["MYDEFINE2"] + cpp_info.version = "2.3" + cpp_info.exelinkflags = ["-exelinkflag"] + cpp_info.sharedlinkflags = ["-sharedlinkflag"] + cpp_info.cppflags = ["-cppflag"] + cpp_info.public_deps = ["MyPkg"] + conanfile.deps_cpp_info.update(cpp_info, ref.name) + generator = PkgConfigGenerator(conanfile) + files = generator.content + + self.assertEquals(files["MyPkg2.pc"], """prefix=dummy_root_folder2 +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: MyPkg2 +Description: Conan package: MyPkg2 +Version: 2.3 +Libs: -L${libdir} -sharedlinkflag -exelinkflag +Cflags: -I${includedir} -cppflag -DMYDEFINE2 +Requires: MyPkg +""") + + self.assertEquals(files["MyPkg.pc"], """prefix=dummy_root_folder1 +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: MyPkg +Description: My cool description +Version: 1.3 +Libs: -L${libdir} +Cflags: -I${includedir} -Flag1=23 -DMYDEFINE1 +""") diff --git a/conans/test/generators/visual_studio_multi_test.py b/conans/test/generators/visual_studio_multi_test.py new file mode 100644 index 00000000000..e3079e0cf83 --- /dev/null +++ b/conans/test/generators/visual_studio_multi_test.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import os +from nose.plugins.attrib import attr +from conans.model.settings import Settings +from conans.model.conan_file import ConanFile +from conans.model.build_info import CppInfo +from conans.model.ref import ConanFileReference +from conans.client.conf import default_settings_yml + +from conans.client.generators import VisualStudioMultiGenerator +from conans.tools import chdir +from conans.test.utils.test_files import temp_folder + + +@attr('visual_studio') +class VisualStudioMultiGeneratorTest(unittest.TestCase): + def valid_xml_test(self): + + tempdir = temp_folder() + with chdir(tempdir): + settings = Settings.loads(default_settings_yml) + settings.os = "Windows" + settings.compiler = "Visual Studio" + settings.compiler.version = "12" + settings.compiler.runtime = "MD" + conanfile = ConanFile(None, None, Settings({}), None) + + ref = ConanFileReference.loads("MyPkg/0.1@user/testing") + cpp_info = CppInfo("dummy_root_folder1") + conanfile.deps_cpp_info.update(cpp_info, ref.name) + ref = ConanFileReference.loads("My.Fancy-Pkg_2/0.1@user/testing") + cpp_info = CppInfo("dummy_root_folder2") + conanfile.deps_cpp_info.update(cpp_info, ref.name) + + settings.arch = "x86" + settings.build_type = "Debug" + settings.compiler.version = "12" + conanfile.settings = settings + + generator = VisualStudioMultiGenerator(conanfile) + generator.output_path = "" + content = generator.content + + self.assertEqual(2, len(content)) + self.assertIn('conanbuildinfo_multi.props', content.keys()) + self.assertIn('conanbuildinfo_debug_win32_12.props', content.keys()) + + content_multi = content['conanbuildinfo_multi.props'] + self.assertIn("", content_multi) + + with open('conanbuildinfo_multi.props', 'w') as f: + f.write(content_multi) + + settings.arch = "x86_64" + settings.build_type = "Release" + settings.compiler.version = "14" + conanfile.settings = settings + + generator = VisualStudioMultiGenerator(conanfile) + generator.output_path = "" + content = generator.content + + self.assertEqual(2, len(content)) + self.assertIn('conanbuildinfo_multi.props', content.keys()) + self.assertIn('conanbuildinfo_release_x64_14.props', content.keys()) + + content_multi = content['conanbuildinfo_multi.props'] + self.assertIn("", content_multi) + self.assertIn("", content_multi) + + os.unlink('conanbuildinfo_multi.props') diff --git a/conans/test/generators/xcode_gcc_vs_test.py b/conans/test/generators/xcode_gcc_vs_test.py new file mode 100644 index 00000000000..098f01d5325 --- /dev/null +++ b/conans/test/generators/xcode_gcc_vs_test.py @@ -0,0 +1,98 @@ +import unittest +from conans.model.ref import ConanFileReference, PackageReference + +from conans.paths import (CONANFILE_TXT, BUILD_INFO_CMAKE, BUILD_INFO_GCC, CONANINFO, + BUILD_INFO_VISUAL_STUDIO, BUILD_INFO_XCODE, BUILD_INFO) +from conans.util.files import load +import os +from conans.test.utils.tools import TestClient + + +class VSXCodeGeneratorsTest(unittest.TestCase): + + def generators_test(self): + conan_ref = ConanFileReference.loads("Hello/0.1@lasote/stable") + client = TestClient() + client.save({"conanfile.py": """from conans import ConanFile +import os +class Pkg(ConanFile): + def package(self): + os.makedirs(os.path.join(self.package_folder, "lib")) + os.makedirs(os.path.join(self.package_folder, "include")) + def package_info(self): + self.cpp_info.libs = ["hello"] + self.cpp_info.cppflags = ["-some_cpp_compiler_flag"] + self.cpp_info.cflags = ["-some_c_compiler_flag"] +"""}) + client.run("export Hello/0.1@lasote/stable") + conanfile_txt = '''[requires] +Hello/0.1@lasote/stable # My req comment +[generators] +gcc # I need this generator for.. +cmake +visual_studio +xcode +''' + client.save({"conanfile.txt": conanfile_txt}, clean_first=True) + + # Install requirements + client.run('install --build missing') + self.assertEqual(sorted([CONANFILE_TXT, BUILD_INFO_GCC, BUILD_INFO_CMAKE, + BUILD_INFO_VISUAL_STUDIO, BUILD_INFO, + BUILD_INFO_XCODE, CONANINFO]), + sorted(os.listdir(client.current_folder))) + + cmake = load(os.path.join(client.current_folder, BUILD_INFO_CMAKE)) + gcc = load(os.path.join(client.current_folder, BUILD_INFO_GCC)) + + self.assertIn("CONAN_INCLUDE_DIRS", cmake) + self.assertIn("CONAN_LIB_DIRS", cmake) + self.assertIn("CONAN_LIBS", cmake) + + self.assertIn("CONAN_INCLUDE_DIRS", cmake) + self.assertIn("CONAN_LIB_DIRS", cmake) + self.assertIn(".conan/data/Hello/0.1/lasote/stable/package", cmake) + + self.assertIn("-L", gcc) + self.assertIn("-l", gcc) + self.assertIn("-I", gcc) + + self.assertIn(".conan/data/Hello/0.1/lasote/stable/package", gcc) + + # CHECK VISUAL STUDIO GENERATOR + + from xml.dom import minidom + xmldoc = minidom.parse(os.path.join(client.current_folder, BUILD_INFO_VISUAL_STUDIO)) + definition_group = xmldoc.getElementsByTagName('ItemDefinitionGroup')[0] + compiler = definition_group.getElementsByTagName("ClCompile")[0] + + include_dirs = compiler.getElementsByTagName("AdditionalIncludeDirectories")[0].firstChild.data + definitions = compiler.getElementsByTagName("PreprocessorDefinitions")[0].firstChild.data + + linker = definition_group.getElementsByTagName("Link")[0] + lib_dirs = linker.getElementsByTagName("AdditionalLibraryDirectories")[0].firstChild.data + libs = linker.getElementsByTagName("AdditionalDependencies")[0].firstChild.data + + package_id = os.listdir(client.paths.packages(conan_ref))[0] + package_ref = PackageReference(conan_ref, package_id) + package_paths = client.paths.package(package_ref).replace("\\", "/") + + expected_lib_dirs = os.path.join(package_paths, "lib").replace("\\", "/") + expected_include_dirs = os.path.join(package_paths, "include").replace("\\", "/") + + self.assertIn(expected_lib_dirs, lib_dirs) + self.assertEquals("hello.lib;%(AdditionalDependencies)", libs) + self.assertEquals("%(PreprocessorDefinitions)", definitions) + self.assertIn(expected_include_dirs, include_dirs) + + # CHECK XCODE GENERATOR + xcode = load(os.path.join(client.current_folder, BUILD_INFO_XCODE)) + + expected_c_flags = '-some_c_compiler_flag' + expected_cpp_flags = '-some_cpp_compiler_flag' + + self.assertIn('LIBRARY_SEARCH_PATHS = $(inherited) "%s"' % expected_lib_dirs, xcode) + self.assertIn('HEADER_SEARCH_PATHS = $(inherited) "%s"' % expected_include_dirs, xcode) + self.assertIn("GCC_PREPROCESSOR_DEFINITIONS = $(inherited)", xcode) + self.assertIn('OTHER_CFLAGS = $(inherited) %s' % expected_c_flags, xcode) + self.assertIn('OTHER_CPLUSPLUSFLAGS = $(inherited) %s' % expected_cpp_flags, xcode) diff --git a/conans/test/integration/basic_build_test.py b/conans/test/integration/basic_build_test.py index 90ab4b70073..9e22d4f3c7c 100644 --- a/conans/test/integration/basic_build_test.py +++ b/conans/test/integration/basic_build_test.py @@ -21,7 +21,7 @@ def _build(self, cmd, static, pure_c, use_cmake, lang): client.save(files) client.run(cmd) - client.run('build') + client.run('build .') ld_path = ("LD_LIBRARY_PATH=`pwd`" if not static and not platform.system() == "Windows" else "") command = os.sep.join([".", "bin", "say_hello"]) @@ -45,7 +45,8 @@ def build_default_test(self): "build default (gcc in nix, VS in win)" if platform.system() == "SunOS": return # If is using sun-cc the gcc generator doesn't work - for cmd, lang, static, pure_c in [("install -g txt", 0, True, True), + + for cmd, lang, static, pure_c in [("install", 0, True, True), ("install -o language=1 -o static=False -g txt", 1, False, False)]: self._build(cmd, static, pure_c, use_cmake=False, lang=lang) diff --git a/conans/test/integration/build_id_test.py b/conans/test/integration/build_id_test.py index 85153020e61..ec892038175 100644 --- a/conans/test/integration/build_id_test.py +++ b/conans/test/integration/build_id_test.py @@ -137,18 +137,6 @@ def basic_test(self, python_consumer): self.assertEqual("Release file1", content) self._check_conaninfo(client) - def package_error_test(self): - client = TestClient() - - client.save({"conanfile.py": conanfile}) - client.run("export user/channel") - client.save({"conanfile.txt": consumer}, clean_first=True) - client.run('install -s build_type=Debug') - error = client.run("package Pkg/0.1@user/channel", ignore_error=True) - self.assertTrue(error) - self.assertIn("ERROR: package command does not support recipes with 'build_id'", - client.user_io.out) - def remove_specific_builds_test(self): client = TestClient() client.save({"conanfile.py": conanfile}) diff --git a/conans/test/integration/case_sensitive_test.py b/conans/test/integration/case_sensitive_test.py index 6bb0ef13346..bac9be3c7c2 100644 --- a/conans/test/integration/case_sensitive_test.py +++ b/conans/test/integration/case_sensitive_test.py @@ -57,15 +57,19 @@ def imports_test(self): client.run("export lasote/stable") client.run("install Hello0/0.1@lasote/stable --build=missing") error = client.run("imports hello0/0.1@lasote/stable", ignore_error=True) - self._check(error, client) + self.assertTrue(error) + # Reference interpreted as a path, so no valid path + self.assertIn("Parameter 'path' cannot be a reference", client.out) def package_test(self): client = TestClient() client.save({CONANFILE: conanfile}) client.run("export lasote/stable") client.run("install Hello0/0.1@lasote/stable --build=missing") - error = client.run("package hello0/0.1@lasote/stable", ignore_error=True) - self._check(error, client) + error = client.run("export-pkg . hello0/0.1@lasote/stable", ignore_error=True) + self.assertTrue(error) + self.assertIn("Specified name/version doesn't match with the name/version in " + "the conanfile", client.out) def copy_test(self): client = TestClient() @@ -74,10 +78,3 @@ def copy_test(self): client.run("install Hello0/0.1@lasote/stable --build=missing") error = client.run("copy hello0/0.1@lasote/stable otheruser/testing", ignore_error=True) self._check(error, client) - - def source_test(self): - client = TestClient() - client.save({CONANFILE: conanfile}) - client.run("export lasote/stable") - error = client.run("source hello0/0.1@lasote/stable", ignore_error=True) - self._check(error, client) diff --git a/conans/test/integration/cmake_configs_test.py b/conans/test/integration/cmake_configs_test.py index f68385e3171..95c386413b1 100644 --- a/conans/test/integration/cmake_configs_test.py +++ b/conans/test/integration/cmake_configs_test.py @@ -30,7 +30,7 @@ def cmake_multi_test(self): client.run("export lasote/stable") client.run('install . --build missing') - client.run("build") + client.run("build .") cmd = os.sep.join([".", "bin", "say_hello"]) client.runner(cmd, cwd=client.current_folder) self.assertIn("Hello Release Hello2 Hello Release Hello1 Hello Release Hello0", diff --git a/conans/test/integration/cmake_flags_test.py b/conans/test/integration/cmake_flags_test.py index 2961a71d0d0..8797eaae345 100644 --- a/conans/test/integration/cmake_flags_test.py +++ b/conans/test/integration/cmake_flags_test.py @@ -173,8 +173,9 @@ def build(self): for settings_line in ('', 'settings="arch"', 'settings="compiler"'): client = TestClient() client.save({"conanfile.py": conanfile % settings_line}) - client.run("install -g txt") - client.run("build", ignore_error=True) + client.run("install") + client.run("build .", ignore_error=True) + self.assertIn("You must specify compiler, compiler.version and arch in " "your settings to use a CMake generator", client.user_io.out,) @@ -199,14 +200,14 @@ def build(self): """ client = TestClient() client.save({"conanfile.py": conanfile % "True"}) - client.run("build", ignore_error=True) + client.run("build .", ignore_error=True) - self.assertIn("self.deps_cpp_info not defined. If you need it for a local command run " - "'conan install -g txt'", client.user_io.out) + self.assertIn("conanbuildinfo.txt file not found", client.user_io.out) - client.run("install -g txt") - client.run("build") + client.run("install") + client.run("build .") client.save({"conanfile.py": conanfile % "False"}, clean_first=True) - client.run("install -g txt") - client.run("build") + client.run("install") + client.run("build .") + diff --git a/conans/test/integration/cmake_install_package_test.py b/conans/test/integration/cmake_install_package_test.py index c105f7bacbe..aca37d8c25f 100644 --- a/conans/test/integration/cmake_install_package_test.py +++ b/conans/test/integration/cmake_install_package_test.py @@ -38,6 +38,6 @@ def test(self): "test/conanfile.py": test_conanfile, "CMakeLists.txt": cmake, "header.h": "my header h!!"}) - client.run("test_package") + client.run("create user/channel") self.assertIn("Test/0.1@user/channel test package: Content: my header h!!", client.user_io.out) diff --git a/conans/test/integration/complete_test.py b/conans/test/integration/complete_test.py index bded74486d0..b90ca6cb35e 100644 --- a/conans/test/integration/complete_test.py +++ b/conans/test/integration/complete_test.py @@ -84,7 +84,7 @@ def reuse_test(self): files3 = cpp_hello_conan_files("Hello1", "0.1", ["Hello0/0.1@lasote/stable"]) client3.save(files3) client3.run('install') - client3.run('build') + client3.run('build .') command = os.sep.join([".", "bin", "say_hello"]) client3.runner(command, cwd=client3.current_folder) self.assertIn("Hello Hello1", client3.user_io.out) @@ -92,7 +92,7 @@ def reuse_test(self): client3.run('install -o language=1 --build missing') time.sleep(1) - client3.run('build') + client3.run('build .') command = os.sep.join([".", "bin", "say_hello"]) client3.runner(command, cwd=client3.current_folder) diff --git a/conans/test/integration/conan_env_test.py b/conans/test/integration/conan_env_test.py index 5031a6458e7..f46656875f6 100644 --- a/conans/test/integration/conan_env_test.py +++ b/conans/test/integration/conan_env_test.py @@ -2,9 +2,10 @@ import platform import unittest +from conans.client.generators.text import TXTGenerator from conans.model.info import ConanInfo -from conans.model.ref import ConanFileReference -from conans.paths import CONANFILE, CONANINFO, CONANENV +from conans.model.ref import ConanFileReference, PackageReference +from conans.paths import CONANFILE, CONANINFO, CONANENV, BUILD_INFO from conans.test.utils.tools import TestClient from conans.test.utils.cpp_test_files import cpp_hello_conan_files from conans.util.files import load @@ -156,61 +157,6 @@ def test(self): client.run("test_package -e MYVAR=MYVALUE") self.assertIn("MYVAR==>MYVALUE", client.user_io.out) - def generator_env_test(self): - client = TestClient() - conanfile = """from conans import ConanFile -class MyPkg(ConanFile): - name = "Pkg" - version = "0.1" - def package_info(self): - self.env_info.MY_VAR = "MY_VALUE" - self.env_info.OTHER_VAR.append("OTHER_VALUE") -""" - client.save({"conanfile.py": conanfile}) - client.run("export lasote/testing") - test_conanfile = """from conans import ConanFile -import os -class MyTest(ConanFile): - name = "Test" - version = "0.1" - requires = "Pkg/0.1@lasote/testing" - def package_info(self): - self.env_info.OTHER_VAR.extend(["OTHER_VALUE2", "OTHER_VALUE3"]) -""" - client.save({"conanfile.py": test_conanfile}) - client.run("export lasote/testing") - test_conanfile = """from conans import ConanFile -import os -class MyTest(ConanFile): - requires = "Test/0.1@lasote/testing" -""" - client.save({"conanfile.py": test_conanfile}) - client.run("install --build=missing -g env -e OTHER_VAR=[COMMAND_LINE_VALUE] -e WHAT=EVER") - contents = load(os.path.join(client.current_folder, CONANENV)) - result = """[MY_VAR] -MY_VALUE -[OTHER_VAR] -OTHER_VALUE2 -OTHER_VALUE3 -OTHER_VALUE - -[Pkg:MY_VAR] -MY_VALUE -[Pkg:OTHER_VAR] -OTHER_VALUE - -[Test:OTHER_VAR] -OTHER_VALUE2 -OTHER_VALUE3 -""" - self.assertEqual(contents.splitlines(), result.splitlines()) - conaninfo = load(os.path.join(client.current_folder, "conaninfo.txt")) - env_section = """[env] - MY_VAR=MY_VALUE - OTHER_VAR=[COMMAND_LINE_VALUE,OTHER_VALUE2,OTHER_VALUE3,OTHER_VALUE] - WHAT=EVER""" - self.assertIn("\n".join(env_section.splitlines()), "\n".join(conaninfo.splitlines())) - def env_path_order_test(self): client = TestClient() with tools.environment_append({"SOME_VAR": ["INITIAL VALUE"]}): @@ -433,7 +379,8 @@ def package_info(self): files["conanfile.py"] = conanfile client.save(files, clean_first=True) client.run("export lasote/stable") - client.run("install Hello2/0.1@lasote/stable --build -g virtualenv -e CPPFLAGS=[OtherFlag=2]") + client.run("install Hello2/0.1@lasote/stable --build " + "-g virtualenv -e CPPFLAGS=[OtherFlag=2]") ext = "bat" if platform.system() == "Windows" else "sh" self.assertTrue(os.path.exists(os.path.join(client.current_folder, "activate.%s" % ext))) self.assertTrue(os.path.exists(os.path.join(client.current_folder, "deactivate.%s" % ext))) @@ -490,26 +437,26 @@ def build(self): files["conanfile.py"] = reuse client.save(files) client.run("install . --build missing") - client.run("build") + client.run("build .") self.assertIn("VAR1=>99", client.user_io.out) # Now specify a different value in command Line, but conaninfo already exists # So you cannot override it from command line without deleting the conaninfo.TXTGenerator client.run("install . -e VAR1=100 --build missing") - client.run("build") + client.run("build .") self.assertIn("VAR1=>100", client.user_io.out) # Remove conaninfo os.remove(os.path.join(client.current_folder, CONANINFO)) client.run("install . -e VAR1=100 --build missing") - client.run("build") + client.run("build .") self.assertIn("VAR1=>100", client.user_io.out) # Now from a profile os.remove(os.path.join(client.current_folder, CONANINFO)) client.save({"myprofile": "[env]\nVAR1=102"}, clean_first=False) client.run("install . --profile ./myprofile --build missing") - client.run("build") + client.run("build .") self.assertIn("VAR1=>102", client.user_io.out) def test_complex_deps_propagation(self): @@ -521,7 +468,7 @@ def test_complex_deps_propagation(self): client.save({"conanfile.py": reuse}) client.run("install . --build missing") - client.run("build") + client.run("build .") self.assertIn("VAR1=>800*", client.user_io.out) self.assertIn("VAR2=>24*", client.user_io.out) self.assertIn("VAR3=>22*", client.user_io.out) @@ -541,7 +488,7 @@ def test_complex_deps_propagation_append(self): client.save({"conanfile.py": reuse}) client.run("install . --build missing") - client.run("build") + client.run("build .") self.assertInSep("VAR1=>700:800:900*" % {"sep": os.pathsep}, client.user_io.out) self.assertInSep("VAR2=>24:23*" % {"sep": os.pathsep}, client.user_io.out) self.assertInSep("VAR3=>45*", client.user_io.out) @@ -553,7 +500,7 @@ def test_complex_deps_propagation_append(self): client.save({"conanfile.py": reuse}) client.run("install . --build missing") - client.run("build") + client.run("build .") self.assertInSep("VAR1=>700:800:900*", client.user_io.out) self.assertInSep("VAR2=>24:23*", client.user_io.out) self.assertInSep("VAR3=>23*", client.user_io.out) @@ -565,7 +512,7 @@ def test_complex_deps_propagation_append(self): client.save({"conanfile.py": reuse}) client.run("install . --build missing -e VAR1=[override] -e VAR3=SIMPLE") - client.run("build") + client.run("build .") self.assertInSep("VAR1=>override:700:800:900", client.user_io.out) self.assertInSep("VAR2=>24:23*", client.user_io.out) self.assertIn("VAR3=>SIMPLE*", client.user_io.out) @@ -579,7 +526,7 @@ def test_override_simple(self): client.save({"conanfile.py": reuse}) client.run("install . --build missing -e LIB_A:VAR3=override") - client.run("build") + client.run("build .") self.assertInSep("VAR1=>700:800:900", client.user_io.out) self.assertInSep("VAR2=>24:23*", client.user_io.out) self.assertIn("VAR3=>-23*", client.user_io.out) @@ -605,7 +552,7 @@ def test_override_simple2(self): self.assertIn("Building LIB_C, VAR2:24", client.user_io.out) self.assertIn("Building LIB_C, VAR3:override", client.user_io.out) - client.run("build") + client.run("build .") self.assertInSep("VAR1=>700:800:900", client.user_io.out) self.assertInSep("VAR2=>24:23*", client.user_io.out) self.assertInSep("VAR3=>override*", client.user_io.out) @@ -631,11 +578,99 @@ def test_complex_deps_propagation_override(self): self.assertIn("Building LIB_C, VAR2:24", client.user_io.out) self.assertIn("Building LIB_C, VAR3:-23", client.user_io.out) - client.run("build") + client.run("build .") self.assertInSep("VAR1=>700:800:900", client.user_io.out) self.assertInSep("VAR2=>24:23*", client.user_io.out) self.assertInSep("VAR3=>bestvalue*", client.user_io.out) + + def check_conaninfo_completion_test(self): + """ + consumer -> B -> C + -> D (conditional) + + The overwritten environment variables caused by the consumer have to be reflected in B's conaninfo.txt + """ + client = TestClient() + conanfile = """ +from conans import ConanFile +class LibConan(ConanFile): + name = "libC" + version = "1.0" + + def package_info(self): + self.env_info.MYVAR = "ValueByLibC" +""" + client.save({"conanfile.py": conanfile}) + client.run("export foo/bar") + + conanfile = """ +from conans import ConanFile +class LibConan(ConanFile): + name = "libD" + version = "1.0" + + def package_info(self): + self.env_info.MYVAR = "ValueByLibD" +""" + client.save({"conanfile.py": conanfile}) + client.run("export foo/bar") + + conanfile = """ +import os +from conans import ConanFile +class LibConan(ConanFile): + name = "libB" + version = "1.0" + + def requirements(self): + if os.environ.get("DEP", None) == "C": + self.requires.add("libC/1.0@foo/bar") + else: + self.requires.add("libD/1.0@foo/bar") + + def build_info(self): + self.output.warn("Value of MYVAR: %s" % os.environ["MYVAR"]) +""" + client.save({"conanfile.py": conanfile}) + client.run("export foo/bar") + + refb = PackageReference.loads("libB/1.0@foo/bar:5fecb9aaf431791c8c06ab146f3451823f982bb8") + + # With no overrides, B takes dependency D and the value should be ValueByLibD + client.run("install libB/1.0@foo/bar --build") + self.assertTrue("Value of MYVAR: ValueByLibD") + conaninfo = load(os.path.join(client.client_cache.package(refb), CONANINFO)) + self.assertTrue(conaninfo.endswith("[env]\n\n")) # Not user input env + + # B takes dependency C and the value should be ValueByLibC + client.run("install libB/1.0@foo/bar --build -e DEP=C") + self.assertTrue("Value of MYVAR: ValueByLibC") + conaninfo = load(os.path.join(client.client_cache.package(refb), CONANINFO)) + self.assertTrue(conaninfo.endswith("[env]\n\n")) # Not user input env + + # Consumer overrides MYVAR, so his conaninfo should have it + client.run("install libB/1.0@foo/bar --build -e MYVAR=ValueByConsumer") + self.assertTrue("Value of MYVAR: ValueByConsumer") + conaninfo = load(os.path.join(client.client_cache.package(refb), CONANINFO)) + self.assertTrue(conaninfo.endswith("[env]\n MYVAR=ValueByConsumer\n")) + + # Consumer overrides MYVAR, so his conaninfo should have it, but not the libC, because + # is not a dependency + client.run("install libB/1.0@foo/bar --build -e libB:MYVAR=ValueByConsumer " + "-e libC:MYVAR=OtherValue") + self.assertTrue("Value of MYVAR: ValueByConsumer") + conaninfo = load(os.path.join(client.client_cache.package(refb), CONANINFO)) + self.assertTrue(conaninfo.endswith("[env]\n libB:MYVAR=ValueByConsumer\n")) + + # Consumer overrides MYVAR, so his conaninfo should have it, both libB and libD + client.run("install libB/1.0@foo/bar --build -e libB:MYVAR=ValueByConsumer " + "-e libD:MYVAR=OtherValue") + self.assertTrue("Value of MYVAR: ValueByConsumer") + conaninfo = load(os.path.join(client.client_cache.package(refb), CONANINFO)) + self.assertTrue(conaninfo.endswith("[env]\n libB:MYVAR=ValueByConsumer\n" + " libD:MYVAR=OtherValue\n")) # Not user input env + def test_conaninfo_filtered(self): client = TestClient() # Try injecting some package level ENV in the install, but without priority @@ -658,40 +693,38 @@ def load_conaninfo(lib): "-e LIB_B2:NEWVAR=VALUE -e VAR3=[newappend]") info = load_conaninfo("A") - self.assertEquals(info.env_values.env_dicts("LIB_A"), ({"VAR3": "override", "GLOBAL": "99"}, {})) - self.assertEquals(info.env_values.env_dicts(""), ({'GLOBAL': '99'}, {'VAR3': ['newappend']})) + self.assertEquals(info.env_values.env_dicts("LIB_A"), + ({"VAR3": "override", "GLOBAL": "99"}, {})) + self.assertEquals(info.env_values.env_dicts(""), + ({'GLOBAL': '99'}, {'VAR3': ['newappend']})) info = load_conaninfo("B") self.assertEquals(info.env_values.env_dicts("LIB_A"), ({'GLOBAL': '99', 'VAR3': "override"}, - {'VAR2': ['23'], 'VAR1': ['900']})) + {})) self.assertEquals(info.env_values.env_dicts("LIB_B"), ({'GLOBAL': '99', "VAR2": "222"}, - {'VAR3': ['newappend', '-23'], 'VAR1': ["900"]})) + {'VAR3': ['newappend']})) info = load_conaninfo("B2") self.assertEquals(info.env_values.env_dicts("LIB_A"), ({'GLOBAL': '99', 'VAR3': 'override'}, - {'VAR2': ['23'], 'VAR1': ['900']})) + {})) self.assertEquals(info.env_values.env_dicts("LIB_B2"), ({'GLOBAL': '99', 'NEWVAR': "VALUE"}, - {'VAR2': ['23'], 'VAR1': ['900'], - 'VAR3': ['newappend', '-23']})) + {'VAR3': ['newappend']})) info = load_conaninfo("C") self.assertEquals(info.env_values.env_dicts("LIB_B2"), ({'GLOBAL': '99', 'NEWVAR': "VALUE"}, - {'VAR3': ['newappend', '-23'], - 'VAR1': ['800', '800_2', '900'], - 'VAR2': ['24', '24_2', '23']})) + {'VAR3': ['newappend']})) self.assertEquals(info.env_values.env_dicts("LIB_C"), ({'GLOBAL': '99'}, - {'VAR2': ['24', '24_2', '23'], - 'VAR1': ['800', '800_2', '900'], - 'VAR3': ['newappend', "-23"]})) + {'VAR3': ['newappend']})) # Now check the info for the project info = ConanInfo.loads(load(os.path.join(client.current_folder, CONANINFO))) self.assertEquals(info.env_values.env_dicts("PROJECT"), ({'GLOBAL': '99'}, - {'VAR2': ['24', '24_2', '23'], - 'VAR1': ['700', '800', '800_2', '900'], - 'VAR3': ['newappend', 'bestvalue']})) + {'VAR3': ['newappend']})) + + _, _, buildinfo = TXTGenerator.loads(load(os.path.join(client.current_folder, BUILD_INFO))) + self.assertEquals(buildinfo["LIB_A"].VAR1, ["900"]) def _export(self, client, name, requires, env_vars, env_vars_append=None): hello_file = """ diff --git a/conans/test/integration/conan_scopes_test.py b/conans/test/integration/conan_scopes_test.py index cc99f97ae57..b85d44a21d4 100644 --- a/conans/test/integration/conan_scopes_test.py +++ b/conans/test/integration/conan_scopes_test.py @@ -148,7 +148,7 @@ def build(self): self.assertNotIn("WARN: BUILD_CONSUMER DEV", client.user_io.out) self.assertNotIn("WARN: BUILD_CONSUMER OTHER", client.user_io.out) - client.run("build") + client.run("build .") conaninfo = load(os.path.join(client.current_folder, "conaninfo.txt")) self.assertIn("[scope] dev=True Hello:other=False", "".join(conaninfo.splitlines())) diff --git a/conans/test/integration/conan_test_test.py b/conans/test/integration/conan_test_test.py index d42139e99c3..82ff3ac03d0 100644 --- a/conans/test/integration/conan_test_test.py +++ b/conans/test/integration/conan_test_test.py @@ -11,34 +11,85 @@ @attr("slow") class ConanTestTest(unittest.TestCase): - def transitive_same_name_test(self): - # https://github.com/conan-io/conan/issues/1366 - client = TestClient() + def test_partial_reference(self): + + # Create two packages to test with the same test conanfile = ''' from conans import ConanFile class HelloConan(ConanFile): - name = "HelloBar" + name = "Hello" version = "0.1" ''' - test_package = ''' + client = TestClient() + client.save({CONANFILE: conanfile}) + client.run("create conan/stable") + client.run("create conan/testing") + client.run("create conan/foo") + + def test(conanfile_test, test_reference, path=None): + path = path or "." + client.save({os.path.join(path, CONANFILE): conanfile_test}, clean_first=True) + client.run("test %s %s" % (path, test_reference)) + + # Now try with no reference in conan test, because we already have it in the requires + test(''' from conans import ConanFile class HelloTestConan(ConanFile): - requires = "HelloBar/0.1@lasote/testing" + requires = "Hello/0.1@conan/stable" def test(self): - pass -''' - client.save({"conanfile.py": conanfile, "test_package/conanfile.py": test_package}) - client.run("test_package") - self.assertIn("HelloBar/0.1@lasote/testing: WARN: Forced build from source", - client.user_io.out) - client.save({"conanfile.py": conanfile.replace("HelloBar", "Hello") + - " requires='HelloBar/0.1@lasote/testing'", - "test_package/conanfile.py": test_package.replace("HelloBar", "Hello")}) - client.run("test_package") - self.assertNotIn("HelloBar/0.1@lasote/testing: WARN: Forced build from source", - client.user_io.out) + self.output.warn("Tested ok!") +''', "") + self.assertIn("Tested ok!", client.out) + + # Now try having two references and specifing nothing + with self.assertRaises(Exception): + test(''' +from conans import ConanFile + +class HelloTestConan(ConanFile): + requires = "Hello/0.1@conan/stable", "other/ref@conan/Stable" + def test(self): + self.output.warn("Tested ok!") +''', "") + self.assertIn("Cannot deduce the reference to be tested,", client.out) + + # Specify a valid name + test(''' +from conans import ConanFile + +class HelloTestConan(ConanFile): + requires = "Hello/0.1@conan/stable" + def test(self): + self.output.warn("Tested ok!") +''', "Hello") + self.assertIn("Tested ok!", client.out) + + # Specify a wrong name + with self.assertRaises(Exception): + test(''' +from conans import ConanFile + +class HelloTestConan(ConanFile): + requires = "Hello/0.1@conan/stable", "other/ref@conan/Stable" + def test(self): + self.output.warn("Tested ok!") +''', "badname") + self.assertIn("The package name 'badname' doesn't match with any requirement in " + "the testing conanfile.py: Hello, other", client.out) + + # Specify a complete reference but not matching with the requires, it's ok, the + # require could be a tool or whatever + test(''' +from conans import ConanFile + +class HelloTestConan(ConanFile): + requires = "Hello/0.1@conan/stable" + def test(self): + self.output.warn("Tested ok!") +''', "Hello/0.1@conan/foo") + self.assertIn("Tested ok!", client.out) def test_package_env_test(self): client = TestClient() @@ -66,7 +117,8 @@ def test(self): ''' client.save({"conanfile.py": conanfile, "test_package/conanfile.py": test_package}) - client.run("test_package") + client.run("export lasote/testing") + client.run("test test_package --build missing") def scopes_test_package_test(self): client = TestClient() @@ -92,8 +144,10 @@ def test(self): """ client.save({"conanfile.py": conanfile, "test/conanfile.py": test_conanfile}) - client.run("test_package --scope Hello:dev=True --build=missing") - self.assertIn("Hello/0.1@lasote/stable: Scope: dev=True", client.user_io.out) + client.run("export lasote/stable") + client.run("test test --scope Hello:dev=True --build=missing") + # we are not in dev scope anymore + self.assertNotIn("Hello/0.1@lasote/stable: Scope: dev=True", client.user_io.out) def fail_test_package_test(self): client = TestClient() @@ -121,13 +175,17 @@ def test(self): client.save({"conanfile.py": conanfile, "FindXXX.cmake": "Hello FindCmake", "test/conanfile.py": test_conanfile}) - client.run("test_package") + client.run("create lasote/stable") + client.run("test test") ref = PackageReference.loads("Hello/0.1@lasote/stable:" "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") self.assertEqual("Hello FindCmake", load(os.path.join(client.paths.package(ref), "FindXXX.cmake"))) client.save({"FindXXX.cmake": "Bye FindCmake"}) - client.run("test_package") + client.run("test test") # Test do not rebuild the package + self.assertEqual("Hello FindCmake", + load(os.path.join(client.paths.package(ref), "FindXXX.cmake"))) + client.run("create lasote/stable") # create rebuild the package self.assertEqual("Bye FindCmake", load(os.path.join(client.paths.package(ref), "FindXXX.cmake"))) @@ -146,7 +204,7 @@ def conan_test_test(self): class HelloReuseConan(ConanFile): settings = "os", "compiler", "build_type", "arch" - requires = "Hello0/0.1@ lasote/stable" + requires = "Hello0/0.1@lasote/stable" generators = "cmake" def build(self): @@ -209,13 +267,13 @@ def _test_with_conanfile(self, test_conanfile): files["test_package/conanfile.py"] = test_conanfile files["test_package/main.cpp"] = files["main.cpp"] client.save(files) - client.run("export lasote/stable") - error = client.run("test_package -s build_type=Release") + client.run("create lasote/stable") + error = client.run("test test_package -s build_type=Release") self.assertFalse(error) self.assertNotIn("WARN: conanbuildinfo.txt file not found", client.user_io.out) self.assertNotIn("WARN: conanenv.txt file not found", client.user_io.out) self.assertIn('Hello Hello0', client.user_io.out) - error = client.run("test_package -s Hello0:build_type=Debug -o Hello0:language=1") + error = client.run("test test_package -s Hello0:build_type=Debug -o Hello0:language=1 --build missing") self.assertFalse(error) self.assertIn('Hola Hello0', client.user_io.out) self.assertIn('BUILD_TYPE=>Debug', client.user_io.out) diff --git a/conans/test/integration/devflow_test.py b/conans/test/integration/devflow_test.py index 3a3ca6b6161..466fa2d39cf 100644 --- a/conans/test/integration/devflow_test.py +++ b/conans/test/integration/devflow_test.py @@ -1,7 +1,9 @@ import unittest + +from conans import tools from conans.test.utils.tools import TestClient import os -from conans.util.files import mkdir, load +from conans.util.files import mkdir, load, rmdir from conans.model.ref import ConanFileReference @@ -51,14 +53,15 @@ def parallel_folders_test(self): "file.h": "file_h_contents!"}) client.current_folder = build_folder - client.run("install ../recipe -g txt") + client.run("install ../recipe") client.run("build ../recipe") client.current_folder = package_folder - client.run("package ../recipe --build_folder=../build") + client.run("package ../recipe --build_folder=../build --package_folder='%s'" % + package_folder) self._assert_pkg(package_folder) client.current_folder = repo_folder client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=../pkg") + client.run("export-pkg . Pkg/0.1@lasote/testing -bf=../pkg") ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) @@ -74,14 +77,14 @@ def insource_build_test(self): client.save({"conanfile.py": conanfile, "file.h": "file_h_contents!"}) - client.run("install . -g txt") + client.run("install .") client.run("build .") client.current_folder = package_folder - client.run("package .. --build_folder=..") + client.run("package .. --build_folder=.. --package_folder='%s' " % package_folder) self._assert_pkg(package_folder) client.current_folder = repo_folder client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=./pkg") + client.run("export-pkg . Pkg/0.1@lasote/testing -bf='%s' -if=." % package_folder) ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) @@ -91,23 +94,25 @@ def insource_build_test(self): def child_build_test(self): client = TestClient() - repo_folder = client.current_folder build_folder = os.path.join(client.current_folder, "build") mkdir(build_folder) - package_folder = os.path.join(client.current_folder, "pkg") + package_folder = os.path.join(build_folder, "package") mkdir(package_folder) client.save({"conanfile.py": conanfile, "file.h": "file_h_contents!"}) client.current_folder = build_folder - client.run("install .. -g txt") + client.run("install ..") client.run("build ..") client.current_folder = package_folder - client.run("package .. --build_folder=../build") + client.run("package ../.. --build_folder=../") self._assert_pkg(package_folder) - client.current_folder = repo_folder - client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=./pkg") + rmdir(package_folder) # IMPORTANT: Symptom that package + package_folder is not fitting + # well now. (To discuss) + # But I think now you choose you way to develop, local or cache, if you use conan export-pkg + # you are done, if you use package() you need the "conan project" feature + client.current_folder = build_folder + client.run("export-pkg .. Pkg/0.1@lasote/testing --source_folder=.. ") ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) @@ -150,7 +155,7 @@ def parallel_folders_test(self): repo_folder = os.path.join(client.current_folder, "recipe") src_folder = os.path.join(client.current_folder, "src") build_folder = os.path.join(client.current_folder, "build") - package_folder = os.path.join(client.current_folder, "pkg") + package_folder = os.path.join(build_folder, "package") mkdir(repo_folder) mkdir(src_folder) mkdir(build_folder) @@ -159,17 +164,18 @@ def parallel_folders_test(self): client.save({"conanfile.py": conanfile_out}) client.current_folder = build_folder - client.run("install ../recipe -g txt") + client.run("install ../recipe") client.current_folder = src_folder + client.run("install ../recipe") client.run("source ../recipe") client.current_folder = build_folder client.run("build ../recipe --source_folder=../src") client.current_folder = package_folder - client.run("package ../recipe --source_folder=../src --build_folder=../build") + client.run("package ../../recipe --source_folder=../../src --build_folder=../") self._assert_pkg(package_folder) client.current_folder = repo_folder client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=../pkg") + client.run("export-pkg . Pkg/0.1@lasote/testing -bf=../build/package") ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) @@ -184,15 +190,15 @@ def insource_build_test(self): mkdir(package_folder) client.save({"conanfile.py": conanfile_out}) - client.run("install . -g txt") + client.run("install .") client.run("source .") client.run("build . ") client.current_folder = package_folder - client.run("package .. --build_folder=..") + client.run("package .. --build-folder=.. --package_folder='%s'" % package_folder) self._assert_pkg(package_folder) client.current_folder = repo_folder client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=./pkg") + client.run("export-pkg . Pkg/0.1@lasote/testing -bf=./pkg") ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) @@ -205,23 +211,42 @@ def child_build_test(self): repo_folder = client.current_folder build_folder = os.path.join(client.current_folder, "build") mkdir(build_folder) - package_folder = os.path.join(client.current_folder, "pkg") + package_folder = os.path.join(build_folder, "package") mkdir(package_folder) client.save({"conanfile.py": conanfile_out}) client.current_folder = build_folder - client.run("install .. -g txt") + client.run("install ..") client.run("source ..") client.run("build .. --source_folder=.") client.current_folder = package_folder - client.run("package .. --build_folder=../build") + client.run("package ../.. --build_folder=../") self._assert_pkg(package_folder) + rmdir(package_folder) client.current_folder = repo_folder - client.run("export lasote/testing") - client.run("package_files Pkg/0.1@lasote/testing -pf=./pkg") + + client.run("export-pkg . Pkg/0.1@lasote/testing -bf=./build") ref = ConanFileReference.loads("Pkg/0.1@lasote/testing") cache_package_folder = client.client_cache.packages(ref) cache_package_folder = os.path.join(cache_package_folder, os.listdir(cache_package_folder)[0]) self._assert_pkg(cache_package_folder) + + def build_local_different_folders_test(self): + # Real build, needed to ensure that the generator is put in the correct place and + # cmake finds it, using an install_folder different from build_folder + client = TestClient() + client.run("new lib/1.0") + client.run("source . --source-folder src") + + # Patch the CMakeLists to include the generator file from a different folder + install_dir = os.path.join(client.current_folder, "install_x86") + tools.replace_in_file(os.path.join(client.current_folder, "src", "hello", "CMakeLists.txt"), + "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake", + '"%s/conanbuildinfo.cmake"' % install_dir) + + client.run("install . --install-folder install_x86 -s arch=x86") + client.run("build . --build-folder build_x86 --install-folder '%s' " + "--source-folder src" % install_dir) + self.assertTrue(os.path.exists(os.path.join(client.current_folder, "build_x86", "lib"))) diff --git a/conans/test/integration/diamond_test.py b/conans/test/integration/diamond_test.py index 3696c302f9c..7d826a7288e 100644 --- a/conans/test/integration/diamond_test.py +++ b/conans/test/integration/diamond_test.py @@ -5,7 +5,6 @@ from conans.test.utils.cpp_test_files import cpp_hello_conan_files from conans.model.ref import ConanFileReference from nose.plugins.attrib import attr -from conans.model.build_info import DepsCppInfo from conans.util.files import load import os from conans.paths import BUILD_INFO, CONANFILE, BUILD_INFO_CMAKE @@ -41,7 +40,7 @@ def _check_individual_deps(self, client): self.assertIn("set(CONAN_LIBS helloHello3 helloHello1 helloHello2 helloHello0", cmakebuildinfo) self.assertIn("set(CONAN_DEPENDENCIES Hello3 Hello1 Hello2 Hello0)", cmakebuildinfo) - deps_cpp_info, _ = TXTGenerator.loads(content) + deps_cpp_info, _, _ = TXTGenerator.loads(content) self.assertEqual(len(deps_cpp_info.include_paths), 4) for dep in ("Hello3", "Hello2", "Hello1", "Hello0"): self.assertEqual(len(deps_cpp_info[dep].include_paths), 1) diff --git a/conans/test/integration/flat_requirements_test.py b/conans/test/integration/flat_requirements_test.py deleted file mode 100644 index 98a652f9379..00000000000 --- a/conans/test/integration/flat_requirements_test.py +++ /dev/null @@ -1,99 +0,0 @@ -import unittest -from conans.model.ref import ConanFileReference, PackageReference -from conans.test.utils.cpp_test_files import cpp_hello_conan_files -from conans.paths import (CONANFILE_TXT, BUILD_INFO_CMAKE, BUILD_INFO_GCC, CONANINFO, - BUILD_INFO_VISUAL_STUDIO, BUILD_INFO_XCODE) -from conans.util.files import save, load -import os -from conans.test.utils.tools import TestClient -from conans.test.utils.test_files import temp_folder - - -class FlatRequirementsTest(unittest.TestCase): - - def setUp(self): - self.conan_reference = ConanFileReference.loads("Hello0/0.1@lasote/stable") - self.files = cpp_hello_conan_files("Hello0", "0.1", build=False) - self.conan = TestClient() - package = """def package(self): - import os - os.mkdir(os.path.join(self.package_folder, "include")) - os.mkdir(os.path.join(self.package_folder, "lib")) - os.mkdir(os.path.join(self.package_folder, "bin")) -""" - self.files["conanfile.py"] = self.files["conanfile.py"].replace("def package(self):", - package) - self.conan.save(self.files) - self.conan.run("export lasote/stable") - - def consumer_with_flat_requirement_test(self): - # We want to reuse exported Hello0/0.1@lasote/stable - tmp_dir = temp_folder() - req_file = '''[requires] -Hello0/0.1@lasote/stable # My req comment -[generators] -gcc # I need this generator for.. -cmake -visual_studio -xcode -''' - save(os.path.join(tmp_dir, CONANFILE_TXT), req_file) - - self.conan.current_folder = tmp_dir - # Install requirements - self.conan.run('install --build missing') - self.assertEqual(sorted([CONANFILE_TXT, BUILD_INFO_GCC, BUILD_INFO_CMAKE, - BUILD_INFO_VISUAL_STUDIO, BUILD_INFO_XCODE, CONANINFO]), - sorted(os.listdir(tmp_dir))) - - cmake = load(os.path.join(tmp_dir, BUILD_INFO_CMAKE)) - gcc = load(os.path.join(tmp_dir, BUILD_INFO_GCC)) - - self.assertIn("CONAN_INCLUDE_DIRS", cmake) - self.assertIn("CONAN_LIB_DIRS", cmake) - self.assertIn("CONAN_LIBS", cmake) - - self.assertIn("CONAN_INCLUDE_DIRS", cmake) - self.assertIn("CONAN_LIB_DIRS", cmake) - self.assertIn(".conan/data/Hello0/0.1/lasote/stable/package", cmake) - - self.assertIn("-L", gcc) - self.assertIn("-l", gcc) - self.assertIn("-I", gcc) - - self.assertIn(".conan/data/Hello0/0.1/lasote/stable/package", gcc) - - # CHECK VISUAL STUDIO GENERATOR - - from xml.dom import minidom - xmldoc = minidom.parse(os.path.join(tmp_dir, BUILD_INFO_VISUAL_STUDIO)) - definition_group = xmldoc.getElementsByTagName('ItemDefinitionGroup')[0] - compiler = definition_group.getElementsByTagName("ClCompile")[0] - - include_dirs = compiler.getElementsByTagName("AdditionalIncludeDirectories")[0].firstChild.data - definitions = compiler.getElementsByTagName("PreprocessorDefinitions")[0].firstChild.data - - linker = definition_group.getElementsByTagName("Link")[0] - lib_dirs = linker.getElementsByTagName("AdditionalLibraryDirectories")[0].firstChild.data - libs = linker.getElementsByTagName("AdditionalDependencies")[0].firstChild.data - - package_id = os.listdir(self.conan.paths.packages(self.conan_reference))[0] - package_ref = PackageReference(self.conan_reference, package_id) - package_paths = self.conan.paths.package(package_ref).replace("\\", "/") - - expected_lib_dirs = os.path.join(package_paths, "lib").replace("\\", "/") - expected_include_dirs = os.path.join(package_paths, "include").replace("\\", "/") - - self.assertIn(expected_lib_dirs, lib_dirs) - self.assertEquals("helloHello0.lib;%(AdditionalDependencies)", libs) - self.assertEquals("%(PreprocessorDefinitions)", definitions) - self.assertIn(expected_include_dirs, include_dirs) - - # CHECK XCODE GENERATOR - xcode = load(os.path.join(tmp_dir, BUILD_INFO_XCODE)) - - self.assertIn('LIBRARY_SEARCH_PATHS = $(inherited) "%s"' % expected_lib_dirs, xcode) - self.assertIn('HEADER_SEARCH_PATHS = $(inherited) "%s"' % expected_include_dirs, xcode) - self.assertIn("GCC_PREPROCESSOR_DEFINITIONS = $(inherited)", xcode) - self.assertIn("OTHER_CFLAGS = $(inherited)", xcode) - self.assertIn("OTHER_CPLUSPLUSFLAGS = $(inherited)", xcode) diff --git a/conans/test/integration/go_diamond_test.py b/conans/test/integration/go_diamond_test.py index a8e29d245f3..4db7077b716 100644 --- a/conans/test/integration/go_diamond_test.py +++ b/conans/test/integration/go_diamond_test.py @@ -35,8 +35,8 @@ def reuse_test(self): files3 = hello_conan_files(conan_reference=conan_reference, number=4, deps=[3], lang='go') client.save(files3) client.run("install --build missing") - client.run("build") - command = os.sep.join([".", "bin", "say_hello"]) + client.run("build .") + with CustomEnvPath(paths_to_add=['$GOPATH/bin'], var_to_add=[('GOPATH', client.current_folder), ]): diff --git a/conans/test/integration/install_selected_packages_test.py b/conans/test/integration/install_selected_packages_test.py index 1149274ba15..40db3d3c5d7 100644 --- a/conans/test/integration/install_selected_packages_test.py +++ b/conans/test/integration/install_selected_packages_test.py @@ -19,34 +19,34 @@ def setUp(self): def install_all_test(self): # Should retrieve the three packages - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") p1 = os.path.join(self.new_client.paths.packages(self.ref)) packages = os.listdir(p1) self.assertEquals(len(packages), 3) def install_some_reference_test(self): # Should retrieve the specified packages - self.new_client.run("install Hello0/0.1@lasote/stable -p %s" % self.package_ids[0]) + self.new_client.run("download Hello0/0.1@lasote/stable -p %s" % self.package_ids[0]) packages = os.listdir(self.new_client.paths.packages(self.ref)) self.assertEquals(len(packages), 1) self.assertEquals(packages[0], self.package_ids[0]) - self.new_client.run("install Hello0/0.1@lasote/stable -p %s -p %s" % (self.package_ids[0], - self.package_ids[1])) + self.new_client.run("download Hello0/0.1@lasote/stable -p %s -p %s" % (self.package_ids[0], + self.package_ids[1])) packages = os.listdir(self.new_client.paths.packages(self.ref)) self.assertEquals(len(packages), 2) def download_recipe_twice_test(self): expected_conanfile_contents = self.files[CONANFILE] - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_conanfile = load(os.path.join(self.new_client.paths.export(self.ref), CONANFILE)) self.assertEquals(expected_conanfile_contents, got_conanfile) - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_conanfile = load(os.path.join(self.new_client.paths.export(self.ref), CONANFILE)) self.assertEquals(expected_conanfile_contents, got_conanfile) - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_conanfile = load(os.path.join(self.new_client.paths.export(self.ref), CONANFILE)) self.assertEquals(expected_conanfile_contents, got_conanfile) @@ -54,15 +54,15 @@ def download_packages_twice_test(self): expected_header_contents = self.files["helloHello0.h"] package_folder = self.new_client.paths.package(PackageReference(self.ref, self.package_ids[0])) - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_header = load(os.path.join(package_folder, "include", "helloHello0.h")) self.assertEquals(expected_header_contents, got_header) - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_header = load(os.path.join(package_folder, "include", "helloHello0.h")) self.assertEquals(expected_header_contents, got_header) - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") got_header = load(os.path.join(package_folder, "include", "helloHello0.h")) self.assertEquals(expected_header_contents, got_header) @@ -72,7 +72,7 @@ def install_all_but_no_packages_test(self): self.new_client.run("remove Hello* -f -r default") # Try to install all - self.new_client.run("install Hello0/0.1@lasote/stable --all", ignore_error=True) + self.new_client.run("download Hello0/0.1@lasote/stable", ignore_error=True) self.assertIn("'Hello0/0.1@lasote/stable' not found in remote", self.new_client.user_io.out) # Upload only the recipe @@ -81,7 +81,7 @@ def install_all_but_no_packages_test(self): self.new_client.run("upload Hello0/0.1@lasote/stable --all") # And try to download all - self.new_client.run("install Hello0/0.1@lasote/stable --all") + self.new_client.run("download Hello0/0.1@lasote/stable") self.assertIn("No remote binary packages found in remote", self.new_client.user_io.out) def _upload_some_packages(self, client): diff --git a/conans/test/integration/install_update_test.py b/conans/test/integration/install_update_test.py index d90c0afd648..cc06f5fcff7 100644 --- a/conans/test/integration/install_update_test.py +++ b/conans/test/integration/install_update_test.py @@ -5,6 +5,7 @@ from conans.test.utils.cpp_test_files import cpp_hello_conan_files from conans.util.files import load, save from time import sleep +import time class InstallUpdateTest(unittest.TestCase): @@ -38,7 +39,6 @@ def timestamps(): initial_timestamps = timestamps() - import time time.sleep(1) # Change and rebuild package @@ -92,3 +92,34 @@ def reuse_test(self): package_path = client2.paths.package(PackageReference(ref, package_ids[0])) header = load(os.path.join(package_path, "include/helloHello0.h")) self.assertEqual(header, "//EMPTY!") + + def remove_old_sources_test(self): + # https://github.com/conan-io/conan/issues/1841 + test_server = TestServer() + + def upload(header_content): + client = TestClient(servers={"default": test_server}, + users={"default": [("lasote", "mypass")]}) + base = '''from conans import ConanFile +class ConanLib(ConanFile): + exports_sources = "*" + def package(self): + self.copy("*") +''' + client.save({"conanfile.py": base, + "header.h": header_content}) + client.run("create Pkg/0.1@lasote/channel") + client.run("upload * --confirm") + return client + + client = upload("mycontent1") + time.sleep(1) + upload("mycontent2") + + # This is no longer necessary, as binary packages are removed when recipe is updated + # client.run("remove Pkg/0.1@lasote/channel -p -f") + client.run("install Pkg/0.1@lasote/channel -u --build=missing") + conan_ref = ConanFileReference.loads("Pkg/0.1@lasote/channel") + pkg_ref = PackageReference(conan_ref, "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") + header = os.path.join(client.client_cache.package(pkg_ref), "header.h") + self.assertEqual(load(header), "mycontent2") diff --git a/conans/test/integration/multi_build_test.py b/conans/test/integration/multi_build_test.py index cc1c18eb1d1..d97fcc03495 100644 --- a/conans/test/integration/multi_build_test.py +++ b/conans/test/integration/multi_build_test.py @@ -29,7 +29,7 @@ def collect_libs_test(self): # reusing the binary already in cache client.save(files3, clean_first=True) client.run('install') - client.run('build') + client.run('build .') command = os.sep.join([".", "bin", "say_hello"]) client.runner(command, cwd=client.current_folder) @@ -39,7 +39,7 @@ def collect_libs_test(self): # rebuilding the binary in cache client.run('remove "*" -p -f') client.run('install --build') - client.run('build') + client.run('build .') command = os.sep.join([".", "bin", "say_hello"]) client.runner(command, cwd=client.current_folder) diff --git a/conans/test/integration/only_source_test.py b/conans/test/integration/only_source_test.py index 57572023331..ef48471c047 100644 --- a/conans/test/integration/only_source_test.py +++ b/conans/test/integration/only_source_test.py @@ -115,14 +115,14 @@ def build_policies_in_conanfile_test(self): client.run("export lasote/stable") # Install, it will build automatically if missing (without the --build missing option) - client.run("install Hello0/1.0@lasote/stable -g txt") + client.run("install Hello0/1.0@lasote/stable") self.assertIn("Building", client.user_io.out) - self.assertIn("Generator txt created conanbuildinfo.txt", client.user_io.out) + self.assertNotIn("Generator txt created conanbuildinfo.txt", client.user_io.out) # Try to do it again, now we have the package, so no build is done - client.run("install Hello0/1.0@lasote/stable -g txt") + client.run("install Hello0/1.0@lasote/stable") self.assertNotIn("Building", client.user_io.out) - self.assertIn("Generator txt created conanbuildinfo.txt", client.user_io.out) + self.assertNotIn("Generator txt created conanbuildinfo.txt", client.user_io.out) # Try now to upload all packages, should not crash because of the "missing" build policy client.run("upload Hello0/1.0@lasote/stable --all", ignore_error=False) @@ -133,18 +133,18 @@ def build_policies_in_conanfile_test(self): client.run("export lasote/stable") # Install, it will build automatically if missing (without the --build missing option) - client.run("install Hello0/1.0@lasote/stable -g txt") + client.run("install Hello0/1.0@lasote/stable") self.assertIn("Detected build_policy 'always', trying to remove source folder", client.user_io.out) self.assertIn("Building", client.user_io.out) - self.assertIn("Generator txt created conanbuildinfo.txt", client.user_io.out) + self.assertNotIn("Generator txt created conanbuildinfo.txt", client.user_io.out) # Try to do it again, now we have the package, but we build again - client.run("install Hello0/1.0@lasote/stable -g txt") + client.run("install Hello0/1.0@lasote/stable") self.assertIn("Building", client.user_io.out) self.assertIn("Detected build_policy 'always', trying to remove source folder", client.user_io.out) - self.assertIn("Generator txt created conanbuildinfo.txt", client.user_io.out) + self.assertNotIn("Generator txt created conanbuildinfo.txt", client.user_io.out) # Try now to upload all packages, should crash because of the "always" build policy client.run("upload Hello0/1.0@lasote/stable --all", ignore_error=True) diff --git a/conans/test/integration/pkg_config_test.py b/conans/test/integration/pkg_config_test.py index 20cbea7a2b3..24e05f94554 100644 --- a/conans/test/integration/pkg_config_test.py +++ b/conans/test/integration/pkg_config_test.py @@ -178,6 +178,8 @@ def _run_reuse(self, conanfile_b, conanfile_a): client.save({"conanfile.py": conanfile_a, "main.cpp": main_cpp}, clean_first=True) - client.run("install -g txt") - client.run("build") + + client.run("install") + client.run("build .") + subprocess.Popen("./main", cwd=client.current_folder) diff --git a/conans/test/integration/private_deps_test.py b/conans/test/integration/private_deps_test.py index dadae59f24f..7e3a454def3 100644 --- a/conans/test/integration/private_deps_test.py +++ b/conans/test/integration/private_deps_test.py @@ -142,7 +142,7 @@ def reuse_test(self): client.save(files3) client.run('install --build missing') - client.run('build') + client.run('build .') # assert Hello3 only depends on Hello2, and Hello1 info_path = os.path.join(client.current_folder, BUILD_INFO_CMAKE) @@ -195,7 +195,7 @@ def reuse_test(self): client2.save(files3) client2.run('install -o language=1 --build missing') - client2.run('build') + client2.run('build .') self.assertNotIn("libhello0.a", client2.user_io.out) self.assertNotIn("libhello00.a", client2.user_io.out) self.assertNotIn("libhello1.a", client2.user_io.out) @@ -215,7 +215,7 @@ def reuse_test(self): client2.save(files3, clean_first=True) client2.run('install -o language=1 --build missing') - client2.run('build') + client2.run('build .') self.assertNotIn("libhello0.a", client2.user_io.out) self.assertNotIn("libhello00.a", client2.user_io.out) self.assertNotIn("libhello1.a", client2.user_io.out) diff --git a/conans/test/integration/profile_build_requires_test.py b/conans/test/integration/profile_build_requires_test.py index 71b790c7f9c..8981fcc8a58 100644 --- a/conans/test/integration/profile_build_requires_test.py +++ b/conans/test/integration/profile_build_requires_test.py @@ -108,7 +108,7 @@ def test_profile_open_requires(self): client.run("install --profile ./profile.txt --build missing") self.assertNotIn("Hello World!", client.user_io.out) - client.run("build") + client.run("build .") self.assertIn("Hello World!", client.user_io.out) self.assertIn("Project: Hello world from python tool!", client.user_io.out) @@ -245,12 +245,12 @@ def build(self): client.run("install -o MyLib:coverage=True --build missing") self.assertIn("Installing build requirements of: PROJECT", client.user_io.out) self.assertIn("Build requires: [MyTool/0.1@lasote/stable]", client.user_io.out) - client.run("build") + client.run("build .") self.assertIn("Project: Coverage True", client.user_io.out) client.save({CONANFILE: conanfile}, clean_first=True) client.run("install -o coverage=True") self.assertIn("Installing build requirements of: PROJECT", client.user_io.out) self.assertIn("Build requires: [MyTool/0.1@lasote/stable]", client.user_io.out) - client.run("build") + client.run("build .") self.assertIn("Project: Coverage True", client.user_io.out) diff --git a/conans/test/integration/profile_build_requires_testing_test.py b/conans/test/integration/profile_build_requires_testing_test.py index e591e46eeac..417644de037 100644 --- a/conans/test/integration/profile_build_requires_testing_test.py +++ b/conans/test/integration/profile_build_requires_testing_test.py @@ -42,6 +42,7 @@ def build(self): """ test_profile = """ +include(default) [build_requires] Test1/0.1@lasote/stable """ diff --git a/conans/test/integration/profile_test.py b/conans/test/integration/profile_test.py index d19cb719046..33272bf4f61 100644 --- a/conans/test/integration/profile_test.py +++ b/conans/test/integration/profile_test.py @@ -1,5 +1,6 @@ import unittest +from conans.client import tools from conans.test.utils.tools import TestClient from conans.test.utils.cpp_test_files import cpp_hello_conan_files from conans.util.files import save, load @@ -7,7 +8,7 @@ from conans.paths import CONANFILE from collections import OrderedDict from conans.test.utils.test_files import temp_folder -from conans.test.utils.profiles import create_profile +from conans.test.utils.profiles import create_profile as _create_profile from nose_parameterized import parameterized @@ -33,6 +34,14 @@ def build(self): """ +def create_profile(folder, name, settings=None, scopes=None, package_settings=None, env=None, + package_env=None, options=None): + _create_profile(folder, name, settings, scopes, package_settings, env, package_env, options) + content = load(os.path.join(folder, name)) + content = "include(default)\n" + content + save(os.path.join(folder, name), content) + + class ProfileTest(unittest.TestCase): def setUp(self): @@ -47,7 +56,8 @@ def bad_syntax_test(self): ''' clang_profile_path = os.path.join(self.client.client_cache.profiles_path, "clang") save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) self.assertIn("Error reading 'clang' profile", self.client.user_io.out) self.assertIn("Bad syntax", self.client.user_io.out) @@ -56,7 +66,8 @@ def bad_syntax_test(self): [invented] ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) self.assertIn("Unrecognized field 'invented'", self.client.user_io.out) self.assertIn("Error reading 'clang' profile", self.client.user_io.out) @@ -65,23 +76,28 @@ def bad_syntax_test(self): as ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) - self.assertIn("Error reading 'clang' profile: Invalid setting line 'as'", self.client.user_io.out) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) + self.assertIn("Error reading 'clang' profile: Invalid setting line 'as'", + self.client.user_io.out) profile = ''' [env] as ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) - self.assertIn("Error reading 'clang' profile: Invalid env line 'as'", self.client.user_io.out) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) + self.assertIn("Error reading 'clang' profile: Invalid env line 'as'", + self.client.user_io.out) profile = ''' [scopes] as ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) self.assertIn("Error reading 'clang' profile: Bad scope as", self.client.user_io.out) profile = ''' @@ -89,26 +105,29 @@ def bad_syntax_test(self): os = a value ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", + ignore_error=True) # stripped "a value" self.assertIn("'a value' is not a valid 'settings.os'", self.client.user_io.out) profile = ''' + include(default) [env] ENV_VAR = a value ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build missing -pr clang") self._assert_env_variable_printed("ENV_VAR", "a value") profile = ''' + include(default) # Line with comments is not a problem [env] # Not even here ENV_VAR = a value ''' save(clang_profile_path, profile) - self.client.run("install Hello0/0.1@lasote/stable --build -pr clang", ignore_error=True) + self.client.run("install Hello0/0.1@lasote/stable --build -pr clang") self._assert_env_variable_printed("ENV_VAR", "a value") @parameterized.expand([("", ), ("./local_profiles/", ), (temp_folder() + "/", )]) @@ -133,7 +152,8 @@ def install_profile_env_test(self): self._assert_env_variable_printed("OTHER_VAR", "2") # Override with package var - self.client.run("install Hello0/0.1@lasote/stable --build -pr envs -e Hello0:A_VAR=OTHER_VALUE") + self.client.run("install Hello0/0.1@lasote/stable --build " + "-pr envs -e Hello0:A_VAR=OTHER_VALUE") self._assert_env_variable_printed("A_VAR", "OTHER_VALUE") self._assert_env_variable_printed("OTHER_VAR", "2") @@ -161,6 +181,9 @@ def install_profile_settings_test(self): create_profile(self.client.client_cache.profiles_path, "vs_12_86", settings=profile_settings, package_settings={}) + tools.replace_in_file(self.client.client_cache.default_profile_path, + "compiler.libcxx", "#compiler.libcxx", strict=False) + self.client.save(files) self.client.run("export lasote/stable") self.client.run("install --build missing -pr vs_12_86") @@ -297,7 +320,8 @@ def test(self): self.assertIn("My var is IN_TEST_PACKAGE", str(self.client.user_io.out)) # Try now overriding some variables with command line - self.client.run("test_package --profile scopes_env2 -e DefaultName:ONE_VAR=InTestPackageOverride " + self.client.run("test_package --profile scopes_env2 " + "-e DefaultName:ONE_VAR=InTestPackageOverride " "-e Hello0:ONE_VAR=PackageValueOverride ") self._assert_env_variable_printed("ONE_VAR", "PackageValueOverride") @@ -347,7 +371,8 @@ def config(self): self.client.run("export lasote/stable") # Create a profile that doesn't activate the require - create_profile(self.client.client_cache.profiles_path, "scopes_env", settings={"os": "Linux"}, + create_profile(self.client.client_cache.profiles_path, "scopes_env", + settings={"os": "Linux"}, scopes={}) # Install with the previous profile @@ -356,7 +381,8 @@ def config(self): WinRequire/0.1@lasote/stable''', self.client.user_io.out) # Create a profile that activate the require - create_profile(self.client.client_cache.profiles_path, "scopes_env", settings={"os": "Windows"}, + create_profile(self.client.client_cache.profiles_path, "scopes_env", + settings={"os": "Windows"}, scopes={}) # Install with the previous profile diff --git a/conans/test/integration/python_build_test.py b/conans/test/integration/python_build_test.py index e65e7a99678..fe5b2ce9fde 100644 --- a/conans/test/integration/python_build_test.py +++ b/conans/test/integration/python_build_test.py @@ -71,10 +71,10 @@ def reuse_test(self): client.run("export lasote/stable") client.save({CONANFILE: reuse}, clean_first=True) - client.run("install . -g txt") - self.assertIn("Hello Bar", client.user_io.out) + client.run("install .") + self.assertNotIn("Hello Bar", client.user_io.out) # IMPORTANT!! WTF? Why this test was passing? Why I'm missing? self.assertNotIn("Hello Foo", client.user_io.out) - client.run("build") + client.run("build .") self.assertNotIn("Hello Bar", client.user_io.out) self.assertIn("Hello Foo", client.user_io.out) @@ -141,17 +141,14 @@ def basic_package_test(self): self.assertEqual([' Hello Baz', ' Hello Foo', ' Hello Boom', ' Hello Bar'], lines) - client.run("package Consumer/0.1@lasote/stable") - def basic_source_test(self): client = TestClient() client.save({CONANFILE: conanfile, "__init__.py": "", "mytest.py": test}) client.run("export lasote/stable") client.save({CONANFILE: reuse}, clean_first=True) - client.run("export lasote/stable") - client.run("install -g txt") - client.run("source Consumer/0.1@lasote/stable") + client.run("install .") + client.run("source .") self.assertIn("Hello Baz", client.user_io.out) self.assertNotIn("Hello Foo", client.user_io.out) self.assertNotIn("Hello Bar", client.user_io.out) @@ -163,16 +160,13 @@ def errors_test(self): client.run("export lasote/stable") client.save({CONANFILE: reuse}, clean_first=True) - client.run("export lasote/stable") client.run("install") # BUILD_INFO is created by default, remove it to check message os.remove(os.path.join(client.current_folder, BUILD_INFO)) - client.run("source Consumer/0.1@lasote/stable") - self.assertNotIn("Consumer/0.1@lasote/stable: WARN: conanbuildinfo.txt file not found", - client.user_io.out) + client.run("source .", ignore_error=True) # Output in py3 is different, uses single quote # Now it works automatically without the env generator file - self.assertNotIn("No module named mytest", str(client.user_io.out).replace("'", "")) + self.assertIn("No module named mytest", str(client.user_io.out).replace("'", "")) def pythonpath_env_injection_test(self): @@ -235,7 +229,7 @@ def build(self): """ client.save({CONANFILE: reuse}) client.run("install --build -e PYTHONPATH=['%s']" % external_dir) - client.run("build") + client.run("build .") info = ConanInfo.loads(load(os.path.join(client.current_folder, "conaninfo.txt"))) pythonpath = info.env_values.env_dicts(None)[1]["PYTHONPATH"] self.assertEquals(os.path.normpath(pythonpath[0]), os.path.normpath(external_dir)) diff --git a/conans/test/integration/python_diamond_test.py b/conans/test/integration/python_diamond_test.py index 120639ae477..63500e74d1c 100644 --- a/conans/test/integration/python_diamond_test.py +++ b/conans/test/integration/python_diamond_test.py @@ -35,7 +35,7 @@ def reuse_test(self): " ".join(str(self.client.user_io.out).splitlines())) self.assertNotIn("Project: Build stuff Hello3", self.client.user_io.out) - self.client.run("build") + self.client.run("build .") self.assertIn("Project: Build stuff Hello3", self.client.user_io.out) if platform.system() == "Windows": diff --git a/conans/test/integration/run_envronment_test.py b/conans/test/integration/run_envronment_test.py index 4c834cff563..5abc4a1cc54 100644 --- a/conans/test/integration/run_envronment_test.py +++ b/conans/test/integration/run_envronment_test.py @@ -33,4 +33,4 @@ def build(self): client.save({"conanfile.py": reuse}, clean_first=True) client.run("install --build missing") - client.run("build") + client.run("build .") diff --git a/conans/test/integration/symlinks_test.py b/conans/test/integration/symlinks_test.py index 2dcb6625fe6..82feebc58c1 100644 --- a/conans/test/integration/symlinks_test.py +++ b/conans/test/integration/symlinks_test.py @@ -85,10 +85,11 @@ def package_files_test(self): class TestConan(ConanFile): name = "Hello" version = "0.1" + + def package(self): + self.copy("*", symlinks=True) """ - client.save({"conanfile.py": conanfile}) - client.run("export lasote/stable") - client.save({}, clean_first=True) + client.save({"recipe/conanfile.py": conanfile}) file1 = os.path.join(client.current_folder, "file1.txt") file2 = os.path.join(client.current_folder, "version1/file2.txt") file11 = os.path.join(client.current_folder, "file1.txt.1") @@ -97,7 +98,7 @@ class TestConan(ConanFile): os.symlink("file1.txt", file11) save(file2, "Hello2") os.symlink("version1", latest) - client.run("package_files Hello/0.1@lasote/stable") + client.run("export-pkg ./recipe Hello/0.1@lasote/stable") ref = PackageReference.loads("Hello/0.1@lasote/stable:" "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") diff --git a/conans/test/integration/upload_test.py b/conans/test/integration/upload_test.py index 87f54cc884b..3ddc90444c1 100644 --- a/conans/test/integration/upload_test.py +++ b/conans/test/integration/upload_test.py @@ -54,7 +54,7 @@ def reuse_downloaded_tgz_test(self): # THEN A NEW USER DOWNLOADS THE PACKAGES AND UPLOADS COMPRESSING AGAIN # BECAUSE ONLY TGZ IS KEPT WHEN UPLOADING other_client = TestClient(servers=self.servers, users={"default": [("lasote", "mypass")]}) - other_client.run("install Hello0/0.1@lasote/stable --all") + other_client.run("download Hello0/0.1@lasote/stable") other_client.run("upload Hello0/0.1@lasote/stable --all") self.assertIn("Compressing recipe", self.client.user_io.out) self.assertIn("Compressing package", self.client.user_io.out) diff --git a/conans/test/libcxx_setting_test.py b/conans/test/libcxx_setting_test.py index 4c46f3e9853..bf5a62323de 100644 --- a/conans/test/libcxx_setting_test.py +++ b/conans/test/libcxx_setting_test.py @@ -56,30 +56,30 @@ def test_declared_stdlib_and_passed(self): if platform.system() == "SunOS": client.run('install -s compiler=sun-cc -s compiler.libcxx=libCstd') - client.run('build') + client.run('build .') self.assertIn("-library=Cstd", client.out) client.run('install -s compiler=sun-cc -s compiler.libcxx=libstdcxx') - client.run('build') + client.run('build .') self.assertIn("-library=stdcxx4", client.out) client.run('install -s compiler=sun-cc -s compiler.libcxx=libstlport') - client.run('build') + client.run('build .') self.assertIn("-library=stlport4", client.out) else: client.run('install -s compiler=clang -s compiler.version=3.3 -s compiler.libcxx=libstdc++ ') - client.run('build') + client.run('build .') self.assertIn("-stdlib=libstdc++", client.out) self.assertIn("Found Define: _GLIBCXX_USE_CXX11_ABI=0", client.out) client.run('install -s compiler=clang -s compiler.version=3.3 -s compiler.libcxx=libstdc++11') - client.run('build') + client.run('build .') self.assertIn("-stdlib=libstdc++", client.out) self.assertIn("Found Define: _GLIBCXX_USE_CXX11_ABI=1", client.out) client.run('install -s compiler=clang -s compiler.version=3.3 -s compiler.libcxx=libc++') - client.run('build') + client.run('build .') self.assertIn("-stdlib=libc++", client.out) self.assertNotIn("Found Define: _GLIBCXX_USE_CXX11", client.out) diff --git a/conans/test/model/build_info_test.py b/conans/test/model/build_info_test.py index 83694726dfe..440dda59ccb 100644 --- a/conans/test/model/build_info_test.py +++ b/conans/test/model/build_info_test.py @@ -3,7 +3,8 @@ from conans.model.build_info import DepsCppInfo, CppInfo from conans.client.generators import TXTGenerator from collections import namedtuple, defaultdict -from conans.model.env_info import DepsEnvInfo +from conans.model.env_info import DepsEnvInfo, EnvInfo +from conans.model.user_info import DepsUserInfo from conans.test.utils.test_files import temp_folder import platform from conans.util.files import mkdir @@ -25,12 +26,46 @@ def parse_test(self): [includedirs_My-Component-Tool] my-component-tool """ - deps_info, _ = TXTGenerator.loads(text) - self.assertEqual(deps_info.includedirs, ['C:/Whenever']) - self.assertEqual(deps_info["Boost"].includedirs, ['F:/ChildrenPath']) - self.assertEqual(deps_info["My_Lib"].includedirs, ['mylib_path']) - self.assertEqual(deps_info["My_Other_Lib"].includedirs, ['otherlib_path']) - self.assertEqual(deps_info["My-Component-Tool"].includedirs, ['my-component-tool']) + deps_cpp_info, _, _ = TXTGenerator.loads(text) + + def assert_cpp(deps_cpp_info_test): + self.assertEqual(deps_cpp_info_test.includedirs, ['C:/Whenever']) + self.assertEqual(deps_cpp_info_test["Boost"].includedirs, ['F:/ChildrenPath']) + self.assertEqual(deps_cpp_info_test["My_Lib"].includedirs, ['mylib_path']) + self.assertEqual(deps_cpp_info_test["My_Other_Lib"].includedirs, ['otherlib_path']) + self.assertEqual(deps_cpp_info_test["My-Component-Tool"].includedirs, ['my-component-tool']) + + assert_cpp(deps_cpp_info) + # Now adding env_info + text2 = text + """ +[ENV_LIBA] +VAR2=23 +""" + deps_cpp_info, _, deps_env_info = TXTGenerator.loads(text2) + assert_cpp(deps_cpp_info) + self.assertEqual(deps_env_info["LIBA"].VAR2, "23") + + # Now only with user info + text3 = text + """ +[USER_LIBA] +VAR2=23 +""" + deps_cpp_info, deps_user_info, _ = TXTGenerator.loads(text3) + assert_cpp(deps_cpp_info) + self.assertEqual(deps_user_info["LIBA"].VAR2, "23") + + # Now with all + text4 = text + """ +[USER_LIBA] +VAR2=23 + +[ENV_LIBA] +VAR2=23 +""" + deps_cpp_info, deps_user_info, deps_env_info = TXTGenerator.loads(text4) + assert_cpp(deps_cpp_info) + self.assertEqual(deps_user_info["LIBA"].VAR2, "23") + self.assertEqual(deps_env_info["LIBA"].VAR2, "23") def help_test(self): deps_env_info = DepsEnvInfo() @@ -45,7 +80,7 @@ def help_test(self): deps_cpp_info._dependencies["Boost"] = child fakeconan = namedtuple("Conanfile", "deps_cpp_info cpp_info deps_env_info env_info user_info deps_user_info") output = TXTGenerator(fakeconan(deps_cpp_info, None, deps_env_info, None, {}, defaultdict(dict))).content - deps_cpp_info2, _ = TXTGenerator.loads(output) + deps_cpp_info2, _, _ = TXTGenerator.loads(output) self.assertEqual(deps_cpp_info.configs, deps_cpp_info2.configs) self.assertEqual(deps_cpp_info.includedirs, deps_cpp_info2.includedirs) self.assertEqual(deps_cpp_info.libdirs, deps_cpp_info2.libdirs) @@ -73,10 +108,19 @@ def configs_test(self): child.debug.cppflags.append("cxxmydebugflag") deps_cpp_info._dependencies["Boost"] = child + deps_env_info = DepsEnvInfo() + env_info_lib1 = EnvInfo() + env_info_lib1.var = "32" + env_info_lib1.othervar.append("somevalue") + deps_env_info.update(env_info_lib1, "LIB1") + + deps_user_info = DepsUserInfo() + deps_user_info["LIB2"].myuservar = "23" + fakeconan = namedtuple("Conanfile", "deps_cpp_info cpp_info deps_env_info env_info user_info deps_user_info") - output = TXTGenerator(fakeconan(deps_cpp_info, None, None, None, {}, defaultdict(dict))).content + output = TXTGenerator(fakeconan(deps_cpp_info, None, deps_env_info, deps_user_info, {}, defaultdict(dict))).content - deps_cpp_info2, _ = TXTGenerator.loads(output) + deps_cpp_info2, _, deps_env_info2 = TXTGenerator.loads(output) self.assertEqual(deps_cpp_info.includedirs, deps_cpp_info2.includedirs) self.assertEqual(deps_cpp_info.libdirs, deps_cpp_info2.libdirs) self.assertEqual(deps_cpp_info.bindirs, deps_cpp_info2.bindirs) @@ -103,6 +147,11 @@ def configs_test(self): deps_cpp_info2["Boost"].debug.cppflags) self.assertEqual(deps_cpp_info["Boost"].debug.cppflags, ["cxxmydebugflag"]) + self.assertEqual(deps_env_info["LIB1"].var, "32") + self.assertEqual(deps_env_info["LIB1"].othervar, ["somevalue"]) + + self.assertEqual(deps_user_info["LIB2"].myuservar, "23") + def cpp_info_test(self): folder = temp_folder() mkdir(os.path.join(folder, "include")) diff --git a/conans/test/model/env_test.py b/conans/test/model/env_test.py index e3fe0c571f0..135e2e57ad5 100644 --- a/conans/test/model/env_test.py +++ b/conans/test/model/env_test.py @@ -80,7 +80,7 @@ class EnvInfoTest(unittest.TestCase): def assign_test(self): env = DepsEnvInfo() - env.foo = "var" + env.foo = ["var"] env.foo.append("var2") env.foo2 = "var3" env.foo2 = "var4" @@ -90,7 +90,7 @@ def assign_test(self): def update_test(self): env = DepsEnvInfo() - env.foo = "var" + env.foo = ["var"] env.foo.append("var2") env.foo2 = "var3" env.foo2 = "var4" diff --git a/conans/test/model/options_test.py b/conans/test/model/options_test.py index 652fba9553b..79773095459 100644 --- a/conans/test/model/options_test.py +++ b/conans/test/model/options_test.py @@ -125,6 +125,106 @@ def basic_test(self): Boost:thread.multi=off Poco:deps_bundled=True""") + def pattern_positive_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("static", False) + boost_values.add_option("path", "FuzzBuzz") + + options = {"Boost.*": boost_values} + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + down_ref = ConanFileReference.loads("Consumer/0.1@diego/testing") + output = TestBufferConanOutput() + self.sut.propagate_upstream(options, down_ref, own_ref, output) + self.assertEqual(self.sut.values.as_list(), [("optimized", "3"), + ("path", "FuzzBuzz"), + ("static", "False"), + ("Boost.*:path", "FuzzBuzz"), + ("Boost.*:static", "False"), + ]) + + def multi_pattern_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("static", False) + boost_values.add_option("path", "FuzzBuzz") + boost_values2 = PackageOptionValues() + boost_values2.add_option("optimized", 2) + + options = {"Boost.*": boost_values, + "*": boost_values2} + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + down_ref = ConanFileReference.loads("Consumer/0.1@diego/testing") + output = TestBufferConanOutput() + self.sut.propagate_upstream(options, down_ref, own_ref, output) + self.assertEqual(self.sut.values.as_list(), [("optimized", "2"), + ("path", "FuzzBuzz"), + ("static", "False"), + ('*:optimized', '2'), + ("Boost.*:path", "FuzzBuzz"), + ("Boost.*:static", "False"), + ]) + + def multi_pattern_error_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("optimized", 4) + boost_values2 = PackageOptionValues() + boost_values2.add_option("optimized", 2) + + options = {"Boost.*": boost_values, + "*": boost_values2} + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + down_ref = ConanFileReference.loads("Consumer/0.1@diego/testing") + output = TestBufferConanOutput() + output.werror_active = True + with self.assertRaises(ConanException): + self.sut.propagate_upstream(options, down_ref, own_ref, output) + + def all_positive_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("static", False) + boost_values.add_option("path", "FuzzBuzz") + + options = {"*": boost_values} + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + down_ref = ConanFileReference.loads("Consumer/0.1@diego/testing") + output = TestBufferConanOutput() + self.sut.propagate_upstream(options, down_ref, own_ref, output) + self.assertEqual(self.sut.values.as_list(), [("optimized", "3"), + ("path", "FuzzBuzz"), + ("static", "False"), + ("*:path", "FuzzBuzz"), + ("*:static", "False"), + ]) + + def pattern_ignore_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("fake_option", "FuzzBuzz") + + options = {"Boost.*": boost_values} + down_ref = ConanFileReference.loads("Consumer/0.1@diego/testing") + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + output = TestBufferConanOutput() + self.sut.propagate_upstream(options, down_ref, own_ref, output) + self.assertEqual(self.sut.values.as_list(), [("optimized", "3"), + ("path", "NOTDEF"), + ("static", "True"), + ("Boost.*:fake_option", "FuzzBuzz"), + ]) + + def pattern_unmatch_test(self): + boost_values = PackageOptionValues() + boost_values.add_option("fake_option", "FuzzBuzz") + + options = {"OpenSSL.*": boost_values} + down_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + own_ref = ConanFileReference.loads("Boost.Assert/0.1@diego/testing") + output = TestBufferConanOutput() + self.sut.propagate_upstream(options, down_ref, own_ref, output) + self.assertEqual(self.sut.values.as_list(), [("optimized", "3"), + ("path", "NOTDEF"), + ("static", "True"), + ("OpenSSL.*:fake_option", "FuzzBuzz"), + ]) + class OptionsValuesTest(unittest.TestCase): diff --git a/conans/test/model/other_settings_test.py b/conans/test/model/other_settings_test.py index ebf7290cfcf..df600d34b04 100644 --- a/conans/test/model/other_settings_test.py +++ b/conans/test/model/other_settings_test.py @@ -148,7 +148,7 @@ class SayConan(ConanFile): self.client.save({CONANFILE: content}) self.client.run("install -s os=ChromeOS --build missing", ignore_error=True) self.assertIn(bad_value_msg("settings.os", "ChromeOS", - ['Android', 'Arduino', 'FreeBSD', 'Linux', 'Macos', 'SunOS', 'Windows', 'iOS']), + ['Android', 'Arduino', 'FreeBSD', 'Linux', 'Macos', 'SunOS', 'Windows', 'iOS', 'tvOS', 'watchOS']), str(self.client.user_io.out)) # Now add new settings to config and try again diff --git a/conans/test/model/transitive_reqs_test.py b/conans/test/model/transitive_reqs_test.py index fe6676396d6..301201d1950 100644 --- a/conans/test/model/transitive_reqs_test.py +++ b/conans/test/model/transitive_reqs_test.py @@ -698,6 +698,56 @@ class ChatConan(ConanFile): "%s:48bb3c5cbdb4822ae87914437ca3cceb733c7e1d" % (str(hello_ref), str(say_ref))) + def test_transitive_pattern_options(self): + say_content = """ +from conans import ConanFile + +class SayConan(ConanFile): + name = "Say" + version = "0.1" + options = {"myoption": [123, 234]} +""" + hello_content = """ +from conans import ConanFile + +class HelloConan(ConanFile): + name = "Hello" + version = "1.2" + requires = "Say/0.1@user/testing" + options = {"myoption": [123, 234]} +""" + chat_content = """ +from conans import ConanFile + +class ChatConan(ConanFile): + name = "Chat" + version = "2.3" + requires = "Hello/1.2@user/testing" + default_options = "*:myoption=234" +""" + self.retriever.conan(say_ref, say_content) + self.retriever.conan(hello_ref, hello_content) + deps_graph = self.root(chat_content) + + self.assertEqual(3, len(deps_graph.nodes)) + hello = _get_nodes(deps_graph, "Hello")[0] + say = _get_nodes(deps_graph, "Say")[0] + chat = _get_nodes(deps_graph, "Chat")[0] + self.assertEqual(_get_edges(deps_graph), {Edge(hello, say), Edge(chat, hello)}) + + self.assertEqual(hello.conan_ref, hello_ref) + self.assertEqual(say.conan_ref, say_ref) + + self._check_say(say.conanfile, options="myoption=234") + + conanfile = hello.conanfile + self.assertEqual(conanfile.options.values.dumps(), "myoption=234\nSay:myoption=234") + self.assertEqual(conanfile.info.full_options.dumps(), "myoption=234\nSay:myoption=234") + + conanfile = chat.conanfile + self.assertEqual(conanfile.options.values.dumps(), "Hello:myoption=234\nSay:myoption=234") + self.assertEqual(conanfile.info.full_options.dumps(), "Hello:myoption=234\nSay:myoption=234") + def test_transitive_two_levels_wrong_options(self): say_content = """ from conans import ConanFile @@ -1817,7 +1867,7 @@ def check(conanfile, options, settings): with self.assertRaises(ConanException) as cm: self.root(content, options="arch_independent=True", settings="os=Linux") self.assertIn(bad_value_msg("settings.os", "Linux", - ['Android', 'Arduino', 'FreeBSD', 'Macos', 'SunOS', "Windows", "iOS"]), + ['Android', 'Arduino', 'FreeBSD', 'Macos', 'SunOS', "Windows", "iOS", "tvOS", "watchOS"]), str(cm.exception)) def test_config_remove2(self): diff --git a/conans/test/model/version_ranges_test.py b/conans/test/model/version_ranges_test.py index e710338faad..71e4ac449a5 100644 --- a/conans/test/model/version_ranges_test.py +++ b/conans/test/model/version_ranges_test.py @@ -153,7 +153,7 @@ class MockSearchRemote(object): def __init__(self, packages=None): self.packages = packages or [] - def search_remotes(self, pattern): # @UnusedVariable + def search_remotes(self, pattern, ignorecase): # @UnusedVariable return self.packages diff --git a/conans/test/performance/large_project.py b/conans/test/performance/large_project.py index db92bb0e002..7b5973fddad 100644 --- a/conans/test/performance/large_project.py +++ b/conans/test/performance/large_project.py @@ -12,6 +12,8 @@ class PerformanceTest(unittest.TestCase): def large_project_test(self): client = TestClient() num = 250 + use_additional_infos = 20 + deep = True # True for N ... -> 3 -> 2 -> 1 -> 0, False for N -> 0, 3-> 0, 2->0, 1->0 for i in range(num): if i == 0: @@ -19,11 +21,13 @@ def large_project_test(self): else: if not deep: files = cpp_hello_conan_files("Hello%d" % i, "0.1", - ["Hello0/0.1@lasote/stable"], build=False) + ["Hello0/0.1@lasote/stable"], build=False, + use_additional_infos=use_additional_infos) else: files = cpp_hello_conan_files("Hello%d" % i, "0.1", ["Hello%s/0.1@lasote/stable" % (i-1)], - build=False) + build=False, + use_additional_infos=use_additional_infos) client.save(files, clean_first=True) client.run("export lasote/stable") @@ -31,11 +35,12 @@ def large_project_test(self): # Now lets depend on it if deep: files = cpp_hello_conan_files("HelloFinal", "0.1", - ["Hello%s/0.1@lasote/stable" % (num - 1)], build=False) + ["Hello%s/0.1@lasote/stable" % (num - 1)], build=False, + use_additional_infos=use_additional_infos) else: files = cpp_hello_conan_files("HelloFinal", "0.1", ["Hello%s/0.1@lasote/stable" % (i) for i in range(num)], - build=False) + build=False, use_additional_infos=use_additional_infos) client.save(files, clean_first=True) t1 = time.time() diff --git a/conans/test/util/build_sln_command_test.py b/conans/test/util/build_sln_command_test.py new file mode 100644 index 00000000000..745f3d7926a --- /dev/null +++ b/conans/test/util/build_sln_command_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +from conans.tools import build_sln_command, cpu_count +from conans.errors import ConanException +from conans.model.settings import Settings +from nose.plugins.attrib import attr + + +@attr('visual_studio') +class BuildSLNCommandTest(unittest.TestCase): + def no_arch_test(self): + with self.assertRaises(ConanException): + build_sln_command(Settings({}), sln_path='dummy.sln', targets=None, upgrade_project=False, + build_type='Debug', arch=None, parallel=False) + + def no_build_type_test(self): + with self.assertRaises(ConanException): + build_sln_command(Settings({}), sln_path='dummy.sln', targets=None, upgrade_project=False, + build_type=None, arch='x86', parallel=False) + + def positive_test(self): + command = build_sln_command(Settings({}), sln_path='dummy.sln', targets=None, upgrade_project=False, + build_type='Debug', arch='x86', parallel=False) + self.assertIn('msbuild dummy.sln', command) + self.assertIn('/p:Platform="x86"', command) + self.assertNotIn('devenv dummy.sln /upgrade', command) + self.assertNotIn('/m:%s' % cpu_count(), command) + self.assertNotIn('/target:teapot', command) + + def upgrade_test(self): + command = build_sln_command(Settings({}), sln_path='dummy.sln', targets=None, upgrade_project=True, + build_type='Debug', arch='x86_64', parallel=False) + self.assertIn('msbuild dummy.sln', command) + self.assertIn('/p:Platform="x64"', command) + self.assertIn('devenv dummy.sln /upgrade', command) + self.assertNotIn('/m:%s' % cpu_count(), command) + self.assertNotIn('/target:teapot', command) + + def parallel_test(self): + command = build_sln_command(Settings({}), sln_path='dummy.sln', targets=None, upgrade_project=True, + build_type='Debug', arch='armv7', parallel=False) + self.assertIn('msbuild dummy.sln', command) + self.assertIn('/p:Platform="ARM"', command) + self.assertIn('devenv dummy.sln /upgrade', command) + self.assertNotIn('/m:%s' % cpu_count(), command) + self.assertNotIn('/target:teapot', command) + + def target_test(self): + command = build_sln_command(Settings({}), sln_path='dummy.sln', targets=['teapot'], upgrade_project=False, + build_type='Debug', arch='x86', parallel=False) + self.assertIn('msbuild dummy.sln', command) + self.assertIn('/p:Platform="x86"', command) + self.assertNotIn('devenv dummy.sln /upgrade', command) + self.assertNotIn('/m:%s' % cpu_count(), command) + self.assertIn('/target:teapot', command) diff --git a/conans/test/util/conanfile_tools_test.py b/conans/test/util/conanfile_tools_test.py index 37b6a42e26a..41874c46441 100644 --- a/conans/test/util/conanfile_tools_test.py +++ b/conans/test/util/conanfile_tools_test.py @@ -92,6 +92,34 @@ def build(self): tmp_dir, file_path, text_file = self._save_files(file_content) self._build_and_check(tmp_dir, file_path, text_file, "ONE TWO DOH!") + def test_patch_new_delete(self): + conanfile = base_conanfile + ''' + def build(self): + from conans.tools import load, save + save("oldfile", "legacy code") + assert(os.path.exists("oldfile")) + patch_content = """--- /dev/null ++++ b/newfile +@@ -0,0 +0,3 @@ ++New file! ++New file! ++New file! +--- a/oldfile ++++ b/dev/null +@@ -0,1 +0,0 @@ +-legacy code +""" + patch(patch_string=patch_content) + self.output.info("NEW FILE=%s" % load("newfile")) + self.output.info("OLD FILE=%s" % os.path.exists("oldfile")) +''' + client = TestClient() + client.save({"conanfile.py": conanfile}) + client.run("create user/testing") + self.assertIn("test/1.9.10@user/testing: NEW FILE=New file!\nNew file!\nNew file!\n", + client.out) + self.assertIn("test/1.9.10@user/testing: OLD FILE=False", client.out) + def test_error_patch(self): file_content = base_conanfile + ''' def build(self): @@ -101,8 +129,8 @@ def build(self): ''' client = TestClient() client.save({"conanfile.py": file_content}) - client.run("install -g txt") - error = client.run("build", ignore_error=True) + client.run("install") + error = client.run("build .", ignore_error=True) self.assertTrue(error) self.assertIn("patch: error: no patch data found!", client.user_io.out) self.assertIn("ERROR: test/1.9.10@PROJECT: Error in build() method, line 12", diff --git a/conans/test/util/file_hashes_test.py b/conans/test/util/file_hashes_test.py index a25d280aed8..a55d7d90270 100644 --- a/conans/test/util/file_hashes_test.py +++ b/conans/test/util/file_hashes_test.py @@ -19,11 +19,11 @@ def md5_test(self): check_sha1(filepath, "eb599ec83d383f0f25691c184f656d40384f9435") check_sha256(filepath, "7365d029861e32c521f8089b00a6fb32daf0615025b69b599d1ce53501b845c2") - with self.assertRaisesRegexp(ConanException, "md5 signature failed for 'file.txt' file. Computed signature:"): + with self.assertRaisesRegexp(ConanException, "md5 signature failed for 'file.txt' file."): check_md5(filepath, "invalid") - with self.assertRaisesRegexp(ConanException, "sha1 signature failed for 'file.txt' file. Computed signature:"): + with self.assertRaisesRegexp(ConanException, "sha1 signature failed for 'file.txt' file."): check_sha1(filepath, "invalid") - with self.assertRaisesRegexp(ConanException, "sha256 signature failed for 'file.txt' file. Computed signature:"): + with self.assertRaisesRegexp(ConanException, "sha256 signature failed for 'file.txt' file."): check_sha256(filepath, "invalid") diff --git a/conans/test/util/output_test.py b/conans/test/util/output_test.py index c7bc27f5ca4..4c0a1798889 100644 --- a/conans/test/util/output_test.py +++ b/conans/test/util/output_test.py @@ -10,6 +10,8 @@ from conans.util.files import save, load import sys from conans.test.utils.tools import TestClient +import platform +from conans.paths import long_paths_support class OutputTest(unittest.TestCase): @@ -35,7 +37,8 @@ def source(self): self.output.info("TEXT ÑÜíóúéáàèòù абвгдежзийкл 做戏之说 ENDTEXT") """ client.save({"conanfile.py": conanfile}) - client.run("source") + client.run("install") + client.run("source .") self.assertIn("TEXT", client.user_io.out) self.assertIn("ENDTEXT", client.user_io.out) @@ -77,3 +80,30 @@ def unzip_output_test(self): self.assertRegexpMatches(output, "Unzipping [\d]+B") content = load(os.path.join(output_dir, "example.txt")) self.assertEqual(content, "Hello world!") + + def short_paths_unzip_output_test(self): + if long_paths_support: + return + tmp_dir = temp_folder() + file_path = os.path.join(tmp_dir, "src/"*40, "example.txt") + save(file_path, "Hello world!") + + zip_path = os.path.join(tmp_dir, 'example.zip') + zipf = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) + for root, _, files in os.walk(tmp_dir): + for f in files: + zipf.write(os.path.join(root, f), os.path.join("src/"*20, f)) + zipf.close() + + output_dir = os.path.join(tmp_dir, "dst/"*40, "output_dir") + new_out = StringIO() + old_out = sys.stdout + try: + import conans + conans.tools.set_global_instances(ConanOutput(new_out), None) + tools.unzip(zip_path, output_dir) + finally: + conans.tools.set_global_instances(ConanOutput(old_out), None) + + output = new_out.getvalue() + self.assertIn("ERROR: Error extract src/src", output) diff --git a/conans/test/util/read_only_test.py b/conans/test/util/read_only_test.py new file mode 100644 index 00000000000..1ccf4b51f04 --- /dev/null +++ b/conans/test/util/read_only_test.py @@ -0,0 +1,16 @@ +import unittest +from conans.test.utils.test_files import temp_folder +from conans.util.files import make_read_only, save, load +import os + + +class ReadOnlyTest(unittest.TestCase): + + def read_only_test(self): + folder = temp_folder() + f = os.path.join(folder, "file.txt") + save(f, "Hello World") + make_read_only(folder) + with self.assertRaises(IOError): + save(f, "Bye World") + self.assertEqual("Hello World", load(f)) \ No newline at end of file diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py index 4af2636ca7d..0f3b59ed01c 100644 --- a/conans/test/util/tools_test.py +++ b/conans/test/util/tools_test.py @@ -1,6 +1,5 @@ import os import platform -import tempfile import unittest from collections import namedtuple @@ -17,11 +16,13 @@ from conans.paths import CONANFILE from conans.test.utils.runner import TestRunner from conans.test.utils.test_files import temp_folder -from conans.test.utils.tools import TestClient, TestBufferConanOutput +from conans.test.utils.tools import TestClient, TestBufferConanOutput, TestRequester from conans.test.utils.visual_project_files import get_vs_project_files from conans.test.utils.context_manager import which -from conans.tools import OSInfo, SystemPackageTool, replace_in_file, AptTool, ChocolateyTool +from conans.tools import OSInfo, SystemPackageTool, replace_in_file, AptTool, ChocolateyTool,\ + set_global_instances from conans.util.files import save +import requests class RunnerMock(object): @@ -74,9 +75,6 @@ def cpu_count_test(self): def test_global_tools_overrided(self): client = TestClient() - tools._global_requester = None - tools._global_output = None - conanfile = """ from conans import ConanFile, tools @@ -85,26 +83,24 @@ class HelloConan(ConanFile): version = "0.1" def build(self): - assert(tools._global_requester != None) - assert(tools._global_output != None) + assert(tools.net._global_requester != None) + assert(tools.files._global_output != None) """ client.save({"conanfile.py": conanfile}) - client.run("install -g txt") - client.run("build") + + client.run("install") + client.run("build .") # Not test the real commmand get_command if it's setting the module global vars - tools._global_requester = None - tools._global_output = None tmp = temp_folder() conf = default_client_conf.replace("\n[proxies]", "\n[proxies]\nhttp = http://myproxy.com") os.mkdir(os.path.join(tmp, ".conan")) save(os.path.join(tmp, ".conan", CONAN_CONF), conf) with tools.environment_append({"CONAN_USER_HOME": tmp}): - conan_api = ConanAPIV1.factory() + conan_api, _, _ = ConanAPIV1.factory() conan_api.remote_list() - self.assertEquals(tools._global_requester.proxies, {"http": "http://myproxy.com"}) - - self.assertIsNotNone(tools._global_output.warn) + self.assertEquals(tools.net._global_requester.proxies, {"http": "http://myproxy.com"}) + self.assertIsNotNone(tools.files._global_output.warn) def test_environment_nested(self): with tools.environment_append({"A": "1", "Z": "40"}): @@ -445,26 +441,26 @@ def package(self): client.save(files) client.run("export lasote/stable") client.run("install Hello/1.2.1@lasote/stable --build -s arch=x86_64") - self.assertTrue("Release|x64", client.user_io.out) - self.assertTrue("Copied 1 '.exe' files: MyProject.exe", client.user_io.out) + self.assertIn("Release|x64", client.user_io.out) + self.assertIn("Copied 1 '.exe' files: 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.assertTrue("Release|x86", client.user_io.out) - self.assertTrue("Copied 1 '.exe' files: MyProject.exe", client.user_io.out) + self.assertIn("Release|x86", client.user_io.out) + self.assertIn("Copied 1 '.exe' files: 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.assertTrue("Debug|x86", client.user_io.out) - self.assertTrue("Copied 1 '.exe' files: MyProject.exe", client.user_io.out) + self.assertIn("Debug|x86", client.user_io.out) + self.assertIn("Copied 1 '.exe' files: MyProject.exe", client.user_io.out) def download_retries_test(self): out = TestBufferConanOutput() - + set_global_instances(out, requests) # Connection error with self.assertRaisesRegexp(ConanException, "HTTPConnectionPool"): tools.download("http://fakeurl3.es/nonexists", @@ -485,3 +481,28 @@ def download_retries_test(self): retry=3, retry_wait=0) self.assertTrue(os.path.exists(dest)) + + # overwrite = False + with self.assertRaises(ConanException): + tools.download("http://www.zlib.net/manual.html", + dest, out=out, + retry=3, retry_wait=0, overwrite=False) + + # overwrite = True + tools.download("http://www.zlib.net/manual.html", + dest, out=out, + retry=3, retry_wait=0, overwrite=True) + + self.assertTrue(os.path.exists(dest)) + + # Not authorized + with self.assertRaises(ConanException): + tools.download("https://httpbin.org/basic-auth/user/passwd", dest, overwrite=True) + + # Authorized + tools.download("https://httpbin.org/basic-auth/user/passwd", dest, auth=("user", "passwd"), + overwrite=True) + + # Authorized using headers + tools.download("https://httpbin.org/basic-auth/user/passwd", dest, + headers={"Authorization": "Basic dXNlcjpwYXNzd2Q="}, overwrite=True) diff --git a/conans/test/util/vcvars_env_test.py b/conans/test/util/vcvars_env_test.py index b4d4888382a..6125761cb5c 100644 --- a/conans/test/util/vcvars_env_test.py +++ b/conans/test/util/vcvars_env_test.py @@ -81,5 +81,5 @@ def build(self): files = {} files["conanfile.py"] = conanfile client.save(files) - client.run("build") + client.run("build .") # print client.user_io.out diff --git a/conans/test/util/which_test.py b/conans/test/util/which_test.py new file mode 100644 index 00000000000..b41a08671e9 --- /dev/null +++ b/conans/test/util/which_test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import os +import stat +import platform +from conans import tools +from conans.test.utils.test_files import temp_folder + + +class WhichTest(unittest.TestCase): + @staticmethod + def _touch(filename): + with open(filename, 'a'): + os.utime(filename, None) + + @staticmethod + def _add_executable_bit(filename): + if os.name == 'posix': + mode = os.stat(filename).st_mode + mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + os.chmod(filename, stat.S_IMODE(mode)) + + def test_which_positive(self): + tmp_dir = temp_folder() + fullname = os.path.join(tmp_dir, 'example.sh') + self._touch(fullname) + self._add_executable_bit(fullname) + with tools.environment_append({'PATH': tmp_dir}): + self.assertEqual(tools.which('example.sh'), fullname) + + def test_which_negative(self): + tmp_dir = temp_folder() + with tools.environment_append({'PATH': tmp_dir}): + self.assertIsNone(tools.which('example.sh')) + + def test_which_non_executable(self): + if platform.system() == "Windows": + """on Windows we always have executable permissions by default""" + return + tmp_dir = temp_folder() + fullname = os.path.join(tmp_dir, 'example.sh') + self._touch(fullname) + with tools.environment_append({'PATH': tmp_dir}): + self.assertIsNone(tools.which('example.sh')) diff --git a/conans/test/utils/conanfile.py b/conans/test/utils/conanfile.py index 2451b59b0d0..d283259b805 100644 --- a/conans/test/utils/conanfile.py +++ b/conans/test/utils/conanfile.py @@ -24,9 +24,14 @@ def __init__(self): class MockConanfile(object): - def __init__(self, settings): + def __init__(self, settings, runner=None): self.deps_cpp_info = MockDepsCppInfo() self.settings = settings + self.runner = runner + + def run(self, *args): + if self.runner: + self.runner(*args, output=None) class TestConanFile(object): diff --git a/conans/test/utils/cpp_test_files.py b/conans/test/utils/cpp_test_files.py index 8daeba08f77..edf40e9b237 100644 --- a/conans/test/utils/cpp_test_files.py +++ b/conans/test/utils/cpp_test_files.py @@ -149,7 +149,7 @@ def package(self): def package_info(self): self.cpp_info.libs = ["hello{name}"] - +{additional_info} def imports(self): self.copy(pattern="*.dylib", dst=".", src="lib") self.copy(pattern="*.dll", dst=".", src="bin") @@ -310,7 +310,8 @@ def cpp_hello_source_files(name="Hello", deps=None, private_includes=False, msg= def cpp_hello_conan_files(name="Hello", version="0.1", deps=None, language=0, static=True, private_includes=False, msg=None, dll_export=False, need_patch=False, pure_c=False, config=True, build=True, collect_libs=False, - use_cmake=True, cmake_targets=False, no_copy_source=False): + use_cmake=True, cmake_targets=False, no_copy_source=False, + use_additional_infos=0): """Generate hello_files, as described above, plus the necessary CONANFILE to manage it param number: integer, defining name of the conans Hello0, Hello1, HelloX @@ -345,13 +346,23 @@ def cpp_hello_conan_files(name="Hello", version="0.1", deps=None, language=0, st pure_c=pure_c, cmake_targets=cmake_targets) libcxx_remove = "del self.settings.compiler.libcxx" if pure_c else "" build_env = conanfile_build_cmake if use_cmake else conanfile_build_new_env + + info_tmp = """ + self.env_info.%s.append("2") + self.user_info.%s = "UserValue" """ + + res = "" + for i in range(use_additional_infos): + res += info_tmp % ("EnvVar%d" % i, "UserVar%d" % i) + conanfile = conanfile_template.format(name=name, version=version, requires=requires, language=language, static=static, libcxx_remove=libcxx_remove, - build=build_env) + build=build_env, + additional_info=res) if no_copy_source: conanfile = conanfile.replace("exports = '*'", """exports = '*' diff --git a/conans/test/utils/tools.py b/conans/test/utils/tools.py index b5261a3de9d..707eda9d3a7 100644 --- a/conans/test/utils/tools.py +++ b/conans/test/utils/tools.py @@ -4,6 +4,7 @@ import sys import uuid from collections import Counter +from contextlib import contextmanager from io import StringIO import requests @@ -35,8 +36,9 @@ TestServerLauncher) from conans.test.utils.runner import TestRunner from conans.test.utils.test_files import temp_folder -from conans.util.files import save_files, save +from conans.util.files import save_files, save, mkdir from conans.util.log import logger +from conans.tools import set_global_instances class TestingResponse(object): @@ -354,6 +356,18 @@ def default_compiler_visual_studio(self): def out(self): return self.user_io.out + @contextmanager + def chdir(self, newdir): + old_dir = self.current_folder + if not os.path.isabs(newdir): + newdir = os.path.join(old_dir, newdir) + mkdir(newdir) + self.current_folder = newdir + try: + yield + finally: + self.current_folder = old_dir + def _init_collaborators(self, user_io=None): output = TestBufferConanOutput() @@ -388,9 +402,7 @@ def _init_collaborators(self, user_io=None): # Handle remote connections self.remote_manager = RemoteManager(self.client_cache, auth_manager, self.user_io.out) - # Patch the globals in tools - tools._global_requester = requests - tools._global_output = self.user_io.out + set_global_instances(output, self.requester) def init_dynamic_vars(self, user_io=None): # Migration system @@ -407,7 +419,7 @@ def run(self, command_line, user_io=None, ignore_error=False): """ self.init_dynamic_vars(user_io) conan = Conan(self.client_cache, self.user_io, self.runner, self.remote_manager, - self.search_manager, settings_preprocessor) + self.search_manager, settings_preprocessor) outputer = CommandOutputer(self.user_io, self.client_cache) command = Command(conan, self.client_cache, self.user_io, outputer) args = shlex.split(command_line) @@ -426,6 +438,7 @@ def run(self, command_line, user_io=None, ignore_error=False): if not ignore_error and error: logger.error(self.user_io.out) + print(self.user_io.out) raise Exception("Command failed:\n%s" % command_line) self.all_output += str(self.user_io.out) @@ -439,3 +452,5 @@ def save(self, files, path=None, clean_first=False): if clean_first: shutil.rmtree(self.current_folder, ignore_errors=True) save_files(path, files) + if not files: + mkdir(self.current_folder) diff --git a/conans/test_integration/__init__.py b/conans/test_integration/__init__.py new file mode 100644 index 00000000000..678b2fae9ff --- /dev/null +++ b/conans/test_integration/__init__.py @@ -0,0 +1,3 @@ +import os + +CONAN_TEST_FOLDER = os.getenv('CONAN_TEST_FOLDER', None) diff --git a/conans/test_integration/meson_test.py b/conans/test_integration/meson_test.py new file mode 100644 index 00000000000..9e4598858ab --- /dev/null +++ b/conans/test_integration/meson_test.py @@ -0,0 +1,83 @@ +import os +import platform +import unittest + +import six +from future.moves import sys + +from conans.paths import CONANFILE +from conans.test.utils.cpp_test_files import cpp_hello_conan_files +from conans.test.utils.tools import TestClient +from conans.util.files import mkdir + + +class PkgConfigGeneratorTest(unittest.TestCase): + + def test_base(self): + pyver = sys.version_info + + # FIXME: Appveyor is not locating meson in py 3.4 + if platform.system() == "Windows" and (pyver[0] < 3 or pyver[1] < 5): + return + + client = TestClient(path_with_spaces=False) + self._export(client, "LIB_C", []) + self._export(client, "LIB_B", ["LIB_C"]) + self._export(client, "LIB_B2", []) + self._export(client, "LIB_A", ["LIB_B", "LIB_B2"]) + + consumer = """ +from conans import ConanFile, tools, Meson +import os + +class ConanFileToolsTest(ConanFile): + generators = "pkg_config" + requires = "LIB_A/0.1@conan/stable" + settings = "os", "compiler", "build_type" + + def build(self): + meson = Meson(self) + meson.configure() + meson.build() + +""" + meson_build = """ +project('conan_hello', 'c') +liba = dependency('LIB_A', version : '>=0') +executable('demo', 'main.c', dependencies: [liba]) +""" + + main_c = """ +#include "helloLIB_A.h" + +int main(){ + helloLIB_A(); +} +""" + + client.save({CONANFILE: consumer, + "meson.build": meson_build, + "main.c": main_c}, clean_first=True) + mkdir(os.path.join(client.current_folder, "build")) + client.current_folder = os.path.join(client.current_folder, "build") + client.run("install .. --build") + + if six.PY2: # Meson only available + return + + client.run("build .. --source_folder ..") + if platform.system() == "Windows": + command = "demo" + else: + command = './demo' + client.runner(command, cwd=os.path.join(client.current_folder)) + self.assertEqual(['Hello LIB_A', 'Hello LIB_B', 'Hello LIB_C', 'Hello LIB_B2'], + str(client.user_io.out).splitlines()[-4:]) + + def _export(self, client, libname, depsname): + files = cpp_hello_conan_files(libname, "0.1", + ["%s/0.1@conan/stable" % name for name in depsname], + build=six.PY3, pure_c=True) + client.save(files, clean_first=True) + files[CONANFILE] = files[CONANFILE].replace('generators = "cmake", "gcc"', "") + client.run("export conan/stable") diff --git a/conans/util/files.py b/conans/util/files.py index 3dbc734e4a4..c5e5ae5c721 100644 --- a/conans/util/files.py +++ b/conans/util/files.py @@ -9,6 +9,16 @@ import six from conans.util.log import logger import tarfile +import stat + + +def make_read_only(path): + for root, _, files in os.walk(path): + for f in files: + full_path = os.path.join(root, f) + mode = os.stat(full_path).st_mode + os.chmod(full_path, mode & ~ stat.S_IWRITE) + _DIRTY_FOLDER = ".dirty" @@ -85,23 +95,27 @@ def _generic_algorithm_sum(file_path, algorithm_name): def save(path, content, append=False): - ''' + """ Saves a file with given content Params: path: path to write file to load: contents to save in the file - ''' + """ try: os.makedirs(os.path.dirname(path)) except: pass + mode = 'wb' if not append else 'ab' + with open(path, mode) as handle: + handle.write(to_file_bytes(content)) + + +def to_file_bytes(content): if six.PY3: if not isinstance(content, bytes): content = bytes(content, "utf-8") - mode = 'wb' if not append else 'ab' - with open(path, mode) as handle: - handle.write(content) + return content def save_files(path, files): @@ -128,7 +142,6 @@ def relative_dirs(path): def _change_permissions(func, path, exc_info): - import stat if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWUSR) func(path) diff --git a/conans/util/locks.py b/conans/util/locks.py new file mode 100644 index 00000000000..e1505550b11 --- /dev/null +++ b/conans/util/locks.py @@ -0,0 +1,75 @@ +import fasteners +from conans.util.log import logger +import time +from conans.util.files import save, load + + +class NoLock(object): + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable + pass + + +class SimpleLock(object): + + def __init__(self, filename): + self._lock = fasteners.InterProcessLock(filename, logger=logger) + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable + self._lock.release() + + +READ_BUSY_DELAY = 0.5 +WRITE_BUSY_DELAY = 0.25 + + +class Lock(object): + + def __init__(self, folder): + self._count_file = folder + ".count" + self._count_lock_file = folder + ".count.lock" + + def _readers(self): + try: + return int(load(self._count_file)) + except IOError: + return 0 + + +class ReadLock(Lock): + + def __enter__(self): + while True: + with fasteners.InterProcessLock(self._count_lock_file, logger=logger): + readers = self._readers() + if readers >= 0: + save(self._count_file, str(readers + 1)) + break + time.sleep(READ_BUSY_DELAY) + + def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable + with fasteners.InterProcessLock(self._count_lock_file, logger=logger): + readers = self._readers() + save(self._count_file, str(readers - 1)) + + +class WriteLock(Lock): + + def __enter__(self): + while True: + with fasteners.InterProcessLock(self._count_lock_file, logger=logger): + readers = self._readers() + if readers == 0: + save(self._count_file, "-1") + break + time.sleep(WRITE_BUSY_DELAY) + + def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable + with fasteners.InterProcessLock(self._count_lock_file, logger=logger): + save(self._count_file, "0") diff --git a/conans/util/windows.py b/conans/util/windows.py index 72112433ccb..ea25fc3f850 100644 --- a/conans/util/windows.py +++ b/conans/util/windows.py @@ -35,7 +35,7 @@ def path_shortener(path, short_paths): True: Always shorten the path, create link if not existing None: Use shorten path only if already exists, not create """ - if short_paths is False or os.getenv("CONAN_USER_HOME_SHORT") == "None": + if short_paths is False: return path link = os.path.join(path, CONAN_LINK) if os.path.exists(link): diff --git a/pyinstaller.py b/pyinstaller.py index 0c33b54dd52..17bee0af011 100644 --- a/pyinstaller.py +++ b/pyinstaller.py @@ -9,8 +9,9 @@ def _install_pyintaller(pyinstaller_path): # try to install pyinstaller if not installed if not os.path.exists(pyinstaller_path): - subprocess.call('git clone https://github.com/pyinstaller/pyinstaller.git', - cwd=os.path.curdir, shell=True) + os.mkdir(pyinstaller_path) + subprocess.call('git clone https://github.com/pyinstaller/pyinstaller.git .', + cwd=pyinstaller_path, shell=True) subprocess.call('git checkout v3.2.1', cwd=pyinstaller_path, shell=True) @@ -25,7 +26,7 @@ def _run_bin(pyinstaller_path): def pyinstall(source_folder): - pyinstaller_path = os.path.join(os.path.curdir, 'pyinstaller') + pyinstaller_path = os.path.join(os.getcwd(), 'pyinstaller') _install_pyintaller(pyinstaller_path) for folder in ("conan", "conan_server", "conan_build_info"): @@ -37,7 +38,9 @@ def pyinstall(source_folder): conan_path = os.path.join(source_folder, 'conans', 'conan.py') conan_server_path = os.path.join(source_folder, 'conans', 'conan_server.py') conan_build_info_path = os.path.join(source_folder, "conans/build_info/command.py") - hidden = "--hidden-import=glob" + hidden = "--hidden-import=glob --hidden-import=pylint.reporters.text" + if platform.system() != "Windows": + hidden += " --hidden-import=setuptools.msvc" subprocess.call('python pyinstaller.py -y -p %s --console %s %s' % (source_folder, conan_path, hidden), cwd=pyinstaller_path, shell=True)