def __init__( self, base_folder=None, current_folder=None, servers=None, users=None, client_version=CLIENT_VERSION, min_server_compatible_version=MIN_SERVER_COMPATIBLE_VERSION, ): """ storage_folder: Local storage path current_folder: Current execution folder servers: dict of {remote_name: TestServer} logins is a list of (user, password) for auto input in order if required==> [("lasote", "mypass"), ("other", "otherpass")] """ if users is None: users = [(TESTING_REMOTE_PRIVATE_USER, TESTING_REMOTE_PRIVATE_PASS)] self.users = users self.servers = servers or {} self.client_version = Version(client_version) self.min_server_compatible_version = Version(min_server_compatible_version) self.base_folder = base_folder or temp_folder() # Define storage_folder, if not, it will be readed from conf file and pointed to real user home self.storage_folder = os.path.join(self.base_folder, ".conan", "data") # First initialization for set paths (some test copies data before first real initialization) self.init_dynamic_vars() self.default_settings(get_env("CONAN_COMPILER", "gcc"), get_env("CONAN_COMPILER_VERSION", "4.8")) logger.debug("Client storage = %s" % self.storage_folder) self.current_folder = current_folder or temp_folder()
def __init__(self, base_folder=None, current_folder=None, servers=None, users=None, client_version=CLIENT_VERSION, min_server_compatible_version=MIN_SERVER_COMPATIBLE_VERSION): """ storage_folder: Local storage path current_folder: Current execution folder servers: dict of {remote_name: TestServer} logins is a list of (user, password) for auto input in order if required==> [("lasote", "mypass"), ("other", "otherpass")] """ self.users = users or {"default": [(TESTING_REMOTE_PRIVATE_USER, TESTING_REMOTE_PRIVATE_PASS)]} self.servers = servers or {} self.client_version = Version(str(client_version)) self.min_server_compatible_version = Version(str(min_server_compatible_version)) self.base_folder = base_folder or temp_folder() # Define storage_folder, if not, it will be read from conf file & pointed to real user home self.storage_folder = os.path.join(self.base_folder, ".conan", "data") self.paths = ConanPaths(self.base_folder, self.storage_folder, TestBufferConanOutput()) self.default_settings(get_env("CONAN_COMPILER", "gcc"), get_env("CONAN_COMPILER_VERSION", "4.8")) self.init_dynamic_vars() save(self.paths.registry, "") registry = RemoteRegistry(self.paths.registry, TestBufferConanOutput()) for name, server in self.servers.items(): registry.add(name, server.fake_url) logger.debug("Client storage = %s" % self.storage_folder) self.current_folder = current_folder or temp_folder()
def _is_sudo_enabled(): if "CONAN_SYSREQUIRES_SUDO" not in os.environ: if os.name == 'posix' and os.geteuid() == 0: return False if os.name == 'nt': return False return get_env("CONAN_SYSREQUIRES_SUDO", True)
def _get_sysrequire_mode(): allowed_modes= ("enabled", "verify", "disabled") mode = get_env("CONAN_SYSREQUIRES_MODE", "enabled") mode_lower = mode.lower() if mode_lower not in allowed_modes: raise ConanException("CONAN_SYSREQUIRES_MODE=%s is not allowed, allowed modes=%r" % (mode, allowed_modes)) return mode_lower
def package(self): """Generate the info txt files and calls the conanfile package method. """ # FIXME: Is weak to assign here the recipe_hash manifest = self._client_cache.load_manifest(self._conan_ref) self._conan_file.info.recipe_hash = manifest.summary_hash # Creating ***info.txt files save(os.path.join(self.build_folder, CONANINFO), self._conan_file.info.dumps()) self._out.info("Generated %s" % CONANINFO) save(os.path.join(self.build_folder, BUILD_INFO), TXTGenerator(self._conan_file).content) self._out.info("Generated %s" % BUILD_INFO) os.chdir(self.build_folder) if getattr(self._conan_file, 'no_copy_source', False): source_folder = self.source_folder else: source_folder = self.build_folder with get_env_context_manager(self._conan_file): install_folder = self.build_folder # While installing, the infos goes to build folder create_package(self._conan_file, source_folder, self.build_folder, self.package_folder, install_folder, self._out) if get_env("CONAN_READ_ONLY_CACHE", False): make_read_only(self.package_folder)
def _format_conanfile_exception(scope, method, exception): """ It will iterate the traceback lines, when it finds that the source code is inside the users conanfile it "start recording" the messages, when the trace exits the conanfile we return the traces. """ import sys import traceback if get_env("CONAN_VERBOSE_TRACEBACK", False): return traceback.format_exc() try: conanfile_reached = False tb = sys.exc_info()[2] index = 0 content_lines = [] while True: # If out of index will raise and will be captured later filepath, line, name, contents = traceback.extract_tb(tb, 40)[index] # 40 levels of nested functions max, get the latest if "conanfile.py" not in filepath: # Avoid show trace from internal conan source code if conanfile_reached: # The error goes to internal code, exit print break else: if not conanfile_reached: # First line msg = "%s: Error in %s() method" % (scope, method) msg += ", line %d\n\t%s" % (line, contents) else: msg = "while calling '%s', line %d\n\t%s" % (name, line, contents) if line else "\n\t%s" % contents content_lines.append(msg) conanfile_reached = True index += 1 except: pass ret = "\n".join(content_lines) ret += "\n\t%s: %s" % (exception.__class__.__name__, str(exception)) return ret
def storage_path(self): try: result = os.path.expanduser(self.storage["path"]) except KeyError: result = None result = get_env("CONAN_STORAGE_PATH", result) return result
def storage_path(self): # Try with CONAN_STORAGE_PATH result = get_env('CONAN_STORAGE_PATH', None) # Try with conan.conf "path" if not result: try: env_conan_user_home = os.getenv("CONAN_USER_HOME") # if env var is declared, any specified path will be relative to CONAN_USER_HOME # even with the ~/ if env_conan_user_home: storage = self.storage["path"] if storage[:2] == "~/": storage = storage[2:] result = os.path.join(env_conan_user_home, storage) else: result = self.storage["path"] except KeyError: pass # expand the result and check if absolute if result: result = conan_expand_user(result) if not os.path.isabs(result): raise ConanException("Conan storage path has to be an absolute path") return result
def get_package(conanfile, package_ref, package_folder, output, recorder, proxy, update): # TODO: This access to proxy attributes has to be improved remote_manager = proxy._remote_manager registry = proxy.registry try: if update: _remove_if_outdated(package_folder, package_ref, proxy, output) local_package = os.path.exists(package_folder) if local_package: output.success('Already installed!') log_package_got_from_local_cache(package_ref) recorder.package_fetched_from_cache(package_ref) return False else: remote = registry.get_ref(package_ref.conan) # remote will be defined, as package availability has been checked from installer remote_manager.get_package(conanfile, package_ref, package_folder, remote, output, recorder) if get_env("CONAN_READ_ONLY_CACHE", False): make_read_only(package_folder) recorder.package_downloaded(package_ref, remote.url) return True except BaseException as e: output.error("Exception while getting package: %s" % str(package_ref.package_id)) output.error("Exception: %s %s" % (type(e), str(e))) _clean_package(package_folder, output) raise
def vs_installation_path(version, preference=None): vs_installation_path = None if not preference: env_prefs = get_env("CONAN_VS_INSTALLATION_PREFERENCE", list()) if env_prefs: preference = env_prefs else: # default values preference = ["Enterprise", "Professional", "Community", "BuildTools"] # Try with vswhere() try: legacy_products = vswhere(legacy=True) all_products = vswhere(products=["*"]) products = legacy_products + all_products except ConanException: products = None vs_paths = [] if products: # remove repeated products seen_products = [] for product in products: if product not in seen_products: seen_products.append(product) # Append products with "productId" by order of preference for product_type in preference: for product in seen_products: product = dict(product) if product["installationVersion"].startswith(("%d." % int(version))) and "productId" in product: if product_type in product["productId"]: vs_paths.append(product["installationPath"]) # Append products without "productId" (Legacy installations) for product in seen_products: product = dict(product) if product["installationVersion"].startswith(("%d." % int(version))) and "productId" not in product: vs_paths.append(product["installationPath"]) # If vswhere does not find anything or not available, try with vs_comntools() if not vs_paths: vs_path = vs_comntools(version) if vs_path: sub_path_to_remove = os.path.join("", "Common7", "Tools", "") # Remove '\\Common7\\Tools\\' to get same output as vswhere if vs_path.endswith(sub_path_to_remove): vs_path = vs_path[:-(len(sub_path_to_remove)+1)] vs_installation_path = vs_path else: vs_installation_path = vs_paths[0] return vs_installation_path
def get_command(): def instance_remote_manager(client_cache): requester = requests.Session() requester.proxies = client_cache.conan_config.proxies # Verify client version against remotes version_checker_requester = VersionCheckerRequester(requester, Version(CLIENT_VERSION), Version(MIN_SERVER_COMPATIBLE_VERSION), out) # To handle remote connections rest_api_client = RestApiClient(out, requester=version_checker_requester) # To store user and token localdb = LocalDB(client_cache.localdb) # Wraps RestApiClient to add authentication support (same interface) auth_manager = ConanApiAuthManager(rest_api_client, user_io, localdb) # Handle remote connections remote_manager = RemoteManager(client_cache, auth_manager, out) return remote_manager use_color = get_env("CONAN_COLOR_DISPLAY", 1) if use_color and hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): import colorama colorama.init() color = True else: color = False out = ConanOutput(sys.stdout, color) user_io = UserIO(out=out) user_folder = os.getenv("CONAN_USER_HOME", conan_expand_user("~")) try: # To capture exceptions in conan.conf parsing client_cache = ClientCache(user_folder, None, out) # obtain a temp ConanManager instance to execute the migrations remote_manager = instance_remote_manager(client_cache) # Get a DiskSearchManager search_adapter = DiskSearchAdapter() search_manager = DiskSearchManager(client_cache, search_adapter) manager = ConanManager(client_cache, user_io, ConanRunner(), remote_manager, search_manager) client_cache = migrate_and_get_client_cache(user_folder, out, manager) except Exception as e: out.error(str(e)) sys.exit(True) # Get the new command instance after migrations have been done remote_manager = instance_remote_manager(client_cache) # Get a search manager search_adapter = DiskSearchAdapter() search_manager = DiskSearchManager(client_cache, search_adapter) command = Command(client_cache, user_io, ConanRunner(), remote_manager, search_manager) return command
def configure_logger(): from conans.util.env_reader import get_env # #### LOGGER, MOVED FROM CONF BECAUSE OF MULTIPLE PROBLEM WITH CIRCULAR INCLUDES ##### logging_level = get_env('CONAN_LOGGING_LEVEL', logging.CRITICAL) logging_file = get_env('CONAN_LOGGING_FILE', None) # None is stdout logger = logging.getLogger('conans') if logging_file is not None: hdlr = logging.FileHandler(logging_file) else: hdlr = StreamHandler(sys.stderr) formatter = MultiLineFormatter('%(levelname)-6s:%(filename)-15s[%(lineno)d]: ' '%(message)s [%(asctime)s]') hdlr.setFormatter(formatter) for hand in logger.handlers: logger.removeHandler(hand) logger.addHandler(hdlr) logger.setLevel(logging_level) return logger
def _build_folder(test_build_folder, profile, base_folder): # Use the specified build folder when available. if test_build_folder: return (os.path.abspath(test_build_folder), False) # Otherwise, generate a new test folder depending on the configuration. if get_env('CONAN_TEMP_TEST_FOLDER', False): return (tempfile.mkdtemp(prefix='conans'), True) sha = hashlib.sha1("".join(profile.dumps()).encode()).hexdigest() build_folder = os.path.join(base_folder, "build", sha) return (build_folder, False)
def factory(): """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) # To handle remote connections put_headers = client_cache.read_put_headers() rest_api_client = RestApiClient(out, requester=version_checker_requester, put_headers=put_headers) # To store user and token localdb = LocalDB(client_cache.localdb) # Wraps RestApiClient to add authentication support (same interface) auth_manager = ConanApiAuthManager(rest_api_client, user_io, localdb) # Handle remote connections remote_manager = RemoteManager(client_cache, auth_manager, out) return remote_manager use_color = get_env("CONAN_COLOR_DISPLAY", 1) if use_color and hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): import colorama colorama.init() color = True else: color = False out = ConanOutput(sys.stdout, color) user_io = UserIO(out=out) user_folder = os.getenv("CONAN_USER_HOME", conan_expand_user("~")) try: client_cache = migrate_and_get_client_cache(user_folder, out) except Exception as e: out.error(str(e)) raise with tools.environment_append(client_cache.conan_config.env_vars): # Adjust CONAN_LOGGING_LEVEL with the env readed conans.util.log.logger = configure_logger() # Get the new command instance after migrations have been done remote_manager = instance_remote_manager(client_cache) # Get a search manager search_adapter = DiskSearchAdapter() search_manager = DiskSearchManager(client_cache, search_adapter) conan = Conan(client_cache, user_io, get_conan_runner(), remote_manager, search_manager) return conan
def storage_path(self): try: conan_user_home = os.getenv("CONAN_USER_HOME") if conan_user_home: storage = self.storage["path"] if storage[:2] == "~/": storage = storage[2:] result = os.path.join(conan_user_home, storage) else: result = conan_expand_user(self.storage["path"]) except KeyError: result = None result = get_env('CONAN_STORAGE_PATH', result) return result
def update(self, down_reqs, output, own_ref, down_ref): """ Compute actual requirement values when downstream values are defined param down_reqs: the current requirements as coming from downstream to override current requirements param own_ref: ConanFileReference of the current conanfile param down_ref: ConanFileReference of the downstream that is overriding values or None return: new Requirements() value to be passed upstream """ assert isinstance(down_reqs, Requirements) assert isinstance(own_ref, ConanFileReference) if own_ref else True assert isinstance(down_ref, ConanFileReference) if down_ref else True error_on_override = get_env("CONAN_ERROR_ON_OVERRIDE", False) new_reqs = down_reqs.copy() if own_ref: new_reqs.pop(own_ref.name, None) for name, req in self.items(): if req.private: continue if name in down_reqs: other_req = down_reqs[name] # update dependency other_ref = other_req.ref if other_ref and other_ref != req.ref: msg = "requirement %s overridden by %s to %s " \ % (req.ref, down_ref or "your conanfile", other_ref) if error_on_override and not other_req.override: raise ConanException(msg) msg = "%s %s" % (own_ref, msg) output.warn(msg) req.ref = other_ref new_reqs[name] = req return new_reqs
def _format_conanfile_exception(scope, method, exception): """ It will iterate the traceback lines, when it finds that the source code is inside the users conanfile it "start recording" the messages, when the trace exits the conanfile we return the traces. """ import sys import traceback if get_env("CONAN_VERBOSE_TRACEBACK", False): return traceback.format_exc() try: conanfile_reached = False tb = sys.exc_info()[2] index = 0 content_lines = [] while True: # If out of index will raise and will be captured later filepath, line, name, contents = traceback.extract_tb( tb, 40)[index] # 40 levels of nested functions max, get the latest if "conanfile.py" not in filepath: # Avoid show trace from internal conan source code if conanfile_reached: # The error goes to internal code, exit print break else: if not conanfile_reached: # First line msg = "%s: Error in %s() method" % (scope, method) msg += ", line %d\n\t%s" % (line, contents) else: msg = "while calling '%s', line %d\n\t%s" % ( name, line, contents) if line else "\n\t%s" % contents content_lines.append(msg) conanfile_reached = True index += 1 except: pass ret = "\n".join(content_lines) ret += "\n\t%s: %s" % (exception.__class__.__name__, str(exception)) return ret
def source(self): filename = "tcl{}-src.tar.gz".format(self.version) url = "https://downloads.sourceforge.net/project/tcl/Tcl/{}/{}".format( self.version, filename) sha256 = "ad0cd2de2c87b9ba8086b43957a0de3eb2eb565c7159d5f53ccbba3feb915f4e" dlfilepath = os.path.join(tempfile.gettempdir(), filename) if os.path.exists(dlfilepath) and not get_env("TCL_FORCE_DOWNLOAD", False): self.output.info( "Skipping download. Using cached {}".format(dlfilepath)) else: self.output.info("Downloading {} from {}".format(self.name, url)) tools.download(url, dlfilepath) tools.check_sha256(dlfilepath, sha256) tools.untargz(dlfilepath) extracted_dir = "{}{}".format(self.name, self.version) os.rename(extracted_dir, self._source_subfolder) unix_config_dir = self._get_configure_dir("unix") # When disabling 64-bit support (in 32-bit), this test must be 0 in order to use "long long" for 64-bit ints # (${tcl_type_64bit} can be either "__int64" or "long long") tools.replace_in_file(os.path.join(unix_config_dir, "configure"), "(sizeof(${tcl_type_64bit})==sizeof(long))", "(sizeof(${tcl_type_64bit})!=sizeof(long))") unix_makefile_in = os.path.join(unix_config_dir, "Makefile.in") # Avoid building internal libraries as shared libraries tools.replace_in_file(unix_makefile_in, "--enable-shared --enable-threads", "--enable-threads") # Avoid clearing CFLAGS and LDFLAGS in the makefile tools.replace_in_file(unix_makefile_in, "\nCFLAGS\t", "\n#CFLAGS\t") tools.replace_in_file(unix_makefile_in, "\nLDFLAGS\t", "\n#LDFLAGS\t") # Use CFLAGS and CPPFLAGS as argument to CC tools.replace_in_file(unix_makefile_in, "${CFLAGS}", "${CFLAGS} ${CPPFLAGS}")
def _validate_fpic(conanfile): v2_mode = get_env(CONAN_V2_MODE_ENVVAR, False) # FIXME: The toolchain() has dissapeared, but now it is integrated with generators. # FIXME: Not sure this check should be here, this could raise before the graph is fully evaluated toolchain = hasattr(conanfile, "toolchain") if not (toolchain or v2_mode): return fpic = conanfile.options.get_safe("fPIC") if fpic is None: return os_ = conanfile.settings.get_safe("os") if os_ and "Windows" in os_: if v2_mode: raise ConanInvalidConfiguration("fPIC option defined for Windows") conanfile.output.error("fPIC option defined for Windows") return shared = conanfile.options.get_safe("shared") if shared: if v2_mode: raise ConanInvalidConfiguration( "fPIC option defined for a shared library") conanfile.output.error("fPIC option defined for a shared library")
def write_generators(conanfile, path, output): """ produces auxiliary files, required to build a project or a package. """ for generator_name in conanfile.generators: try: generator_class = registered_generators[generator_name] except KeyError: raise ConanException("Invalid generator '%s'. Available types: %s" % (generator_name, ", ".join(registered_generators.available))) try: generator = generator_class(conanfile) except TypeError: # To allow old-style generator packages to work (e.g. premake) output.warn("Generator %s failed with new __init__(), trying old one") 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: output.warn("Generator %s is multifile. Property 'filename' not used" % (generator_name,)) for k, v in content.items(): v = normalize(v) output.info("Generator %s created %s" % (generator_name, k)) save(join(path, k), v) else: content = normalize(content) output.info("Generator %s created %s" % (generator_name, generator.filename)) save(join(path, generator.filename), content) except Exception as e: if get_env("CONAN_VERBOSE_TRACEBACK", False): output.error(traceback.format_exc()) output.error("Generator %s(file:%s) failed\n%s" % (generator_name, generator.filename, str(e))) raise ConanException(e)
def write_generators(conanfile, path, output): """ produces auxiliary files, required to build a project or a package. """ for generator_name in conanfile.generators: try: generator_class = registered_generators[generator_name] except KeyError: raise ConanException("Invalid generator '%s'. Available types: %s" % (generator_name, ", ".join(registered_generators.available))) try: generator = generator_class(conanfile) except TypeError: # To allow old-style generator packages to work (e.g. premake) output.warn("Generator %s failed with new __init__(), trying old one") 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: output.warn("Generator %s is multifile. Property 'filename' not used" % (generator_name,)) for k, v in content.items(): v = normalize(v) output.info("Generator %s created %s" % (generator_name, k)) save(join(path, k), v, only_if_modified=True) else: content = normalize(content) output.info("Generator %s created %s" % (generator_name, generator.filename)) save(join(path, generator.filename), content, only_if_modified=True) except Exception as e: if get_env("CONAN_VERBOSE_TRACEBACK", False): output.error(traceback.format_exc()) output.error("Generator %s(file:%s) failed\n%s" % (generator_name, generator.filename, str(e))) raise ConanException(e)
def run(self, command_line, user_io=None, ignore_error=False): """ run a single command as in the command line. If user or password is filled, user_io will be mocked to return this tuple if required """ self.init_dynamic_vars(user_io) with tools.environment_append(self.client_cache.conan_config.env_vars): # Settings preprocessor interactive = not get_env("CONAN_NON_INTERACTIVE", False) conan = Conan(self.client_cache, self.user_io, self.runner, self.remote_manager, self.search_manager, settings_preprocessor, interactive=interactive) outputer = CommandOutputer(self.user_io, self.client_cache) command = Command(conan, self.client_cache, self.user_io, outputer) args = shlex.split(command_line) current_dir = os.getcwd() os.chdir(self.current_folder) old_path = sys.path[:] sys.path.append(os.path.join(self.client_cache.conan_folder, "python")) old_modules = list(sys.modules.keys()) try: error = command.run(args) finally: sys.path = old_path os.chdir(current_dir) # Reset sys.modules to its prev state. A .copy() DOES NOT WORK added_modules = set(sys.modules).difference(old_modules) for added in added_modules: sys.modules.pop(added, None) 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) return error
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 package_folder = self._client_cache.package(package_reference, conan_file.short_paths) # If already exists do not dirt the output, the common situation # is that package is already installed and OK. If don't, the proxy # will print some other message about it if not os.path.exists(package_folder): self._out.info("Retrieving package %s" % package_reference.package_id) if self._remote_proxy.get_package(package_reference, 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)
def run(self, command_line, user_io=None, ignore_error=False): """ run a single command as in the command line. If user or password is filled, user_io will be mocked to return this tuple if required """ self.init_dynamic_vars(user_io) with tools.environment_append(self.client_cache.conan_config.env_vars): # Settings preprocessor interactive = not get_env("CONAN_NON_INTERACTIVE", False) conan = Conan(self.client_cache, self.user_io, self.runner, self.remote_manager, settings_preprocessor, interactive=interactive) outputer = CommandOutputer(self.user_io, self.client_cache) command = Command(conan, self.client_cache, self.user_io, outputer) args = shlex.split(command_line) current_dir = os.getcwd() os.chdir(self.current_folder) old_path = sys.path[:] sys.path.append(os.path.join(self.client_cache.conan_folder, "python")) old_modules = list(sys.modules.keys()) try: error = command.run(args) finally: sys.path = old_path os.chdir(current_dir) # Reset sys.modules to its prev state. A .copy() DOES NOT WORK added_modules = set(sys.modules).difference(old_modules) for added in added_modules: sys.modules.pop(added, None) 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) return error
def setUp(self): revisions_enabled = get_env("TESTING_REVISIONS_ENABLED", False) self.server = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")]) self.client = TestClient(servers={"default": self.server}) self.client.save({"conanfile.py": GenConanfile()}) self.client.run("create . Pkg/0.1@user/testing") self.client.run("upload * --all --confirm -r default") # Check files are uploded in this order: conan_package.tgz, conaninfo.txt, conanmanifest.txt order1 = str(self.client.out).find("Uploading conan_package.tgz") order2 = str(self.client.out).find("Uploading conaninfo.txt", order1) order3 = str(self.client.out).find("Uploading conanmanifest.txt", order2) self.assertTrue(order1 < order2 < order3) rrev = "f3367e0e7d170aa12abccb175fee5f97" if revisions_enabled else "0" pref_str = "Pkg/0.1@user/testing#%s" % rrev prev = "83c38d3b4e5f1b8450434436eec31b00" if revisions_enabled else "0" self.pref = pref = PackageReference(ConanFileReference.loads(pref_str), NO_SETTINGS_PACKAGE_ID, prev) self.manifest_path = self.server.server_store.get_package_file_path( pref, "conanmanifest.txt") self.info_path = self.server.server_store.get_package_file_path( pref, "conaninfo.txt") self.tgz_path = self.server.server_store.get_package_file_path( pref, "conan_package.tgz")
def _init_collaborators(self, user_io=None): output = TestBufferConanOutput() self.user_io = user_io or MockedUserIO(self.users, out=output) self.client_cache = ClientCache(self.base_folder, self.storage_folder, output) self.runner = TestRunner(output, runner=self.conan_runner) # Check if servers are real real_servers = False for server in self.servers.values(): if isinstance(server, str): # Just URI real_servers = True with tools.environment_append(self.client_cache.conan_config.env_vars): if real_servers: requester = requests.Session() else: if self.requester_class: requester = self.requester_class(self.servers) else: requester = TestRequester(self.servers) self.requester = ConanRequester(requester, self.client_cache, get_request_timeout()) self.hook_manager = HookManager(self.client_cache.hooks_path, get_env("CONAN_HOOKS", list()), self.user_io.out) self.localdb, self.rest_api_client, self.remote_manager = Conan.instance_remote_manager( self.requester, self.client_cache, self.user_io, self.client_version, self.min_server_compatible_version, self.hook_manager) self.rest_api_client.block_v2 = self.block_v2 return output, self.requester
class MultiRemoteTest(unittest.TestCase): def setUp(self): self.servers = OrderedDict() self.users = {} for i in range(3): test_server = TestServer() self.servers["remote%d" % i] = test_server self.users["remote%d" % i] = [("lasote", "mypass")] self.client = TestClient(servers=self.servers, users=self.users) def test_predefine_remote(self): self.client.save({"conanfile.py": GenConanfile("Hello0", "0.1")}) self.client.run("export . lasote/stable") self.client.run("upload Hello0/0.1@lasote/stable -r=remote0") self.client.run("upload Hello0/0.1@lasote/stable -r=remote1") self.client.run("upload Hello0/0.1@lasote/stable -r=remote2") self.client.run('remove "*" -f') self.client.run("remote add_ref Hello0/0.1@lasote/stable remote1") self.client.run("install Hello0/0.1@lasote/stable --build=missing") self.assertIn( "Hello0/0.1@lasote/stable: Retrieving from predefined remote 'remote1'", self.client.out) self.client.run("remote list_ref") self.assertIn(": remote1", self.client.out) def test_upload(self): ref = ConanFileReference.loads("Hello0/0.1@lasote/stable") self.client.save({"conanfile.py": GenConanfile("Hello0", "0.1")}) self.client.run("export . lasote/stable") self.client.run("upload %s" % str(ref)) self.client.run("info %s" % str(ref)) self.assertIn("remote0=http://", self.client.out) # The remote, once fixed does not change self.client.run("upload %s -r=remote1" % str(ref)) self.client.run("info %s" % str(ref)) self.assertIn("remote0=http://", self.client.out) # Now install it in other machine from remote 0 client2 = TestClient(servers=self.servers, users=self.users) client2.run("install %s --build=missing" % str(ref)) client2.run("info %s" % str(ref)) self.assertIn("remote0=http://", client2.out) # Now install it in other machine from remote 1 servers = self.servers.copy() servers.pop("remote0") client3 = TestClient(servers=servers, users=self.users) client3.run("install %s --build=missing" % str(ref)) client3.run("info %s" % str(ref)) self.assertIn("remote1=http://", client3.out) def test_fail_when_not_notfound(self): """ If a remote fails with a 404 it has to keep looking in the next remote, but if it fails by any other reason it has to stop """ servers = OrderedDict() servers["s0"] = TestServer() servers["s1"] = TestServer() servers["s2"] = TestServer() client = TestClient(servers=servers, users=self.users) client.save({"conanfile.py": GenConanfile("MyLib", "0.1")}) client.run("create . lasote/testing") client.run("user lasote -p mypass -r s1") client.run("upload MyLib* -r s1 -c") servers[ "s1"].fake_url = "http://asdlhaljksdhlajkshdljakhsd.com" # Do not exist client2 = TestClient(servers=servers, users=self.users) err = client2.run("install MyLib/0.1@conan/testing --build=missing", assert_error=True) self.assertTrue(err) self.assertIn("MyLib/0.1@conan/testing: Trying with 's0'...", client2.out) self.assertIn("MyLib/0.1@conan/testing: Trying with 's1'...", client2.out) self.assertIn( "Unable to connect to s1=http://asdlhaljksdhlajkshdljakhsd.com", client2.out) # s2 is not even tried self.assertNotIn("MyLib/0.1@conan/testing: Trying with 's2'...", client2.out) def test_install_from_remotes(self): for i in range(3): ref = ConanFileReference.loads("Hello%d/0.1@lasote/stable" % i) self.client.save( {"conanfile.py": GenConanfile("Hello%d" % i, "0.1")}) self.client.run("export . lasote/stable") self.client.run("upload %s -r=remote%d" % (str(ref), i)) self.client.run("info %s" % str(ref)) self.assertIn("remote%d=http://" % i, self.client.out) # Now install it in other machine from remote 0 client2 = TestClient(servers=self.servers, users=self.users) refs = [ "Hello0/0.1@lasote/stable", "Hello1/0.1@lasote/stable", "Hello2/0.1@lasote/stable" ] client2.save({ "conanfile.py": GenConanfile("HelloX", "0.1").with_requires(*refs) }) client2.run("install . --build=missing") self.assertIn("Hello0/0.1@lasote/stable from 'remote0'", client2.out) self.assertIn("Hello1/0.1@lasote/stable from 'remote1'", client2.out) self.assertIn("Hello2/0.1@lasote/stable from 'remote2'", client2.out) client2.run("info .") self.assertIn("Remote: remote0=http://", client2.out) self.assertIn("Remote: remote1=http://", client2.out) self.assertIn("Remote: remote2=http://", client2.out) @pytest.mark.skipif( get_env("TESTING_REVISIONS_ENABLED", False), reason= "This test is not valid for revisions, where we keep iterating the " "remotes for searching a package for the same recipe revision") def test_package_binary_remote(self): # https://github.com/conan-io/conan/issues/3882 # Upload recipe + package to remote1 and remote2 reference = "Hello/0.1@lasote/stable" self.client.save({"conanfile.py": GenConanfile()}) self.client.run("create . %s" % reference) self.client.run("upload %s -r=remote0 --all" % reference) self.client.run("upload %s -r=remote2 --all" % reference) ref = ConanFileReference.loads(reference) # Remove only binary from remote1 and everything in local self.client.run("remove -f %s -p -r remote0" % reference) self.client.run('remove "*" -f') self.servers.pop("remote1") # Now install it from a client, it won't find the binary in remote2 self.client.run("install %s" % reference, assert_error=True) self.assertIn("Can't find a 'Hello/0.1@lasote/stable' package", self.client.out) self.assertNotIn("remote2", self.client.out) self.client.run("install %s -r remote2" % reference) self.assertIn( "Package installed 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", self.client.out) self.assertIn("Hello/0.1@lasote/stable from 'remote0' - Cache", self.client.out) metadata = self.client.cache.package_layout(ref).load_metadata() self.assertEqual(metadata.recipe.remote, "remote0") self.assertEqual( metadata.packages["5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9"]. remote, "remote2") client2 = TestClient(servers=self.servers, users=self.users) time.sleep(1) # Make sure timestamps increase client2.save({"conanfile.py": str(GenConanfile()) + " # Comment"}) client2.run("create . %s" % reference) client2.run("upload %s -r=remote2 --all" % reference) # Install from client, it should update the package from remote2 self.client.run("install %s --update" % reference) self.assertNotIn( "Hello/0.1@lasote/stable: WARN: Can't update, no package in remote", self.client.out) self.assertIn( "Hello/0.1@lasote/stable:" "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Update", self.client.out) self.assertIn("Downloading conan_package.tgz", self.client.out)
class BuildOrderTest(unittest.TestCase): def single_consumer_test(self): # https://github.com/conan-io/conan/issues/5727 client = TestClient() client.save({"conanfile.py": GenConanfile("test4", "0.1")}) client.run( "lock create conanfile.py --build --lockfile-out=conan.lock") client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual([], jsonbo) def test_base_graph(self): client = TestClient() client.save({"conanfile.py": GenConanfile("test4", "0.1")}) client.run("lock create conanfile.py --base --lockfile-out=conan.lock") client.run("lock build-order conan.lock --json=bo.json", assert_error=True) self.assertIn( "Lockfiles with --base do not contain profile information, " "cannot be used. Create a full lockfile", client.out) @parameterized.expand([(True, ), (False, )]) def build_not_locked_test(self, export): # https://github.com/conan-io/conan/issues/5727 client = TestClient() client.save({"conanfile.py": GenConanfile("test4", "0.1")}) if export: client.run("export .") client.run( "lock create --reference=test4/0.1@ --lockfile-out=conan.lock") else: client.run("create .") client.run( "lock create --reference=test4/0.1@ --build=test4 --lockfile-out=conan.lock" ) if client.cache.config.revisions_enabled: ref = "test4/0.1#f876ec9ea0f44cb7adb1588e431b391a" prev = "92cf292e73488c3527dab5f5ba81b947" build_order = [[[ "test4/0.1@#f876ec9ea0f44cb7adb1588e431b391a", "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", "host", "1" ]]] else: ref = "test4/0.1" prev = "0" build_order = [[[ "test4/0.1@", "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", "host", "1" ]]] locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] test4 = locked["1"] self.assertEqual(test4["ref"], ref) self.assertEqual(test4["package_id"], "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") self.assertEqual(test4.get("prev"), None) # PREV is not defined yet, only exported client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual(build_order, jsonbo) client.run( "install test4/0.1@ --lockfile=conan.lock --lockfile-out=conan.lock --build" ) self.assertIn( "test4/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Build", client.out) locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] test4 = locked["1"] self.assertEqual(test4["ref"], ref) self.assertEqual(test4["package_id"], "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") self.assertEqual(test4["prev"], prev) # New build order, nothing else to do client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual([], jsonbo) def build_locked_error_test(self): client = TestClient() client.save({"conanfile.py": GenConanfile("test4", "0.1")}) client.run("create .") client.run( "lock create --reference=test4/0.1@ --lockfile-out=conan.lock") locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] test4 = locked["1"] if client.cache.config.revisions_enabled: ref = "test4/0.1#f876ec9ea0f44cb7adb1588e431b391a" prev = "92cf292e73488c3527dab5f5ba81b947" else: ref = "test4/0.1" prev = "0" self.assertEqual(test4["ref"], ref) self.assertEqual(test4["package_id"], "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") self.assertEqual(test4["prev"], prev) client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual([], jsonbo) # if we try to build anyway, error client.run("install test4/0.1@ --lockfile=conan.lock --build", assert_error=True) self.assertIn( "Cannot build 'test4/0.1#f876ec9ea0f44cb7adb1588e431b391a' because it is " "already locked in the input lockfile", client.out) @parameterized.expand([(True, ), (False, )]) def transitive_build_not_locked_test(self, export): # https://github.com/conan-io/conan/issues/5727 client = TestClient() client.save({ "dep/conanfile.py": GenConanfile(), "pkg/conanfile.py": GenConanfile().with_require("dep/0.1"), "app/conanfile.py": GenConanfile().with_require("pkg/0.1") }) if export: client.run("export dep dep/0.1@") client.run("export pkg pkg/0.1@") client.run("export app app/0.1@") client.run( "lock create --reference=app/0.1@ --lockfile-out=conan.lock") else: client.run("create dep dep/0.1@") client.run("create pkg pkg/0.1@") client.run("create app app/0.1@") client.run( "lock create --reference=app/0.1@ --build --lockfile-out=conan.lock" ) locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] if client.cache.config.revisions_enabled: build_order = [[[ "dep/0.1@#f3367e0e7d170aa12abccb175fee5f97", "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", "host", "3" ]], [[ "pkg/0.1@#447b56f0334b7e2a28aa86e218c8b3bd", "0b3845ce7fd8c0b4e46566097797bd872cb5bcf6", "host", "2" ]], [[ "app/0.1@#5e0af887c3e9391c872773734ccd2ca0", "745ccd40fd696b66b0cb160fd5251a533563bbb4", "host", "1" ]]] prev_dep = "83c38d3b4e5f1b8450434436eec31b00" prev_pkg = "bcde0c25612a6d296cf2cab2c264054d" prev_app = "9f30558ce471f676e3e06b633aabcf99" else: build_order = [[[ "dep/0.1@", "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", "host", "3" ]], [[ "pkg/0.1@", "0b3845ce7fd8c0b4e46566097797bd872cb5bcf6", "host", "2" ]], [[ "app/0.1@", "745ccd40fd696b66b0cb160fd5251a533563bbb4", "host", "1" ]]] prev_dep = "0" prev_pkg = "0" prev_app = "0" for level in build_order: for item in level: ref, package_id, _, lockid = item ref = ref.replace("@", "") node = locked[lockid] self.assertEqual(node["ref"], ref) self.assertEqual(node["package_id"], package_id) self.assertEqual( node.get("prev"), None) # PREV is not defined yet, only exported client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual(build_order, jsonbo) if export: client.run("install app/0.1@ --lockfile=conan.lock", assert_error=True) self.assertIn( "Missing prebuilt package for 'app/0.1', 'dep/0.1', 'pkg/0.1'", client.out) # Build one by one client.run( "install {0} --lockfile=conan.lock --lockfile-out=conan.lock" " --build={0}".format(build_order[0][0][0])) self.assertIn( "dep/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Build", client.out) locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] node = locked["3"] self.assertEqual(node.get("prev"), prev_dep) node = locked["2"] self.assertEqual(node.get("prev"), None) # PREV is not defined yet, only exported node = locked["1"] self.assertEqual(node.get("prev"), None) # PREV is not defined yet, only exported client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual(build_order[1:], jsonbo) client.run("install pkg/0.1@ --lockfile=conan.lock --build", assert_error=True) self.assertIn( "Cannot build 'dep/0.1#f3367e0e7d170aa12abccb175fee5f97' because it is " "already locked in the input lockfile", client.out) client.run( "install {0} --lockfile=conan.lock --lockfile-out=conan.lock " "--build={0}".format(build_order[1][0][0])) self.assertIn( "dep/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache", client.out) self.assertIn( "pkg/0.1:0b3845ce7fd8c0b4e46566097797bd872cb5bcf6 - Build", client.out) locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] node = locked["3"] self.assertEqual(node.get("prev"), prev_dep) node = locked["2"] self.assertEqual(node.get("prev"), prev_pkg) node = locked["1"] self.assertEqual(node.get("prev"), None) # PREV is not defined yet, only exported client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual(build_order[2:], jsonbo) client.run("install app/0.1@ --lockfile=conan.lock --build", assert_error=True) self.assertIn( "Cannot build 'dep/0.1#f3367e0e7d170aa12abccb175fee5f97' because it is " "already locked in the input lockfile", client.out) client.run( "install {0} --lockfile=conan.lock --lockfile-out=conan.lock " "--build={0}".format(build_order[2][0][0])) self.assertIn( "dep/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Cache", client.out) self.assertIn( "pkg/0.1:0b3845ce7fd8c0b4e46566097797bd872cb5bcf6 - Cache", client.out) self.assertIn( "app/0.1:745ccd40fd696b66b0cb160fd5251a533563bbb4 - Build", client.out) locked = json.loads(client.load("conan.lock"))["graph_lock"]["nodes"] node = locked["3"] self.assertEqual(node.get("prev"), prev_dep) node = locked["2"] self.assertEqual(node.get("prev"), prev_pkg) node = locked["1"] self.assertEqual(node.get("prev"), prev_app) # New build order, nothing else to do client.run("lock build-order conan.lock --json=bo.json") jsonbo = json.loads(client.load("bo.json")) self.assertEqual([], jsonbo) @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions") def package_revision_mode_build_order_test(self): # https://github.com/conan-io/conan/issues/6232 client = TestClient() client.run( "config set general.default_package_id_mode=package_revision_mode") client.save({"conanfile.py": GenConanfile()}) client.run("export . libb/0.1@") client.run("export . libc/0.1@") client.save({"conanfile.py": GenConanfile().with_require("libc/0.1")}) client.run("export . liba/0.1@") client.save({ "conanfile.py": GenConanfile().with_require("liba/0.1").with_require("libb/0.1") }) client.run("export . app/0.1@") client.run( "lock create --reference=app/0.1@ --build=missing --lockfile-out=conan.lock" ) self.assertIn("app/0.1:Package_ID_unknown - Unknown", client.out) self.assertIn("liba/0.1:Package_ID_unknown - Unknown", client.out) self.assertIn( "libb/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Build", client.out) self.assertIn( "libc/0.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Build", client.out) lock = json.loads(client.load("conan.lock")) app = lock["graph_lock"]["nodes"]["1"] self.assertEqual(app["package_id"], "Package_ID_unknown") liba = lock["graph_lock"]["nodes"]["2"] self.assertEqual(liba["package_id"], "Package_ID_unknown") libc = lock["graph_lock"]["nodes"]["3"] self.assertEqual(libc["package_id"], "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") libd = lock["graph_lock"]["nodes"]["4"] self.assertEqual(libd["package_id"], "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9") client.run("lock build-order conan.lock --json=bo.json") bo = client.load("bo.json") build_order = json.loads(bo) expected = [[[ 'libc/0.1@#f3367e0e7d170aa12abccb175fee5f97', '5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9', 'host', '3' ], [ 'libb/0.1@#f3367e0e7d170aa12abccb175fee5f97', '5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9', 'host', '4' ]], [[ 'liba/0.1@#7086607aa6efbad8e2527748e3ee8237', 'Package_ID_unknown', 'host', '2' ]], [[ 'app/0.1@#7742ee9e2f19af4f9ed7619f231ca871', 'Package_ID_unknown', 'host', '1' ]]] self.assertEqual(build_order, expected)
def _make_files_writable(file_names): if not get_env("CONAN_READ_ONLY_CACHE", False): return for file_name in file_names: os.chmod(file_name, os.stat(file_name).st_mode | stat.S_IWRITE)
pkg_b_revision = "b6f49e5ba6dd3d64af09a2f288e71330" pkg_b_id = "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9" pkg_b_package_revision = "#33a5634bbd9ec26b369d3900d91ea9a0" modified_pkg_b_revision = "62a38c702f14cb9de952bb22b40d6ecc" modified_pkg_b_package_revision = "#b7850e289326d594fbc10088d55f5259" class GraphLockVersionRangeInfoTest(GraphLockVersionRangeTest): graph_lock_command = "info . --install-folder=." class GraphLockVersionRangeGraphLockTest(GraphLockVersionRangeTest): graph_lock_command = "graph lock ." @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions") class GraphLockRevisionTest(unittest.TestCase): pkg_b_revision = "9b64caa2465f7660e6f613b7e87f0cd7" pkg_b_id = "5bf1ba84b5ec8663764a406f08a7f9ae5d3d5fb5" pkg_b_package_revision = "#2ec4fb334e1b4f3fd0a6f66605066ac7" def setUp(self): test_server = TestServer(users={"user": "******"}) servers = {"default": test_server} client = TestClient(servers=servers, users={"default": [("user", "user")]}) # Important to activate revisions self.client = client client.save({ "conanfile.py":
def _cmake_cross_build_defines(self): os_ = self._ss("os") arch = self._ss("arch") os_ver_str = "os.api_level" if os_ == "Android" else "os.version" op_system_version = self._ss(os_ver_str) env_sn = get_env("CONAN_CMAKE_SYSTEM_NAME", "") env_sn = {"False": False, "True": True, "": None}.get(env_sn, env_sn) cmake_system_name = env_sn or self._forced_cmake_system_name os_build, _, _, _ = get_cross_building_settings(self._conanfile.settings) ret = OrderedDict() os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", op_system_version) toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "") if toolchain_file != "": logger.info("Setting Cross build toolchain file: %s" % toolchain_file) ret["CMAKE_TOOLCHAIN_FILE"] = toolchain_file return ret if cmake_system_name is False: return ret # System name and system version if cmake_system_name is not True: # String not empty ret["CMAKE_SYSTEM_NAME"] = cmake_system_name else: # detect if we are cross building and the system name and version if cross_building(self._conanfile.settings): # We are cross building if os_ != os_build: if os_: # the_os is the host (regular setting) ret["CMAKE_SYSTEM_NAME"] = ("Darwin" if os_ in ["iOS", "tvOS", "watchOS"] else os_) else: ret["CMAKE_SYSTEM_NAME"] = "Generic" if os_ver: ret["CMAKE_SYSTEM_VERSION"] = os_ver if str(os_) == "Macos": ret["CMAKE_OSX_DEPLOYMENT_TARGET"] = os_ver # system processor cmake_system_processor = os.getenv("CONAN_CMAKE_SYSTEM_PROCESSOR") if cmake_system_processor: ret["CMAKE_SYSTEM_PROCESSOR"] = cmake_system_processor if ret: # If enabled cross compile for env_var in ["CONAN_CMAKE_FIND_ROOT_PATH", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]: value = os.getenv(env_var) if value: ret[env_var] = value if self._conanfile and self._conanfile.deps_cpp_info.sysroot: sysroot_path = self._conanfile.deps_cpp_info.sysroot else: sysroot_path = os.getenv("CONAN_CMAKE_FIND_ROOT_PATH", None) if sysroot_path: # Needs to be set here, can't be managed in the cmake generator, CMake needs # to know about the sysroot before any other thing ret["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/") # Adjust Android stuff if os_ == "Android": arch_abi_settings = {"armv8": "arm64-v8a", "armv7": "armeabi-v7a", "armv7hf": "armeabi-v7a", "armv6": "armeabi-v6", "armv5": "armeabi" }.get(arch, arch) if arch_abi_settings: ret["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings logger.info("Setting Cross build flags: %s" % ", ".join(["%s=%s" % (k, v) for k, v in ret.items()])) return ret
def write_generators(self, conanfile, path, output): """ produces auxiliary files, required to build a project or a package. """ for generator_name in set(conanfile.generators): generator_class = self._new_generator(generator_name, output) if generator_class: if generator_name == "msbuild": msg = ( "\n*****************************************************************\n" "******************************************************************\n" "'msbuild' has been deprecated and moved.\n" "It will be removed in next Conan release.\n" "Use 'MSBuildDeps' method instead.\n" "********************************************************************\n" "********************************************************************\n") from conans.client.output import Color output.writeln(msg, front=Color.BRIGHT_RED) try: generator = generator_class(conanfile) output.highlight("Generator '{}' calling 'generate()'".format(generator_name)) generator.output_path = path mkdir(path) with chdir(path): generator.generate() continue except Exception as e: raise ConanException("Error in generator '{}': {}".format(generator_name, str(e))) try: generator_class = self._generators[generator_name] except KeyError: available = list(self._generators.keys()) + self._new_generators raise ConanException("Invalid generator '%s'. Available types: %s" % (generator_name, ", ".join(available))) try: generator = generator_class(conanfile) except TypeError: # To allow old-style generator packages to work (e.g. premake) output.warn("Generator %s failed with new __init__(), trying old one") 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: output.warn("Generator %s is multifile. Property 'filename' not used" % (generator_name,)) for k, v in content.items(): if generator.normalize: # To not break existing behavior, to be removed 2.0 v = normalize(v) output.info("Generator %s created %s" % (generator_name, k)) save(join(path, k), v, only_if_modified=True) else: content = normalize(content) output.info("Generator %s created %s" % (generator_name, generator.filename)) save(join(path, generator.filename), content, only_if_modified=True) except Exception as e: if get_env("CONAN_VERBOSE_TRACEBACK", False): output.error(traceback.format_exc()) output.error("Generator %s(file:%s) failed\n%s" % (generator_name, generator.filename, str(e))) raise ConanException(e)
def logging_file(self): return get_env('CONAN_LOGGING_FILE', None)
def get_conan_runner(): print_commands_to_output = get_env("CONAN_PRINT_RUN_COMMANDS", False) generate_run_log_file = get_env("CONAN_LOG_RUN_TO_FILE", False) log_run_to_output = get_env("CONAN_LOG_RUN_TO_OUTPUT", True) runner = ConanRunner(print_commands_to_output, generate_run_log_file, log_run_to_output) return runner
import logging from logging import StreamHandler import sys from conans.util.env_reader import get_env # #### LOGGER, MOVED FROM CONF BECAUSE OF MULTIPLE PROBLEM WITH CIRCULAR INCLUDES ##### CONAN_LOGGING_LEVEL = get_env('CONAN_LOGGING_LEVEL', logging.CRITICAL) CONAN_LOGGING_FILE = get_env('CONAN_LOGGING_FILE', None) # None is stdout class MultiLineFormatter(logging.Formatter): def format(self, record): str_ = logging.Formatter.format(self, record) separator = record.message if record.message else None if separator is None: return separator tmp = str_.split(separator) if len(tmp) == 2: header, _ = tmp else: header = tmp str_ = str_.replace('\n', '\n' + ' ' * len(header)) return str_ logger = logging.getLogger('conans') if CONAN_LOGGING_FILE is not None: hdlr = logging.FileHandler(CONAN_LOGGING_FILE) else: hdlr = StreamHandler(sys.stderr) formatter = MultiLineFormatter('%(levelname)-6s:%(filename)-15s[%(lineno)d]: '
GREEN = Fore.GREEN # @UndefinedVariable MAGENTA = Fore.MAGENTA # @UndefinedVariable BLUE = Fore.BLUE # @UndefinedVariable YELLOW = Fore.YELLOW # @UndefinedVariable BLACK = Fore.BLACK # @UndefinedVariable BRIGHT_RED = Style.BRIGHT + Fore.RED # @UndefinedVariable BRIGHT_BLUE = Style.BRIGHT + Fore.BLUE # @UndefinedVariable BRIGHT_YELLOW = Style.BRIGHT + Fore.YELLOW # @UndefinedVariable BRIGHT_GREEN = Style.BRIGHT + Fore.GREEN # @UndefinedVariable BRIGHT_CYAN = Style.BRIGHT + Fore.CYAN # @UndefinedVariable BRIGHT_WHITE = Style.BRIGHT + Fore.WHITE # @UndefinedVariable BRIGHT_MAGENTA = Style.BRIGHT + Fore.MAGENTA # @UndefinedVariable if get_env("CONAN_COLOR_DARK", 0): Color.WHITE = Fore.BLACK Color.CYAN = Fore.BLUE Color.YELLOW = Fore.MAGENTA Color.BRIGHT_WHITE = Fore.BLACK Color.BRIGHT_CYAN = Fore.BLUE Color.BRIGHT_YELLOW = Fore.MAGENTA Color.BRIGHT_GREEN = Fore.GREEN class ConanOutput(object): """ wraps an output stream, so it can be pretty colored, and auxiliary info, success, warn methods for convenience. """ def __init__(self, stream, color=False):
import logging from logging import StreamHandler import sys from conans.util.env_reader import get_env # #### LOGGER, MOVED FROM CONF BECAUSE OF MULTIPLE PROBLEM WITH CIRCULAR INCLUDES ##### CONAN_LOGGING_LEVEL = get_env("CONAN_LOGGING_LEVEL", logging.CRITICAL) CONAN_LOGGING_FILE = get_env("CONAN_LOGGING_FILE", None) # None is stdout class MultiLineFormatter(logging.Formatter): def format(self, record): str_ = logging.Formatter.format(self, record) separator = record.message if record.message else None if separator is None: return separator tmp = str_.split(separator) if len(tmp) == 2: header, _ = tmp else: header = tmp str_ = str_.replace("\n", "\n" + " " * len(header)) return str_ logger = logging.getLogger("conans") if CONAN_LOGGING_FILE is not None: hdlr = logging.FileHandler(CONAN_LOGGING_FILE) else: hdlr = StreamHandler(sys.stderr)
def get_command(self, project_file, props_file_path=None, targets=None, upgrade_project=True, build_type=None, arch=None, parallel=True, toolset=None, platforms=None, use_env=False): targets = targets or [] command = [] if upgrade_project and not get_env("CONAN_SKIP_VS_PROJECTS_UPGRADE", False): command.append("devenv %s /upgrade &&" % project_file) else: self._output.info("Skipped sln project upgrade") build_type = build_type or self._settings.get_safe("build_type") arch = arch or self._settings.get_safe("arch") if not build_type: raise ConanException("Cannot build_sln_command, build_type not defined") if not arch: raise ConanException("Cannot build_sln_command, arch not defined") command.append("msbuild %s /p:Configuration=%s" % (project_file, build_type)) msvc_arch = {'x86': 'x86', 'x86_64': 'x64', 'armv7': 'ARM', 'armv8': 'ARM64'} if platforms: msvc_arch.update(platforms) msvc_arch = msvc_arch.get(str(arch)) try: sln = tools.load(project_file) pattern = re.compile(r"GlobalSection\(SolutionConfigurationPlatforms\)" r"(.*?)EndGlobalSection", re.DOTALL) solution_global = pattern.search(sln).group(1) lines = solution_global.splitlines() lines = [s.split("=")[0].strip() for s in lines] except Exception: pass else: config = "%s|%s" % (build_type, msvc_arch) if config not in "".join(lines): self._output.warn("***** The configuration %s does not exist in this solution *****" % config) self._output.warn("Use 'platforms' argument to define your architectures") if use_env: command.append('/p:UseEnv=true') if msvc_arch: command.append('/p:Platform="%s"' % msvc_arch) if parallel: command.append('/m:%s' % cpu_count()) if targets: command.append("/target:%s" % ";".join(targets)) if toolset: command.append("/p:PlatformToolset=%s" % toolset) if props_file_path: command.append('/p:ForceImportBeforeCppTargets="%s"' % props_file_path) return " ".join(command)
def cache_no_locks(self): try: return get_env("CONAN_CACHE_NO_LOCKS", False) except ConanException: return False
def _cmake_cross_build_defines(self, the_os, os_ver): ret = OrderedDict() os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", os_ver) toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "") if toolchain_file != "": logger.info("Setting Cross build toolchain file: %s" % toolchain_file) ret["CMAKE_TOOLCHAIN_FILE"] = toolchain_file return ret if self._cmake_system_name is False: return ret if self._cmake_system_name is not True: # String not empty ret["CMAKE_SYSTEM_NAME"] = self._cmake_system_name ret["CMAKE_SYSTEM_VERSION"] = os_ver else: # detect if we are cross building and the system name and version 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 if os_ver: ret["CMAKE_SYSTEM_VERSION"] = os_ver else: ret["CMAKE_SYSTEM_NAME"] = "Generic" if ret: # If enabled cross compile for env_var in ["CONAN_CMAKE_SYSTEM_PROCESSOR", "CONAN_CMAKE_FIND_ROOT_PATH", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]: value = os.getenv(env_var, None) if value: ret[env_var] = value if self._conanfile and self._conanfile.deps_cpp_info.sysroot: sysroot_path = self._conanfile.deps_cpp_info.sysroot else: sysroot_path = os.getenv("CONAN_CMAKE_FIND_ROOT_PATH", None) if sysroot_path: # Needs to be set here, can't be managed in the cmake generator, CMake needs # to know about the sysroot before any other thing ret["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/") # Adjust Android stuff if self._os == "Android": arch_abi_settings = {"armv8": "arm64-v8a", "armv7": "armeabi-v7a", "armv7hf": "armeabi-v7a", "armv6": "armeabi-v6", "armv5": "armeabi" }.get(self._arch, self._arch) if arch_abi_settings: ret["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings logger.info("Setting Cross build flags: %s" % ", ".join(["%s=%s" % (k, v) for k, v in ret.items()])) return ret
def _get_env_cmake_system_name(): env_system_name = get_env("CONAN_CMAKE_SYSTEM_NAME", "") return {"False": False, "True": True, "": None}.get(env_system_name, env_system_name)
info_lock = client.load("conan.lock") self.assertEqual(lockfile, info_lock) def test_reproducible_lockfile_txt(self): client = TestClient() client.save({"conanfile.txt": ""}) client.run("install .") lockfile = client.load("conan.lock") client.run("install .") lockfile2 = client.load("conan.lock") self.assertEqual(lockfile, lockfile2) # check that the path to local conanfile.txt is relative, reproducible in other machine self.assertIn('"path": "conanfile.txt"', lockfile) @pytest.mark.skipif(not get_env("TESTING_REVISIONS_ENABLED", False), reason="Only revisions") class GraphLockRevisionTest(unittest.TestCase): rrev_b = "9b64caa2465f7660e6f613b7e87f0cd7" pkg_b_id = "5bf1ba84b5ec8663764a406f08a7f9ae5d3d5fb5" prev_b = "2ec4fb334e1b4f3fd0a6f66605066ac7" def setUp(self): client = TestClient(default_server_user=True) # Important to activate revisions self.client = client client.save({"conanfile.py": GenConanfile("PkgA", "0.1")}) client.run("create . PkgA/0.1@user/channel") client.run("upload PkgA/0.1@user/channel --all") consumer = textwrap.dedent(""" from conans import ConanFile
def _cmake_cross_build_defines(self, cmake_version): os_ = self._ss("os") arch = self._ss("arch") os_ver_str = "os.api_level" if os_ == "Android" else "os.version" op_system_version = self._ss(os_ver_str) env_sn = get_env("CONAN_CMAKE_SYSTEM_NAME", "") env_sn = {"False": False, "True": True, "": None}.get(env_sn, env_sn) cmake_system_name = env_sn or self._forced_cmake_system_name os_build, _, _, _ = get_cross_building_settings(self._conanfile) compiler = self._ss("compiler") libcxx = self._ss("compiler.libcxx") definitions = OrderedDict() os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", op_system_version) toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "") if toolchain_file != "": logger.info("Setting Cross build toolchain file: %s" % toolchain_file) definitions["CMAKE_TOOLCHAIN_FILE"] = toolchain_file return definitions if cmake_system_name is False: return definitions # System name and system version if cmake_system_name is not True: # String not empty definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name else: # detect if we are cross building and the system name and version skip_x64_x86 = os_ in ['Windows', 'Linux'] if cross_building(self._conanfile, skip_x64_x86=skip_x64_x86): # We are cross building apple_system_name = "Darwin" if Version(cmake_version) < Version("3.14") else None cmake_system_name_map = {"Macos": "Darwin", "iOS": apple_system_name or "iOS", "tvOS": apple_system_name or "tvOS", "watchOS": apple_system_name or "watchOS", "Neutrino": "QNX", "": "Generic", None: "Generic"} definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name_map.get(os_, os_) if os_ver: definitions["CMAKE_SYSTEM_VERSION"] = os_ver if is_apple_os(os_): definitions["CMAKE_OSX_DEPLOYMENT_TARGET"] = os_ver # system processor cmake_system_processor = os.getenv("CONAN_CMAKE_SYSTEM_PROCESSOR") if cmake_system_processor: definitions["CMAKE_SYSTEM_PROCESSOR"] = cmake_system_processor if definitions: # If enabled cross compile for env_var in ["CONAN_CMAKE_FIND_ROOT_PATH", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]: value = os.getenv(env_var) if value: definitions[env_var] = value if self._conanfile and self._conanfile.deps_cpp_info.sysroot: sysroot_path = self._conanfile.deps_cpp_info.sysroot else: sysroot_path = os.getenv("CONAN_CMAKE_FIND_ROOT_PATH", None) if sysroot_path: # Needs to be set here, can't be managed in the cmake generator, CMake needs # to know about the sysroot before any other thing definitions["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/") # Adjust Android stuff if str(os_) == "Android" and definitions["CMAKE_SYSTEM_NAME"] == "Android": arch_abi_settings = tools.to_android_abi(arch) if arch_abi_settings: definitions["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings definitions["ANDROID_ABI"] = arch_abi_settings conan_cmake_android_ndk = os.getenv("CONAN_CMAKE_ANDROID_NDK") if conan_cmake_android_ndk: definitions["ANDROID_NDK"] = conan_cmake_android_ndk definitions["ANDROID_PLATFORM"] = "android-%s" % op_system_version definitions["ANDROID_TOOLCHAIN"] = compiler # More details about supported stdc++ libraries here: # https://developer.android.com/ndk/guides/cpp-support.html if libcxx: definitions["ANDROID_STL"] = libcxx else: definitions["ANDROID_STL"] = 'none' logger.info("Setting Cross build flags: %s" % ", ".join(["%s=%s" % (k, v) for k, v in definitions.items()])) return definitions
def latest_package(self, pref): if not pref.ref.revision: raise Exception( "Pass a pref with .rev.revision (Testing framework)") prev = self.test_server.server_store.get_last_package_revision(pref) return pref.copy_with_revs(pref.ref.revision, prev) def package_revision_time(self, pref): if not pref: raise Exception("Pass a pref with revision (Testing framework)") tmp = self.test_server.server_store.get_package_revision_time(pref) return tmp if get_env("CONAN_TEST_WITH_ARTIFACTORY", False): TestServer = ArtifactoryServer def _copy_cache_folder(target_folder): # Some variables affect to cache population (take a different default folder) vars_ = [CONAN_V2_MODE_ENVVAR, 'CC', 'CXX', 'PATH'] cache_key = hash('|'.join( map(str, [os.environ.get(it, None) for it in vars_]))) master_folder = _copy_cache_folder.master.setdefault( cache_key, temp_folder(create_dir=False)) if not os.path.exists(master_folder): # Create and populate the cache folder with the defaults cache = ClientCache(master_folder, TestBufferConanOutput()) cache.initialize_config() cache.registry.initialize_remotes()
def get_command(self, project_file, props_file_path=None, targets=None, upgrade_project=True, build_type=None, arch=None, parallel=True, toolset=None, platforms=None, use_env=False, properties=None): targets = targets or [] properties = properties or {} command = [] if upgrade_project and not get_env("CONAN_SKIP_VS_PROJECTS_UPGRADE", False): command.append('devenv "%s" /upgrade &&' % project_file) else: self._output.info("Skipped sln project upgrade") build_type = build_type or self._settings.get_safe("build_type") arch = arch or self._settings.get_safe("arch") if not build_type: raise ConanException( "Cannot build_sln_command, build_type not defined") if not arch: raise ConanException("Cannot build_sln_command, arch not defined") command.append('msbuild "%s" /p:Configuration="%s"' % (project_file, build_type)) msvc_arch = { 'x86': 'x86', 'x86_64': 'x64', 'armv7': 'ARM', 'armv8': 'ARM64' } if platforms: msvc_arch.update(platforms) msvc_arch = msvc_arch.get(str(arch)) try: sln = tools.load(project_file) pattern = re.compile( r"GlobalSection\(SolutionConfigurationPlatforms\)" r"(.*?)EndGlobalSection", re.DOTALL) solution_global = pattern.search(sln).group(1) lines = solution_global.splitlines() lines = [s.split("=")[0].strip() for s in lines] except Exception: pass else: config = "%s|%s" % (build_type, msvc_arch) if config not in "".join(lines): self._output.warn( "***** The configuration %s does not exist in this solution *****" % config) self._output.warn( "Use 'platforms' argument to define your architectures") if use_env: command.append('/p:UseEnv=true') if msvc_arch: command.append('/p:Platform="%s"' % msvc_arch) if parallel: command.append('/m:%s' % cpu_count()) if targets: command.append("/target:%s" % ";".join(targets)) if toolset: command.append('/p:PlatformToolset="%s"' % toolset) if props_file_path: command.append('/p:ForceImportBeforeCppTargets="%s"' % props_file_path) for name, value in properties.items(): command.append('/p:%s="%s"' % (name, value)) return " ".join(command)
def _cmake_cross_build_defines(self): ret = OrderedDict() os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", self._op_system_version) toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "") if toolchain_file != "": logger.info("Setting Cross build toolchain file: %s" % toolchain_file) ret["CMAKE_TOOLCHAIN_FILE"] = toolchain_file return ret if self._cmake_system_name is False: return ret # System name and system version if self._cmake_system_name is not True: # String not empty ret["CMAKE_SYSTEM_NAME"] = self._cmake_system_name ret["CMAKE_SYSTEM_VERSION"] = os_ver else: # detect if we are cross building and the system name and version if cross_building(self._conanfile.settings): # We are cross building if self._os != self._os_build: if self._os: # the_os is the host (regular setting) ret["CMAKE_SYSTEM_NAME"] = "Darwin" if self._os in ["iOS", "tvOS", "watchOS"] else self._os if os_ver: ret["CMAKE_SYSTEM_VERSION"] = os_ver else: ret["CMAKE_SYSTEM_NAME"] = "Generic" # system processor cmake_system_processor = os.getenv("CONAN_CMAKE_SYSTEM_PROCESSOR", None) if cmake_system_processor: ret["CMAKE_SYSTEM_PROCESSOR"] = cmake_system_processor if ret: # If enabled cross compile for env_var in ["CONAN_CMAKE_FIND_ROOT_PATH", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]: value = os.getenv(env_var, None) if value: ret[env_var] = value if self._conanfile and self._conanfile.deps_cpp_info.sysroot: sysroot_path = self._conanfile.deps_cpp_info.sysroot else: sysroot_path = os.getenv("CONAN_CMAKE_FIND_ROOT_PATH", None) if sysroot_path: # Needs to be set here, can't be managed in the cmake generator, CMake needs # to know about the sysroot before any other thing ret["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/") # Adjust Android stuff if self._os == "Android": arch_abi_settings = {"armv8": "arm64-v8a", "armv7": "armeabi-v7a", "armv7hf": "armeabi-v7a", "armv6": "armeabi-v6", "armv5": "armeabi" }.get(self._arch, self._arch) if arch_abi_settings: ret["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings logger.info("Setting Cross build flags: %s" % ", ".join(["%s=%s" % (k, v) for k, v in ret.items()])) return ret
def path_shortener(path, short_paths): """ short_paths is 4-state: False: Never shorten the path True: Always shorten the path, create link if not existing None: Use shorten path only if already exists, not create """ use_always_short_paths = get_env("CONAN_USE_ALWAYS_SHORT_PATHS", False) short_paths = use_always_short_paths or short_paths if short_paths is False or os.getenv("CONAN_USER_HOME_SHORT") == "None": return path link = os.path.join(path, CONAN_LINK) if os.path.exists(link): return load(link) elif short_paths is None: return path if os.path.exists(path): rmdir(path) short_home = os.getenv("CONAN_USER_HOME_SHORT") if not short_home: if OSInfo().is_cygwin: try: cmd = ['cygpath', path, '--unix'] out, _ = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False).communicate() out = decode_text(out) if out.startswith('/cygdrive/'): # It was a Windows 'path' _, _, drive, _ = out.split('/', 3) short_home = os.path.join('/cygdrive', drive, '.conan') else: # It was a cygwin path, use a path inside the user home short_home = os.path.join(os.path.expanduser("~"), '.conan_short') except Exception: raise ConanException( "Conan failed to create the short_paths home for path '{}'" " in Cygwin. Please report this issue. You can use environment" " variable 'CONAN_USER_HOME_SHORT' to set the short_paths" " home.".format(path)) else: drive = os.path.splitdrive(path)[0] short_home = os.path.join(drive, os.sep, ".conan") mkdir(short_home) # Workaround for short_home living in NTFS file systems. Give full control permission # to current user to avoid # access problems in cygwin/msys2 windows subsystems when using short_home folder try: userdomain, username = os.getenv("USERDOMAIN"), os.environ["USERNAME"] domainname = "%s\%s" % (userdomain, username) if userdomain else username cmd = r'cacls %s /E /G "%s":F' % (short_home, domainname) subprocess.check_output( cmd, stderr=subprocess.STDOUT) # Ignoring any returned output, quiet except (subprocess.CalledProcessError, EnvironmentError): # cmd can fail if trying to set ACL in non NTFS drives, ignoring it. pass redirect = hashed_redirect(short_home, path) if not redirect: logger.warning("Failed to create a deterministic short path in %s", short_home) redirect = tempfile.mkdtemp(dir=short_home, prefix="") # Save the full path of the local cache directory where the redirect is from. # This file is for debugging purposes and not used by Conan. save(os.path.join(redirect, CONAN_REAL_PATH), path) # This "1" is the way to have a non-existing directory, so commands like # shutil.copytree() to it, works. It can be removed without compromising the # temp folder generator and conan-links consistency redirect = os.path.join(redirect, "1") save(link, redirect) return redirect
def get_command(self, project_file, props_file_path=None, targets=None, upgrade_project=True, build_type=None, arch=None, parallel=True, toolset=None, platforms=None, use_env=False, properties=None, output_binary_log=None, verbosity=None): targets = targets or [] properties = properties or {} command = [] if upgrade_project and not get_env("CONAN_SKIP_VS_PROJECTS_UPGRADE", False): command.append('devenv "%s" /upgrade &&' % project_file) else: self._output.info("Skipped sln project upgrade") build_type = build_type or self._settings.get_safe("build_type") arch = arch or self._settings.get_safe("arch") toolset = toolset or tools.msvs_toolset(self._settings) verbosity = os.getenv("CONAN_MSBUILD_VERBOSITY") or verbosity or "minimal" if not build_type: raise ConanException("Cannot build_sln_command, build_type not defined") if not arch: raise ConanException("Cannot build_sln_command, arch not defined") command.append('msbuild "%s" /p:Configuration="%s"' % (project_file, build_type)) msvc_arch = {'x86': 'x86', 'x86_64': 'x64', 'armv7': 'ARM', 'armv8': 'ARM64'} if platforms: msvc_arch.update(platforms) msvc_arch = msvc_arch.get(str(arch)) try: sln = tools.load(project_file) pattern = re.compile(r"GlobalSection\(SolutionConfigurationPlatforms\)" r"(.*?)EndGlobalSection", re.DOTALL) solution_global = pattern.search(sln).group(1) lines = solution_global.splitlines() lines = [s.split("=")[0].strip() for s in lines] except Exception: pass # TODO: !!! what are we catching here? tools.load? .group(1)? .splitlines? else: config = "%s|%s" % (build_type, msvc_arch) if config not in "".join(lines): self._output.warn("***** The configuration %s does not exist in this solution *****" % config) self._output.warn("Use 'platforms' argument to define your architectures") if output_binary_log: msbuild_version = MSBuild.get_version(self._settings) if msbuild_version >= "15.3": # http://msbuildlog.com/ command.append('/bl' if isinstance(output_binary_log, bool) else '/bl:"%s"' % output_binary_log) else: raise ConanException("MSBuild version detected (%s) does not support " "'output_binary_log' ('/bl')" % msbuild_version) if use_env: command.append('/p:UseEnv=true') if msvc_arch: command.append('/p:Platform="%s"' % msvc_arch) if parallel: command.append('/m:%s' % cpu_count(output=self._output)) if targets: command.append("/target:%s" % ";".join(targets)) if toolset: command.append('/p:PlatformToolset="%s"' % toolset) if verbosity: command.append('/verbosity:%s' % verbosity) if props_file_path: command.append('/p:ForceImportBeforeCppTargets="%s"' % props_file_path) for name, value in properties.items(): command.append('/p:%s="%s"' % (name, value)) return " ".join(command)
def __init__(self, base_folder, storage_folder=None, environment=os.environ): ConfigParser.__init__(self) self.conan_folder = os.path.join(base_folder, '.conan_server') self.config_filename = os.path.join(self.conan_folder, 'server.conf') self._loaded = False self.env_config = {"updown_secret": get_env("CONAN_UPDOWN_SECRET", None, environment), "store_adapter": get_env("CONAN_STORE_ADAPTER", None, environment), "authorize_timeout": get_env("CONAN_AUTHORIZE_TIMEOUT", None, environment), "disk_storage_path": get_env("CONAN_STORAGE_PATH", storage_folder, environment), "jwt_secret": get_env("CONAN_JWT_SECRET", None, environment), "jwt_expire_minutes": get_env("CONAN_JWT_EXPIRE_MINUTES", None, environment), "write_permissions": [], "read_permissions": [], "ssl_enabled": get_env("CONAN_SSL_ENABLED", None, environment), "port": get_env("CONAN_SERVER_PORT", None, environment), "public_port": get_env("CONAN_SERVER_PUBLIC_PORT", None, environment), "host_name": get_env("CONAN_HOST_NAME", None, environment), # "user:pass,user2:pass2" "users": get_env("CONAN_SERVER_USERS", None, environment)}
def _upload(self, conan_file, conan_ref, packages_ids, retry, retry_wait, integrity_check, policy, remote_name, recorder): """Uploads the recipes and binaries identified by conan_ref""" default_remote = self._registry.remotes.default cur_recipe_remote = self._registry.refs.get(conan_ref) if remote_name: # If remote_name is given, use it recipe_remote = self._registry.remotes.get(remote_name) else: recipe_remote = cur_recipe_remote or default_remote conanfile_path = self._client_cache.conanfile(conan_ref) # FIXME: I think it makes no sense to specify a remote to "pre_upload" # FIXME: because the recipe can have one and the package a different one self._hook_manager.execute("pre_upload", conanfile_path=conanfile_path, reference=conan_ref, remote=recipe_remote) if policy != UPLOAD_POLICY_FORCE: remote_manifest = self._check_recipe_date(conan_ref, recipe_remote) else: remote_manifest = None self._user_io.out.info("Uploading %s to remote '%s'" % (str(conan_ref), recipe_remote.name)) metadata = self._client_cache.load_metadata(conan_ref) ref = conan_ref.copy_with_rev(metadata.recipe.revision) self._upload_recipe(ref, retry, retry_wait, policy, recipe_remote, remote_manifest) recorder.add_recipe(ref, recipe_remote.name, recipe_remote.url) if packages_ids: # Filter packages that don't match the recipe revision revisions_enabled = get_env("CONAN_CLIENT_REVISIONS_ENABLED", False) if revisions_enabled and ref.revision: recipe_package_ids = [] for package_id in packages_ids: rec_rev = metadata.packages[package_id].recipe_revision if ref.revision != rec_rev: self._user_io.out.warn( "Skipping package '%s', it doesn't belong to " "the current recipe revision" % package_id) else: recipe_package_ids.append(package_id) packages_ids = recipe_package_ids # Can't use build_policy_always here because it's not loaded (only load_class) if conan_file.build_policy == "always": raise ConanException("Conanfile has build_policy='always', " "no packages can be uploaded") total = len(packages_ids) for index, package_id in enumerate(packages_ids): pref = PackageReference(ref, package_id) p_remote = recipe_remote self._upload_package(pref, metadata, index + 1, total, retry, retry_wait, integrity_check, policy, p_remote) recorder.add_package(pref, p_remote.name, p_remote.url) # FIXME: I think it makes no sense to specify a remote to "post_upload" # FIXME: because the recipe can have one and the package a different one self._hook_manager.execute("post_upload", conanfile_path=conanfile_path, reference=ref, remote=recipe_remote)
def vs_installation_path(version, preference=None): vs_installation_path = None if not preference: env_prefs = get_env("CONAN_VS_INSTALLATION_PREFERENCE", list()) if env_prefs: preference = env_prefs else: # default values preference = [ "Enterprise", "Professional", "Community", "BuildTools" ] # Try with vswhere() try: legacy_products = vswhere(legacy=True) all_products = vswhere(products=["*"]) products = legacy_products + all_products except ConanException: products = None vs_paths = [] if products: # remove repeated products seen_products = [] for product in products: if product not in seen_products: seen_products.append(product) # Append products with "productId" by order of preference for product_type in preference: for product in seen_products: product = dict(product) if (product["installationVersion"].startswith( ("%d." % int(version))) and "productId" in product): if product_type in product["productId"]: vs_paths.append(product["installationPath"]) # Append products without "productId" (Legacy installations) for product in seen_products: product = dict(product) if (product["installationVersion"].startswith( ("%d." % int(version))) and "productId" not in product): vs_paths.append(product["installationPath"]) # If vswhere does not find anything or not available, try with vs_comntools() if not vs_paths: vs_path = vs_comntools(version) if vs_path: sub_path_to_remove = os.path.join("", "Common7", "Tools", "") # Remove '\\Common7\\Tools\\' to get same output as vswhere if vs_path.endswith(sub_path_to_remove): vs_path = vs_path[:-(len(sub_path_to_remove) + 1)] vs_installation_path = vs_path else: vs_installation_path = vs_paths[0] return vs_installation_path
class UploadTest(unittest.TestCase): def test_upload_dirty(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": GenConanfile("Hello", "0.1")}) client.run("create . lasote/testing") ref = ConanFileReference.loads("Hello/0.1@lasote/testing") pref = PackageReference(ref, NO_SETTINGS_PACKAGE_ID) layout = client.cache.package_layout(pref.ref) pkg_folder = os.path.join(layout.base_folder(), PACKAGES_FOLDER, pref.id) set_dirty(pkg_folder) client.run("upload * --all --confirm", assert_error=True) self.assertIn( "ERROR: Hello/0.1@lasote/testing:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9: " "Upload package to 'default' failed: Package %s is corrupted, aborting upload" % str(pref), client.out) self.assertIn( "Remove it with 'conan remove Hello/0.1@lasote/testing -p=%s'" % NO_SETTINGS_PACKAGE_ID, client.out) client.run("remove Hello/0.1@lasote/testing -p=%s -f" % NO_SETTINGS_PACKAGE_ID) client.run("upload * --all --confirm") @pytest.mark.artifactory_ready def test_upload_force(self): ref = ConanFileReference.loads("Hello/0.1@conan/testing") client = TurboTestClient(servers={"default": TestServer()}) pref = client.create(ref, conanfile=GenConanfile().with_package_file( "myfile.sh", "foo")) client.run("upload * --all --confirm") self.assertIn("Uploading conan_package.tgz", client.out) client.run("upload * --all --confirm") self.assertNotIn("Uploading conan_package.tgz", client.out) package_folder = client.cache.package_layout(pref.ref).package(pref) package_file_path = os.path.join(package_folder, "myfile.sh") if platform.system() == "Linux": client.run("remove '*' -f") client.create(ref, conanfile=GenConanfile().with_package_file( "myfile.sh", "foo")) os.system('chmod +x "{}"'.format(package_file_path)) self.assertTrue(os.stat(package_file_path).st_mode & stat.S_IXUSR) client.run("upload * --all --confirm") self.assertNotIn("Uploading conan_package.tgz", client.out) self.assertIn("Package is up to date, upload skipped", client.out) self.assertIn("Compressing package...", client.out) client.run("upload * --all --confirm --force") self.assertIn("Uploading conanfile.py", client.out) self.assertIn("Uploading conan_package.tgz", client.out) if platform.system() == "Linux": client.run("remove '*' -f") client.run("install {}".format(ref)) # Owner with execute permissions self.assertTrue(os.stat(package_file_path).st_mode & stat.S_IXUSR) def test_upload_binary_not_existing(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": GenConanfile()}) client.run("export . Hello/0.1@lasote/testing") client.run("upload Hello/0.1@lasote/testing -p=123", assert_error=True) self.assertIn( "ERROR: Binary package Hello/0.1@lasote/testing:123 not found", client.out) def test_not_existing_error(self): """ Trying to upload with pattern not matched must raise an Error """ client = TestClient() client.run("upload some_nonsense", assert_error=True) self.assertIn( "ERROR: No packages found matching pattern 'some_nonsense'", client.out) def test_invalid_reference_error(self): """ Trying to upload an invalid reference must raise an Error """ client = TestClient() client.run("upload some_nonsense -p hash1", assert_error=True) self.assertIn( "ERROR: -p parameter only allowed with a valid recipe reference", client.out) def test_non_existing_recipe_error(self): """ Trying to upload a non-existing recipe must raise an Error """ client = TestClient(default_server_user=True) client.run("upload Pkg/0.1@user/channel", assert_error=True) self.assertIn("Recipe not found: 'Pkg/0.1@user/channel'", client.out) def test_non_existing_package_error(self): """ Trying to upload a non-existing package must raise an Error """ client = TestClient(default_server_user=True) client.run("upload Pkg/0.1@user/channel -p hash1", assert_error=True) self.assertIn("ERROR: Recipe not found: 'Pkg/0.1@user/channel'", client.out) def test_deprecated_p_arg(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run("upload Hello0/1.2.1@user/testing -p {} -c".format( NO_SETTINGS_PACKAGE_ID)) self.assertIn( "WARN: Usage of `--package` argument is deprecated. " "Use a full reference instead: `conan upload [...] " "Hello0/1.2.1@user/testing:{}`".format(NO_SETTINGS_PACKAGE_ID), client.out) def test_upload_with_pref(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run("upload Hello0/1.2.1@user/testing:{} -c".format( NO_SETTINGS_PACKAGE_ID)) self.assertNotIn( "WARN: Usage of `--package` argument is deprecated. " "Use a full reference instead: `conan upload [...] " "Hello0/1.2.1@user/testing:{}`".format(NO_SETTINGS_PACKAGE_ID), client.out) self.assertIn( "Uploading package 1/1: {} to 'default'".format( NO_SETTINGS_PACKAGE_ID), client.out) def test_upload_with_pref_and_p(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run("upload Hello0/1.2.1@user/testing:{} -c -p {}".format( NO_SETTINGS_PACKAGE_ID, NO_SETTINGS_PACKAGE_ID), assert_error=True) self.assertIn( "Use a full package reference (preferred) or the " "`--package` command argument, but not both.", client.out) def test_pattern_upload(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run("upload Hello0/*@user/testing --confirm --all") self.assertIn("Uploading conanmanifest.txt", client.out) self.assertIn("Uploading conan_package.tgz", client.out) self.assertIn("Uploading conanfile.py", client.out) def test_query_upload(self): client = TestClient(default_server_user=True) conanfile_upload_query = textwrap.dedent(""" from conans import ConanFile class MyPkg(ConanFile): name = "Hello1" version = "1.2.1" exports_sources = "*" settings = "os", "arch" def package(self): self.copy("*") """) client.save({"conanfile.py": conanfile_upload_query}) for _os, arch in itertools.product(["Macos", "Linux", "Windows"], ["armv8", "x86_64"]): client.run("create . user/testing -s os=%s -s arch=%s" % (_os, arch)) # Check that the right number of packages are picked up by the queries client.run( "upload Hello1/*@user/testing --confirm -q 'os=Windows or os=Macos'" ) for i in range(1, 5): self.assertIn("Uploading package %d/4" % i, client.out) self.assertNotIn("Package is up to date, upload skipped", client.out) client.run( "upload Hello1/*@user/testing --confirm -q 'os=Linux and arch=x86_64'" ) self.assertIn("Uploading package 1/1", client.out) client.run("upload Hello1/*@user/testing --confirm -q 'arch=armv8'") for i in range(1, 4): self.assertIn("Uploading package %d/3" % i, client.out) self.assertIn("Package is up to date, upload skipped", client.out) # Check that a query not matching any packages doesn't upload any packages client.run("upload Hello1/*@user/testing --confirm -q 'arch=sparc'") self.assertNotIn("Uploading package", client.out) # Check that an invalid query fails client.run( "upload Hello1/*@user/testing --confirm -q 'blah blah blah'", assert_error=True) self.assertIn("Invalid package query", client.out) def test_broken_sources_tgz(self): # https://github.com/conan-io/conan/issues/2854 client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "source.h": "my source"}) client.run("create . user/testing") ref = ConanFileReference.loads("Hello0/1.2.1@user/testing") def gzopen_patched(name, mode="r", fileobj=None, **kwargs): raise ConanException("Error gzopen %s" % name) with patch('conans.client.cmd.uploader.gzopen_without_timestamps', new=gzopen_patched): client.run("upload * --confirm", assert_error=True) self.assertIn( "ERROR: Hello0/1.2.1@user/testing: Upload recipe to 'default' failed: " "Error gzopen conan_sources.tgz", client.out) export_download_folder = client.cache.package_layout( ref).download_export() tgz = os.path.join(export_download_folder, EXPORT_SOURCES_TGZ_NAME) self.assertTrue(os.path.exists(tgz)) self.assertTrue(is_dirty(tgz)) client.run("upload * --confirm") self.assertIn( "WARN: Hello0/1.2.1@user/testing: Removing conan_sources.tgz, " "marked as dirty", client.out) self.assertTrue(os.path.exists(tgz)) self.assertFalse(is_dirty(tgz)) def test_broken_package_tgz(self): # https://github.com/conan-io/conan/issues/2854 client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "source.h": "my source"}) client.run("create . user/testing") pref = PackageReference.loads("Hello0/1.2.1@user/testing:" + NO_SETTINGS_PACKAGE_ID) def gzopen_patched(name, mode="r", fileobj=None, **kwargs): if name == PACKAGE_TGZ_NAME: raise ConanException("Error gzopen %s" % name) return gzopen_without_timestamps(name, mode, fileobj, **kwargs) with patch('conans.client.cmd.uploader.gzopen_without_timestamps', new=gzopen_patched): client.run("upload * --confirm --all", assert_error=True) self.assertIn( "ERROR: Hello0/1.2.1@user/testing:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9" ": Upload package to 'default' failed: Error gzopen conan_package.tgz", client.out) download_folder = client.cache.package_layout( pref.ref).download_package(pref) tgz = os.path.join(download_folder, PACKAGE_TGZ_NAME) self.assertTrue(os.path.exists(tgz)) self.assertTrue(is_dirty(tgz)) client.run("upload * --confirm --all") self.assertIn( "WARN: Hello0/1.2.1@user/testing:%s: " "Removing conan_package.tgz, marked as dirty" % NO_SETTINGS_PACKAGE_ID, client.out) self.assertTrue(os.path.exists(tgz)) self.assertFalse(is_dirty(tgz)) def test_corrupt_upload(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "include/hello.h": ""}) client.run("create . frodo/stable") ref = ConanFileReference.loads("Hello0/1.2.1@frodo/stable") packages_folder = client.cache.package_layout(ref).packages() pkg_id = os.listdir(packages_folder)[0] package_folder = os.path.join(packages_folder, pkg_id) save(os.path.join(package_folder, "added.txt"), "") os.remove(os.path.join(package_folder, "include/hello.h")) client.run("upload Hello0/1.2.1@frodo/stable --all --check", assert_error=True) self.assertIn("WARN: Mismatched checksum 'added.txt'", client.out) self.assertIn("WARN: Mismatched checksum 'include/hello.h'", client.out) self.assertIn( "ERROR: Hello0/1.2.1@frodo/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9: " "Upload package to 'default' failed: Cannot upload corrupted package", client.out) def test_upload_modified_recipe(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "hello.cpp": "int i=0"}) client.run("export . frodo/stable") client.run("upload Hello0/1.2.1@frodo/stable") self.assertIn("Uploading conanmanifest.txt", client.out) assert "Uploading Hello0/1.2.1@frodo/stable to remote" in client.out client2 = TestClient(servers=client.servers, users=client.users) client2.save({ "conanfile.py": conanfile + "\r\n#end", "hello.cpp": "int i=1" }) client2.run("export . frodo/stable") ref = ConanFileReference.loads("Hello0/1.2.1@frodo/stable") manifest = client2.cache.package_layout(ref).recipe_manifest() manifest.time += 10 manifest.save(client2.cache.package_layout(ref).export()) client2.run("upload Hello0/1.2.1@frodo/stable") self.assertIn("Uploading conanmanifest.txt", client2.out) assert "Uploading Hello0/1.2.1@frodo/stable to remote" in client2.out # first client tries to upload again if not client.cache.config.revisions_enabled: client.run("upload Hello0/1.2.1@frodo/stable", assert_error=True) self.assertIn("Remote recipe is newer than local recipe", client.out) self.assertIn("Local 'conanfile.py' using '\\n' line-ends", client.out) self.assertIn("Remote 'conanfile.py' using '\\r\\n' line-ends", client.out) else: # The client tries to upload exactly the same revision already uploaded, so no changes client.run("upload Hello0/1.2.1@frodo/stable") self.assertIn("Recipe is up to date, upload skipped", client.out) def test_upload_unmodified_recipe(self): client = TestClient(default_server_user=True) files = {"conanfile.py": GenConanfile("Hello0", "1.2.1")} client.save(files) client.run("export . frodo/stable") client.run("upload Hello0/1.2.1@frodo/stable") self.assertIn("Uploading conanmanifest.txt", client.out) assert "Uploading Hello0/1.2.1@frodo/stable to remote" in client.out client2 = TestClient(servers=client.servers, users=client.users) client2.save(files) client2.run("export . frodo/stable") ref = ConanFileReference.loads("Hello0/1.2.1@frodo/stable") manifest = client2.cache.package_layout(ref).recipe_manifest() manifest.time += 10 manifest.save(client2.cache.package_layout(ref).export()) client2.run("upload Hello0/1.2.1@frodo/stable") self.assertNotIn("Uploading conanmanifest.txt", client2.out) assert "Uploading Hello0/1.2.1@frodo/stable to remote" in client2.out self.assertIn("Recipe is up to date, upload skipped", client2.out) # first client tries to upload again client.run("upload Hello0/1.2.1@frodo/stable") self.assertNotIn("Uploading conanmanifest.txt", client.out) assert "Uploading Hello0/1.2.1@frodo/stable to remote" in client.out self.assertIn("Recipe is up to date, upload skipped", client.out) def test_upload_unmodified_package(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "hello.cpp": ""}) client.run("create . frodo/stable") client.run("upload Hello0/1.2.1@frodo/stable --all") client2 = TestClient(servers=client.servers, users=client.users) client2.save({"conanfile.py": conanfile, "hello.cpp": ""}) client2.run("create . frodo/stable") 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.assertNotIn("Uploading conan_package.tgz", client2.out) self.assertIn("Package is up to date, upload skipped", client2.out) # first client tries to upload again 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.assertNotIn("Uploading conan_package.tgz", client2.out) self.assertIn("Package is up to date, upload skipped", client2.out) def test_no_overwrite_argument_collision(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile, "hello.cpp": ""}) client.run("create . frodo/stable") # Not valid values as arguments client.run("upload Hello0/1.2.1@frodo/stable --no-overwrite kk", assert_error=True) self.assertIn("ERROR", client.out) # --force not valid with --no-overwrite client.run("upload Hello0/1.2.1@frodo/stable --no-overwrite --force", assert_error=True) self.assertIn( "ERROR: '--no-overwrite' argument cannot be used together with '--force'", client.out) def test_upload_no_overwrite_all(self): conanfile_new = """from conans import ConanFile, tools class MyPkg(ConanFile): name = "Hello0" version = "1.2.1" exports_sources = "*" options = {"shared": [True, False]} default_options = "shared=False" def build(self): if tools.get_env("MY_VAR", False): open("file.h", 'w').close() def package(self): self.copy("*.h") """ client = TestClient(default_server_user=True) client.save({ "conanfile.py": conanfile_new, "hello.h": "", "hello.cpp": "" }) client.run("create . frodo/stable") # First time upload client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite") self.assertNotIn("Forbidden overwrite", client.out) self.assertIn("Uploading Hello0/1.2.1@frodo/stable", client.out) # CASE: Upload again client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite") self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn("Package is up to date, upload skipped", client.out) self.assertNotIn("Forbidden overwrite", client.out) # CASE: Without changes client.run("create . frodo/stable") # upload recipe and packages client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite") self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn("Package is up to date, upload skipped", client.out) self.assertNotIn("Forbidden overwrite", client.out) # CASE: When recipe and package changes new_recipe = conanfile_new.replace( "self.copy(\"*.h\")", "self.copy(\"*.h\")\n self.copy(\"*.cpp\")") client.save({"conanfile.py": new_recipe}) client.run("create . frodo/stable") # upload recipe and packages # *1 client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite", assert_error=not client.cache.config.revisions_enabled) if not client.cache.config.revisions_enabled: # The --no-overwrite makes no sense with revisions self.assertIn("Forbidden overwrite", client.out) self.assertNotIn("Uploading conan_package.tgz", client.out) # CASE: When package changes client.run("upload Hello0/1.2.1@frodo/stable --all") with environment_append({"MY_VAR": "True"}): client.run("create . frodo/stable") # upload recipe and packages client.run("upload Hello0/1.2.1@frodo/stable --all --no-overwrite", assert_error=not client.cache.config.revisions_enabled) if not client.cache.config.revisions_enabled: self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn( "ERROR: Hello0/1.2.1@frodo/stable:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9" ": Upload package to 'default' failed: " "Local package is different from the remote package", client.out) self.assertIn("Forbidden overwrite", client.out) self.assertNotIn("Uploading conan_package.tgz", client.out) else: self.assertIn("Uploading conan_package.tgz", client.out) def test_upload_no_overwrite_recipe(self): conanfile_new = """from conans import ConanFile, tools class MyPkg(ConanFile): name = "Hello0" version = "1.2.1" exports_sources = "*" options = {"shared": [True, False]} default_options = "shared=False" def build(self): if tools.get_env("MY_VAR", False): open("file.h", 'w').close() def package(self): self.copy("*.h") """ client = TestClient(default_server_user=True) client.save({ "conanfile.py": conanfile_new, "hello.h": "", "hello.cpp": "" }) client.run("create . frodo/stable") # First time upload client.run( "upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe") self.assertNotIn("Forbidden overwrite", client.out) self.assertIn("Uploading Hello0/1.2.1@frodo/stable", client.out) # Upload again client.run( "upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe") self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn("Package is up to date, upload skipped", client.out) self.assertNotIn("Forbidden overwrite", client.out) # Create without changes # *1 client.run("create . frodo/stable") client.run( "upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe") self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn("Package is up to date, upload skipped", client.out) self.assertNotIn("Forbidden overwrite", client.out) # Create with recipe and package changes new_recipe = conanfile_new.replace( "self.copy(\"*.h\")", "self.copy(\"*.h\")\n self.copy(\"*.cpp\")") client.save({"conanfile.py": new_recipe}) client.run("create . frodo/stable") # upload recipe and packages client.run( "upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe", assert_error=not client.cache.config.revisions_enabled) if not client.cache.config.revisions_enabled: self.assertIn("Forbidden overwrite", client.out) self.assertNotIn("Uploading package", client.out) # Create with package changes client.run("upload Hello0/1.2.1@frodo/stable --all") with environment_append({"MY_VAR": "True"}): client.run("create . frodo/stable") # upload recipe and packages client.run( "upload Hello0/1.2.1@frodo/stable --all --no-overwrite recipe") self.assertIn("Recipe is up to date, upload skipped", client.out) self.assertIn("Uploading conan_package.tgz", client.out) self.assertNotIn("Forbidden overwrite", client.out) else: self.assertIn("Uploading conan_package.tgz", client.out) def test_skip_upload(self): """ Check that the option --dry does not upload anything """ client = TestClient(default_server_user=True) files = { "conanfile.py": GenConanfile("Hello0", "1.2.1").with_exports("*"), "file.txt": "" } client.save(files) client.run("create . frodo/stable") client.run( "upload Hello0/1.2.1@frodo/stable -r default --all --skip-upload") # dry run should not upload self.assertNotIn("Uploading conan_package.tgz", client.out) # but dry run should compress self.assertIn("Compressing recipe...", client.out) self.assertIn("Compressing package...", client.out) client.run("search -r default") # after dry run nothing should be on the server ... self.assertNotIn("Hello0/1.2.1@frodo/stable", client.out) # now upload, the stuff should NOT be recompressed client.run("upload Hello0/1.2.1@frodo/stable -r default --all") # check for upload message self.assertIn("Uploading conan_package.tgz", client.out) # check if compressed files are re-used self.assertNotIn("Compressing recipe...", client.out) self.assertNotIn("Compressing package...", client.out) # now it should be on the server client.run("search -r default") self.assertIn("Hello0/1.2.1@frodo/stable", client.out) def test_upload_without_sources(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": GenConanfile()}) client.run("create . Pkg/0.1@user/testing") client.run("upload * --all --confirm") client2 = TestClient(servers=client.servers, users=client.users) client2.run("install Pkg/0.1@user/testing") client2.run("remote remove default") server2 = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")], users={"lasote": "mypass"}) client2.users = {"server2": [("lasote", "mypass")]} client2.servers = {"server2": server2} client2.update_servers() client2.run("upload * --all --confirm -r=server2") self.assertIn("Uploading conanfile.py", client2.out) self.assertIn("Uploading conan_package.tgz", client2.out) def test_upload_login_prompt_disabled_no_user(self): """ Without user info, uploads should fail when login prompt has been disabled. """ files = {"conanfile.py": GenConanfile("Hello0", "1.2.1")} client = TestClient(default_server_user=True) client.save(files) client.run("config set general.non_interactive=True") client.run("create . user/testing") client.run("user -c") client.run("upload Hello0/1.2.1@user/testing", assert_error=True) self.assertIn( "ERROR: Hello0/1.2.1@user/testing: Upload recipe to 'default' failed: " "Conan interactive mode disabled", client.out) self.assertNotIn("Uploading conanmanifest.txt", client.out) self.assertNotIn("Uploading conanfile.py", client.out) self.assertNotIn("Uploading conan_export.tgz", client.out) def test_upload_login_prompt_disabled_user_not_authenticated(self): # When a user is not authenticated, uploads should fail when login prompt has been disabled. files = {"conanfile.py": GenConanfile("Hello0", "1.2.1")} client = TestClient(default_server_user=True) client.save(files) client.run("config set general.non_interactive=True") client.run("create . user/testing") client.run("user -c") client.run("user lasote") client.run("upload Hello0/1.2.1@user/testing", assert_error=True) self.assertIn( "ERROR: Hello0/1.2.1@user/testing: Upload recipe to 'default' failed: " "Conan interactive mode disabled", client.out) self.assertNotIn("Uploading conanmanifest.txt", client.out) self.assertNotIn("Uploading conanfile.py", client.out) self.assertNotIn("Uploading conan_export.tgz", client.out) self.assertNotIn("Please enter a password for", client.out) def test_upload_login_prompt_disabled_user_authenticated(self): # When user is authenticated, uploads should work even when login prompt has been disabled. client = TestClient(default_server_user=True) client.save({"conanfile.py": GenConanfile("Hello0", "1.2.1")}) client.run("config set general.non_interactive=True") client.run("create . user/testing") client.run("user -c") client.run("user user -p password") client.run("upload Hello0/1.2.1@user/testing") self.assertIn("Uploading conanmanifest.txt", client.out) self.assertIn("Uploading conanfile.py", client.out) @pytest.mark.skipif(not get_env("TESTING_REVISIONS_ENABLED", False), reason="Only revisions") def test_upload_key_error(self): files = {"conanfile.py": GenConanfile("Hello0", "1.2.1")} server1 = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")], users={"lasote": "mypass"}) server2 = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")], users={"lasote": "mypass"}) servers = OrderedDict() servers["server1"] = server1 servers["server2"] = server2 client = TestClient(servers=servers) client.save(files) client.run("create . user/testing") client.run("user lasote -p mypass") client.run("user lasote -p mypass -r server2") client.run("upload Hello0/1.2.1@user/testing --all -r server1") client.run("remove * --force") client.run("install Hello0/1.2.1@user/testing -r server1") client.run("remote remove server1") client.run("upload Hello0/1.2.1@user/testing --all -r server2") self.assertNotIn("ERROR: 'server1'", client.out) def test_upload_export_pkg(self): """ Package metadata created when doing an export-pkg and then uploading the package works """ server1 = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")], users={"lasote": "mypass"}) servers = OrderedDict() servers["server1"] = server1 client = TestClient(servers=servers) client.save({"release/kk.lib": ""}) client.run("user lasote -r server1 -p mypass") client.run("new hello/1.0 --header") client.run("export-pkg . user/testing -pf release") client.run("upload hello/1.0@user/testing --all -r server1") self.assertNotIn( "Binary package hello/1.0@user/testing:5%s not found" % NO_SETTINGS_PACKAGE_ID, client.out) ref = ConanFileReference("hello", "1.0", "user", "testing") metadata = client.cache.package_layout(ref).load_metadata() self.assertIn(NO_SETTINGS_PACKAGE_ID, metadata.packages) self.assertTrue(metadata.packages[NO_SETTINGS_PACKAGE_ID].revision) def test_no_remote_recipe_manifest(self): # https://github.com/conan-io/conan/issues/4953 server = TestServer() servers = OrderedDict([("default", server)]) client = TurboTestClient(servers=servers) client2 = TurboTestClient(servers=servers) ref = ConanFileReference.loads("lib/1.0@conan/testing") client.create(ref) complete_ref = client.upload_all(ref) # Simulate a missing manifest, maybe because it hasn't been uploaded yet export_folder = server.server_store.export(complete_ref) os.unlink(os.path.join(export_folder, "conanmanifest.txt")) # Upload same with client2 client2.create(ref) client2.upload_all(ref) self.assertIn( "WARN: The remote recipe doesn't have the 'conanmanifest.txt' file " "and will be uploaded: 'lib/1.0@conan/testing'", client2.out) def test_concurrent_upload(self): # https://github.com/conan-io/conan/issues/4953 server = TestServer() servers = OrderedDict([("default", server)]) client = TurboTestClient(servers=servers) client2 = TurboTestClient(servers=servers) ref = ConanFileReference.loads("lib/1.0@conan/testing") client.create(ref) client.upload_all(ref) # The _check_recipe_date returns None, but later it will get the manifest ok with patch.object(CmdUpload, "_check_recipe_date") as check_date: check_date.return_value = None # Upload same with client2 client2.create(ref) client2.run("upload lib/1.0@conan/testing") self.assertIn("Recipe is up to date, upload skipped", client2.out) self.assertNotIn("WARN", client2.out) def test_upload_with_pref_and_query(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run( "upload Hello0/1.2.1@user/testing:{} " "-q 'os=Windows or os=Macos'".format(NO_SETTINGS_PACKAGE_ID), assert_error=True) self.assertIn( "'--query' argument cannot be used together with full reference", client.out) def test_upload_with_package_id_and_query(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": conanfile}) client.run("create . user/testing") client.run( "upload Hello0/1.2.1@user/testing -p {} " "-q 'os=Windows or os=Macos'".format(NO_SETTINGS_PACKAGE_ID), assert_error=True) self.assertIn( "'--query' argument cannot be used together with '--package'", client.out) def test_upload_without_user_channel(self): server = TestServer(users={"user": "******"}, write_permissions=[("*/*@*/*", "*")]) servers = {"default": server} client = TestClient(servers=servers, users={"default": [("user", "password")]}) client.save({"conanfile.py": GenConanfile()}) client.run('create . lib/1.0@') self.assertIn( "lib/1.0: Package '{}' created".format(NO_SETTINGS_PACKAGE_ID), client.out) client.run('upload lib/1.0 -c --all') assert "Uploading lib/1.0 to remote" in client.out # Verify that in the remote it is stored as "_" pref = PackageReference.loads( "lib/1.0@#0:{}#0".format(NO_SETTINGS_PACKAGE_ID)) path = server.server_store.export(pref.ref) self.assertIn("/lib/1.0/_/_/0/export", path.replace("\\", "/")) path = server.server_store.package(pref) self.assertIn("/lib/1.0/_/_/0/package", path.replace("\\", "/")) # Should be possible with explicit package client.run('upload lib/1.0:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9') self.assertIn( "Uploading package 1/1: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 to 'default'", client.out) def test_checksums_metadata(self): client = TestClient(default_server_user=True) client.save({"conanfile.py": GenConanfile()}) client.run('create . lib/1.0@user/channel') client.run('upload lib/1.0 -c --all -r default') ref = ConanFileReference("lib", "1.0", "user", "channel") metadata = client.cache.package_layout(ref).load_metadata() package_md5 = metadata.packages[NO_SETTINGS_PACKAGE_ID].checksums[ "conan_package.tgz"]["md5"] package_sha1 = metadata.packages[NO_SETTINGS_PACKAGE_ID].checksums[ "conan_package.tgz"]["sha1"] recipe_md5 = metadata.recipe.checksums["conanfile.py"]["md5"] recipe_sha1 = metadata.recipe.checksums["conanfile.py"]["sha1"] self.assertEqual(package_md5, "25f53ac9685e07815b990e7c6f21bfd0") self.assertEqual(package_sha1, "be152c82859285658a06816aaaec529a159c33d3") self.assertEqual(recipe_md5, "8eda41e0997f3eacc436fb6c621d7396") self.assertEqual(recipe_sha1, "b97d6b26be5bd02252a44c265755f873cf5ec70b") client.run('remove * -f') client.run('install lib/1.0@user/channel -r default') metadata = client.cache.package_layout(ref).load_metadata() self.assertEqual( metadata.packages[NO_SETTINGS_PACKAGE_ID]. checksums["conan_package.tgz"]["md5"], package_md5) self.assertEqual( metadata.packages[NO_SETTINGS_PACKAGE_ID]. checksums["conan_package.tgz"]["sha1"], package_sha1) self.assertEqual(metadata.recipe.checksums["conanfile.py"]["md5"], recipe_md5) self.assertEqual(metadata.recipe.checksums["conanfile.py"]["sha1"], recipe_sha1) def test_upload_without_cleaned_user(self): """ When a user is not authenticated, uploads failed first time https://github.com/conan-io/conan/issues/5878 """ class EmptyCapabilitiesResponse(object): def __init__(self): self.ok = False self.headers = { "X-Conan-Server-Capabilities": "", "Content-Type": "application/json" } self.status_code = 401 self.content = b'' class ErrorApiResponse(object): def __init__(self): self.ok = False self.status_code = 400 self.content = "Unsupported Conan v1 repository request for 'conan'" class ServerCapabilitiesRequester(TestRequester): def __init__(self, *args, **kwargs): self._first_ping = True super(ServerCapabilitiesRequester, self).__init__(*args, **kwargs) def get(self, url, **kwargs): app, url = self._prepare_call(url, kwargs) if app: if url.endswith("ping") and self._first_ping: self._first_ping = False return EmptyCapabilitiesResponse() elif "Hello0" in url and "1.2.1" in url and "v1" in url: return ErrorApiResponse() else: response = app.get(url, **kwargs) return TestingResponse(response) else: return requests.get(url, **kwargs) server = TestServer(users={"user": "******"}, write_permissions=[("*/*@*/*", "*")], server_capabilities=[REVISIONS]) servers = {"default": server} client = TestClient(requester_class=ServerCapabilitiesRequester, servers=servers, revisions_enabled=True) files = {"conanfile.py": GenConanfile("Hello0", "1.2.1")} client.save(files) client.run("create . user/testing") client.run("user -c") client.run("upload Hello0/1.2.1@user/testing --all -r default") assert "Uploading Hello0/1.2.1@user/testing to remote" in client.out @pytest.mark.skipif(get_env("TESTING_REVISIONS_ENABLED", False), reason="No sense with revs") def test_upload_with_rev_revs_disabled(self): client = TestClient(default_server_user=True, revisions_enabled=False) client.run("upload pkg/1.0@user/channel#fakerevision --confirm", assert_error=True) self.assertIn( "ERROR: Revisions not enabled in the client, specify a reference without revision", client.out) @pytest.mark.skipif(not get_env("TESTING_REVISIONS_ENABLED", False), reason="Only revisions") def test_upload_with_recipe_revision(self): ref = ConanFileReference.loads("pkg/1.0@user/channel") client = TurboTestClient(default_server_user=True, revisions_enabled=True) pref = client.create(ref, conanfile=GenConanfile()) client.run("upload pkg/1.0@user/channel#fakerevision --confirm", assert_error=True) self.assertIn( "ERROR: Recipe revision fakerevision does not match the one stored in " "the cache {}".format(pref.ref.revision), client.out) client.run("upload pkg/1.0@user/channel#{} --confirm".format( pref.ref.revision)) search_result = client.search( "pkg/1.0@user/channel --revisions -r default")[0] self.assertIn(pref.ref.revision, search_result["revision"]) @pytest.mark.skipif(not get_env("TESTING_REVISIONS_ENABLED", False), reason="Only revisions") def test_upload_with_package_revision(self): ref = ConanFileReference.loads("pkg/1.0@user/channel") client = TurboTestClient(default_server_user=True, revisions_enabled=True) pref = client.create(ref, conanfile=GenConanfile()) client.run( "upload pkg/1.0@user/channel#{}:{}#fakeprev --confirm".format( pref.ref.revision, pref.id), assert_error=True) self.assertIn( "ERROR: Binary package pkg/1.0@user/channel:{}#fakeprev not found". format(pref.id), client.out) client.run("upload pkg/1.0@user/channel#{}:{}#{} --confirm".format( pref.ref.revision, pref.id, pref.revision)) search_result = client.search( "pkg/1.0@user/channel --revisions -r default")[0] self.assertIn(pref.ref.revision, search_result["revision"]) search_result = client.search( "pkg/1.0@user/channel#{}:{} --revisions -r default".format( pref.ref.revision, pref.id))[0] self.assertIn(pref.revision, search_result["revision"])
class InstallOutdatedPackagesTest(unittest.TestCase): def setUp(self): test_server = TestServer() self.servers = {"default": test_server} self.client = TestClient(servers=self.servers, users={"default": [("lasote", "mypass")]}) self.new_client = TestClient(servers=self.servers, users={"default": [("lasote", "mypass")]}) self.ref = ConanFileReference.loads("Hello0/0.1@lasote/stable") files = cpp_hello_conan_files("Hello0", "0.1", build=False) self.client.save(files) self.client.run("export . lasote/stable") self.client.run("install Hello0/0.1@lasote/stable --build missing") self.client.run("upload Hello0/0.1@lasote/stable --all") @unittest.skipIf(get_env("TESTING_REVISIONS_ENABLED", False), "No sense with revs") def test_install_outdated(self): # If we try to install the same package with --build oudated it's already ok self.client.run("install Hello0/0.1@lasote/stable --build outdated") self.assertIn("Hello0/0.1@lasote/stable: Package is up to date", self.client.out) # Then we can export a modified recipe and try to install without --build outdated files = cpp_hello_conan_files("Hello0", "0.1", build=False) files["conanfile.py"] += "\n#Otherline" self.client.save(files) self.client.run("export . lasote/stable") self.client.run("install Hello0/0.1@lasote/stable") self.assertIn("Hello0/0.1@lasote/stable: Already installed!", self.client.out) self.assertNotIn("Package is up to date", self.client.out) self.assertNotIn("Outdated package!", self.client.out) # Try now with the --build outdated self.client.run("install Hello0/0.1@lasote/stable --build outdated") self.assertNotIn("Package is up to date", self.client.out) self.assertIn("Outdated package!", self.client.out) self.assertIn("Building your package", self.client.out) # Remove all local references, export again (the modified version not uploaded) # and try to install, it will discard the remote package too self.client.run("remove Hello0* -f") self.client.save(files) self.client.run("export . lasote/stable") self.client.run("remote add_ref Hello0/0.1@lasote/stable default") self.client.run("install Hello0/0.1@lasote/stable --build outdated") self.assertNotIn("Hello0/0.1@lasote/stable: Already installed!", self.client.out) self.assertNotIn("Package is up to date", self.client.out) self.assertIn("Building your package", self.client.out) def test_install_outdated_dep(self): # A new recipe that depends on Hello0/0.1 new_client = TestClient(servers=self.servers, users={"default": [("lasote", "mypass")]}) files = cpp_hello_conan_files("Hello1", "0.1", ["Hello0/0.1@lasote/stable"], build=False) new_client.save(files) new_client.run("export . lasote/stable") self.assertIn("A new conanfile.py version was exported", new_client.out) # It will retrieve from the remote Hello0 and build Hello1 new_client.run("install Hello1/0.1@lasote/stable --build missing") # Then modify REMOTE Hello0 recipe files (WITH THE OTHER CLIENT) files = cpp_hello_conan_files("Hello0", "0.1", build=False) files["conanfile.py"] += "\n#MODIFIED RECIPE" self.client.save(files) self.client.run("export . lasote/stable") self.assertIn("A new conanfile.py version was exported", self.client.out) self.client.run("install Hello0/0.1@lasote/stable --build missing") # Upload only the recipe, so the package is outdated in the server self.client.run("upload Hello0/0.1@lasote/stable") # Now, with the new_client, remove only the binary package from Hello0 rmdir(new_client.cache.package_layout(self.ref).packages()) # And try to install Hello1 again, should not complain because the remote # binary is in the "same version" than local cached Hello0 new_client.run("install Hello1/0.1@lasote/stable --build outdated") self.assertIn("Downloading conan_package.tgz", new_client.out) self.assertIn("Hello0/0.1@lasote/stable: Package is up to date", new_client.out) # With revisions makes no sense, it won't download an outdated package, it belongs to # a different recipe if not new_client.cache.config.revisions_enabled: # But if we remove the full Hello0 local package, will retrieve the updated # recipe and the outdated package new_client.run("remove Hello0* -f") new_client.run("install Hello1/0.1@lasote/stable --build outdated") self.assertIn("Hello0/0.1@lasote/stable: Outdated package!", new_client.out) self.assertIn("Hello0/0.1@lasote/stable: Building your package", new_client.out) def test_install_outdated_and_dep(self): # regression test for https://github.com/conan-io/conan/issues/1053 # A new recipe that depends on Hello0/0.1 new_client = TestClient(servers=self.servers, users={"default": [("lasote", "mypass")]}) files = cpp_hello_conan_files("Hello1", "0.1", ["Hello0/0.1@lasote/stable"], build=False) new_client.save(files) new_client.run("export . lasote/stable") self.assertIn("A new conanfile.py version was exported", new_client.out) # It will retrieve from the remote Hello0 and build Hello1 new_client.run("install Hello1/0.1@lasote/stable --build missing") # Then modify REMOTE Hello0 recipe files (WITH THE OTHER CLIENT) files = cpp_hello_conan_files("Hello0", "0.1", build=False) files["conanfile.py"] += "\n#MODIFIED RECIPE" self.client.save(files) self.client.run("export . lasote/stable") self.assertIn("A new conanfile.py version was exported", self.client.out) self.client.run("install Hello0/0.1@lasote/stable --build missing") # Upload only the recipe, so the package is outdated in the server self.client.run("upload Hello0/0.1@lasote/stable") # Now, with the new_client, remove only the binary package from Hello0 rmdir(new_client.cache.package_layout(self.ref).packages()) # And try to install Hello1 again, should not complain because the remote # binary is in the "same version" than local cached Hello0 new_client.run( "install Hello1/0.1@lasote/stable --build outdated --build Hello1") self.assertIn("Downloading conan_package.tgz", new_client.out) self.assertIn("Hello1/0.1@lasote/stable: Forced build from source", new_client.out) def test_install_outdated_checking_updates(self): server = TestServer() servers = OrderedDict([("default", server)]) client = TurboTestClient(servers=servers) client2 = TurboTestClient(servers=servers) ref = ConanFileReference.loads("lib/1.0@conan/testing") client.create(ref) client.upload_all(ref) # Generate a new recipe, the binary becomes outdated time.sleep(1) client2.create( ref, conanfile=GenConanfile().with_build_msg("Some modified stuff")) client2.run("upload {} -r default".format(ref)) # Update, building the outdated client.run("install -u -b outdated {}".format(ref)) # The outdated is built self.assertIn("Some modified stuff", client.out)