conans/client/conan_api.py
import json
import os
import sys
from collections import OrderedDict
from collections import namedtuple
from six import StringIO
import conans
from conans import __version__ as client_version
from conans.client.cache.cache import ClientCache
from conans.client.cmd.build import cmd_build
from conans.client.cmd.create import create
from conans.client.cmd.download import download
from conans.client.cmd.export import cmd_export, export_alias
from conans.client.cmd.export_pkg import export_pkg
from conans.client.cmd.profile import (cmd_profile_create, cmd_profile_delete_key, cmd_profile_get,
cmd_profile_list, cmd_profile_update)
from conans.client.cmd.search import Search
from conans.client.cmd.test import install_build_and_test
from conans.client.cmd.uploader import CmdUpload
from conans.client.cmd.user import user_set, users_clean, users_list, token_present
from conans.client.conanfile.package import run_package_method
from conans.client.conf.required_version import check_required_conan_version
from conans.client.generators import GeneratorManager
from conans.client.graph.graph import RECIPE_EDITABLE
from conans.client.graph.graph_binaries import GraphBinariesAnalyzer
from conans.client.graph.graph_manager import GraphManager
from conans.client.graph.printer import print_graph
from conans.client.graph.proxy import ConanProxy
from conans.client.graph.python_requires import ConanPythonRequire, PyRequireLoader
from conans.client.graph.range_resolver import RangeResolver
from conans.client.hook_manager import HookManager
from conans.client.importer import run_imports, undo_imports
from conans.client.installer import BinaryInstaller
from conans.client.loader import ConanFileLoader
from conans.client.manager import deps_install
from conans.client.migrations import ClientMigrator
from conans.client.output import ConanOutput, colorama_initialize
from conans.client.profile_loader import profile_from_args, read_profile
from conans.client.recorder.action_recorder import ActionRecorder
from conans.client.recorder.search_recorder import SearchRecorder
from conans.client.recorder.upload_recoder import UploadRecorder
from conans.client.remote_manager import RemoteManager
from conans.client.remover import ConanRemover
from conans.client.rest.auth_manager import ConanApiAuthManager
from conans.client.rest.conan_requester import ConanRequester
from conans.client.rest.rest_client import RestApiClientFactory
from conans.client.runner import ConanRunner
from conans.client.source import config_source_local
from conans.client.tools.env import environment_append
from conans.client.userio import UserIO
from conans.errors import (ConanException, RecipeNotFoundException,
PackageNotFoundException, NoRestV2Available, NotFoundException)
from conans.model.editable_layout import get_editable_abs_path
from conans.model.graph_info import GraphInfo, GRAPH_INFO_FILE
from conans.model.graph_lock import GraphLockFile, LOCKFILE, GraphLock
from conans.model.lock_bundle import LockBundle
from conans.model.ref import ConanFileReference, PackageReference, check_valid_ref
from conans.model.version import Version
from conans.model.workspace import Workspace
from conans.paths import BUILD_INFO, CONANINFO, get_conan_user_home
from conans.paths.package_layouts.package_cache_layout import PackageCacheLayout
from conans.search.search import search_recipes
from conans.tools import set_global_instances
from conans.util.conan_v2_mode import conan_v2_error
from conans.util.files import exception_message_safe, mkdir, save_files, load, save
from conans.util.log import configure_logger
from conans.util.tracer import log_command, log_exception
default_manifest_folder = '.conan_manifests'
class ProfileData(namedtuple("ProfileData", ["profiles", "settings", "options", "env", "conf"])):
def __bool__(self):
return bool(self.profiles or self.settings or self.options or self.env or self.conf)
__nonzero__ = __bool__
def api_method(f):
def wrapper(api, *args, **kwargs):
quiet = kwargs.pop("quiet", False)
try: # getcwd can fail if Conan runs on an unexisting folder
old_curdir = os.getcwd()
except EnvironmentError:
old_curdir = None
old_output = api.user_io.out
quiet_output = ConanOutput(StringIO(), color=api.color) if quiet else None
try:
api.create_app(quiet_output=quiet_output)
log_command(f.__name__, kwargs)
with environment_append(api.app.cache.config.env_vars):
return f(api, *args, **kwargs)
except Exception as exc:
if quiet_output:
old_output.write(quiet_output._stream.getvalue())
old_output.flush()
msg = exception_message_safe(exc)
try:
log_exception(exc, msg)
except BaseException:
pass
raise
finally:
if old_curdir:
os.chdir(old_curdir)
return wrapper
def _make_abs_path(path, cwd=None, default=None):
"""convert 'path' to absolute if necessary (could be already absolute)
if not defined (empty, or None), will return 'default' one or 'cwd'
"""
cwd = cwd or os.getcwd()
if not path:
abs_path = default or cwd
elif os.path.isabs(path):
abs_path = path
else:
abs_path = os.path.normpath(os.path.join(cwd, path))
return abs_path
def _get_conanfile_path(path, cwd, py):
"""
param py= True: Must be .py, False: Must be .txt, None: Try .py, then .txt
"""
candidate_paths = list()
path = _make_abs_path(path, cwd)
if os.path.isdir(path): # Can be a folder
if py:
path = os.path.join(path, "conanfile.py")
candidate_paths.append(path)
elif py is False:
path = os.path.join(path, "conanfile.txt")
candidate_paths.append(path)
else:
path_py = os.path.join(path, "conanfile.py")
candidate_paths.append(path_py)
if os.path.exists(path_py):
path = path_py
else:
path = os.path.join(path, "conanfile.txt")
candidate_paths.append(path)
else:
candidate_paths.append(path)
if not os.path.isfile(path): # Must exist
raise ConanException("Conanfile not found at %s" % " or ".join(candidate_paths))
if py and not path.endswith(".py"):
raise ConanException("A conanfile.py is needed, " + path + " is not acceptable")
return path
class ConanApp(object):
def __init__(self, cache_folder, user_io, http_requester=None, runner=None, quiet_output=None):
# User IO, interaction and logging
self.user_io = user_io
self.out = self.user_io.out
if quiet_output:
self.user_io.out = quiet_output
self.out = quiet_output
self.cache_folder = cache_folder
self.cache = ClientCache(self.cache_folder, self.out)
self.config = self.cache.config
if self.config.non_interactive or quiet_output:
self.user_io.disable_input()
# Adjust CONAN_LOGGING_LEVEL with the env readed
conans.util.log.logger = configure_logger(self.config.logging_level,
self.config.logging_file)
conans.util.log.logger.debug("INIT: Using config '%s'" % self.cache.conan_conf_path)
self.hook_manager = HookManager(self.cache.hooks_path, self.config.hooks, self.out)
# Wraps an http_requester to inject proxies, certs, etc
self.requester = ConanRequester(self.config, http_requester)
# To handle remote connections
artifacts_properties = self.cache.read_artifacts_properties()
rest_client_factory = RestApiClientFactory(self.out, self.requester, self.config,
artifacts_properties=artifacts_properties)
# Wraps RestApiClient to add authentication support (same interface)
auth_manager = ConanApiAuthManager(rest_client_factory, self.user_io, self.cache.localdb)
# Handle remote connections
self.remote_manager = RemoteManager(self.cache, auth_manager, self.out, self.hook_manager)
# Adjust global tool variables
set_global_instances(self.out, self.requester, self.config)
self.runner = runner or ConanRunner(self.config.print_commands_to_output,
self.config.generate_run_log_file,
self.config.log_run_to_output,
self.out)
self.proxy = ConanProxy(self.cache, self.out, self.remote_manager)
self.range_resolver = RangeResolver(self.cache, self.remote_manager)
self.generator_manager = GeneratorManager()
self.python_requires = ConanPythonRequire(self.proxy, self.range_resolver,
self.generator_manager)
self.pyreq_loader = PyRequireLoader(self.proxy, self.range_resolver)
self.loader = ConanFileLoader(self.runner, self.out, self.python_requires,
self.generator_manager, self.pyreq_loader, self.requester)
self.binaries_analyzer = GraphBinariesAnalyzer(self.cache, self.out, self.remote_manager)
self.graph_manager = GraphManager(self.out, self.cache, self.remote_manager, self.loader,
self.proxy, self.range_resolver, self.binaries_analyzer)
def load_remotes(self, remote_name=None, update=False, check_updates=False):
remotes = self.cache.registry.load_remotes()
if remote_name:
remotes.select(remote_name)
self.python_requires.enable_remotes(update=update, check_updates=check_updates,
remotes=remotes)
self.pyreq_loader.enable_remotes(update=update, check_updates=check_updates, remotes=remotes)
return remotes
class ConanAPIV1(object):
@classmethod
def factory(cls):
return cls(), None, None
def __init__(self, cache_folder=None, output=None, user_io=None, http_requester=None,
runner=None):
self.color = colorama_initialize()
self.out = output or ConanOutput(sys.stdout, sys.stderr, self.color)
self.user_io = user_io or UserIO(out=self.out)
self.cache_folder = cache_folder or os.path.join(get_conan_user_home(), ".conan")
self.http_requester = http_requester
self.runner = runner
self.app = None # Api calls will create a new one every call
# Migration system
migrator = ClientMigrator(self.cache_folder, Version(client_version), self.out)
migrator.migrate()
check_required_conan_version(self.cache_folder, self.out)
python_folder = os.path.join(self.cache_folder, "python")
conan_v2_error("Using code from cache/python not allowed", os.path.isdir(python_folder))
sys.path.append(python_folder)
def create_app(self, quiet_output=None):
self.app = ConanApp(self.cache_folder, self.user_io, self.http_requester,
self.runner, quiet_output=quiet_output)
@api_method
def new(self, name, header=False, pure_c=False, test=False, exports_sources=False, bare=False,
cwd=None, visual_versions=None, linux_gcc_versions=None, linux_clang_versions=None,
osx_clang_versions=None, shared=None, upload_url=None, gitignore=None,
gitlab_gcc_versions=None, gitlab_clang_versions=None,
circleci_gcc_versions=None, circleci_clang_versions=None, circleci_osx_versions=None,
template=None, defines=None):
from conans.client.cmd.new import cmd_new
cwd = os.path.abspath(cwd or os.getcwd())
files = cmd_new(name, header=header, pure_c=pure_c, test=test,
exports_sources=exports_sources, bare=bare,
visual_versions=visual_versions,
linux_gcc_versions=linux_gcc_versions,
linux_clang_versions=linux_clang_versions,
osx_clang_versions=osx_clang_versions, shared=shared,
upload_url=upload_url, gitignore=gitignore,
gitlab_gcc_versions=gitlab_gcc_versions,
gitlab_clang_versions=gitlab_clang_versions,
circleci_gcc_versions=circleci_gcc_versions,
circleci_clang_versions=circleci_clang_versions,
circleci_osx_versions=circleci_osx_versions,
template=template, cache=self.app.cache, defines=defines)
save_files(cwd, files)
for f in sorted(files):
self.app.out.success("File saved: %s" % f)
@api_method
def inspect(self, path, attributes, remote_name=None):
remotes = self.app.load_remotes(remote_name=remote_name)
try:
ref = ConanFileReference.loads(path)
except ConanException:
conanfile_path = _get_conanfile_path(path, os.getcwd(), py=True)
conanfile = self.app.loader.load_named(conanfile_path, None, None, None, None)
else:
if remote_name:
remotes = self.app.load_remotes()
remote = remotes.get_remote(remote_name)
try: # get_recipe_manifest can fail, not in server
_, ref = self.app.remote_manager.get_recipe_manifest(ref, remote)
except NotFoundException:
raise RecipeNotFoundException(ref)
else:
ref = self.app.remote_manager.get_recipe(ref, remote)
result = self.app.proxy.get_recipe(ref, False, False, remotes, ActionRecorder())
conanfile_path, _, _, ref = result
conanfile = self.app.loader.load_basic(conanfile_path)
conanfile.name = ref.name
# FIXME: Conan 2.0, this should be a string, not a Version object
conanfile.version = ref.version
result = OrderedDict()
if not attributes:
attributes = ['name', 'version', 'url', 'homepage', 'license', 'author',
'description', 'topics', 'generators', 'exports', 'exports_sources',
'short_paths', 'apply_env', 'build_policy', 'revision_mode', 'settings',
'options', 'default_options', 'deprecated']
# TODO: Change this in Conan 2.0, cli stdout should display only fields with values,
# json should contain all values for easy automation
for attribute in attributes:
try:
attr = getattr(conanfile, attribute)
result[attribute] = attr
except AttributeError:
result[attribute] = ''
return result
@api_method
def test(self, path, reference, profile_names=None, settings=None, options=None, env=None,
remote_name=None, update=False, build_modes=None, cwd=None, test_build_folder=None,
lockfile=None, profile_build=None, conf=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
remotes = self.app.load_remotes(remote_name=remote_name, update=update)
conanfile_path = _get_conanfile_path(path, cwd, py=True)
cwd = cwd or os.getcwd()
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(profile_host, profile_build, cwd, None,
self.app.cache, self.app.out, lockfile=lockfile)
ref = ConanFileReference.loads(reference)
recorder = ActionRecorder()
install_build_and_test(self.app, conanfile_path, ref, graph_info, remotes,
update, build_modes=build_modes,
test_build_folder=test_build_folder, recorder=recorder)
@api_method
def create(self, conanfile_path, name=None, version=None, user=None, channel=None,
profile_names=None, settings=None,
options=None, env=None, test_folder=None, not_export=False,
build_modes=None,
keep_source=False, keep_build=False, verify=None,
manifests=None, manifests_interactive=None,
remote_name=None, update=False, cwd=None, test_build_folder=None,
lockfile=None, lockfile_out=None, ignore_dirty=False, profile_build=None,
is_build_require=False, conf=None, require_overrides=None):
"""
API method to create a conan package
test_folder default None - looks for default 'test' or 'test_package' folder),
string - test_folder path
False - disabling tests
"""
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
cwd = cwd or os.getcwd()
recorder = ActionRecorder()
try:
conanfile_path = _get_conanfile_path(conanfile_path, cwd, py=True)
remotes = self.app.load_remotes(remote_name=remote_name, update=update)
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(profile_host, profile_build, cwd, None,
self.app.cache, self.app.out, lockfile=lockfile)
# Make sure keep_source is set for keep_build
keep_source = keep_source or keep_build
new_ref = cmd_export(self.app, conanfile_path, name, version, user, channel, keep_source,
not not_export, graph_lock=graph_info.graph_lock,
ignore_dirty=ignore_dirty)
self.app.range_resolver.clear_output() # invalidate version range output
# The new_ref contains the revision
# To not break existing things, that they used this ref without revision
ref = new_ref.copy_clear_rev()
recorder.recipe_exported(new_ref)
if build_modes is None: # Not specified, force build the tested library
build_modes = [ref.name]
manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd)
manifest_folder, manifest_interactive, manifest_verify = manifests
# FIXME: Dirty hack: remove the root for the test_package/conanfile.py consumer
graph_info.root = ConanFileReference(None, None, None, None, validate=False)
recorder.add_recipe_being_developed(ref)
create(self.app, ref, graph_info, remotes, update, build_modes,
manifest_folder, manifest_verify, manifest_interactive, keep_build,
test_build_folder, test_folder, conanfile_path, recorder=recorder,
is_build_require=is_build_require, require_overrides=require_overrides)
if lockfile_out:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file = GraphLockFile(graph_info.profile_host, graph_info.profile_build,
graph_info.graph_lock)
graph_lock_file.save(lockfile_out)
return recorder.get_info(self.app.config.revisions_enabled)
except ConanException as exc:
recorder.error = True
exc.info = recorder.get_info(self.app.config.revisions_enabled)
raise
@api_method
def export_pkg(self, conanfile_path, name, channel, source_folder=None, build_folder=None,
package_folder=None, install_folder=None, profile_names=None, settings=None,
options=None, env=None, force=False, user=None, version=None, cwd=None,
lockfile=None, lockfile_out=None, ignore_dirty=False, profile_build=None,
conf=None, output_folder=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
remotes = self.app.load_remotes()
cwd = cwd or os.getcwd()
recorder = ActionRecorder()
try:
conanfile_path = _get_conanfile_path(conanfile_path, cwd, py=True)
if package_folder:
if build_folder or source_folder:
raise ConanException("package folder definition incompatible with build "
"and source folders")
package_folder = _make_abs_path(package_folder, cwd)
output_folder = _make_abs_path(output_folder, cwd) if output_folder else None
build_folder = _make_abs_path(build_folder, cwd)
if install_folder:
install_folder = _make_abs_path(install_folder, cwd)
else:
# FIXME: This is a hack for old UI, need to be fixed in Conan 2.0
if os.path.exists(os.path.join(build_folder, GRAPH_INFO_FILE)):
install_folder = build_folder
source_folder = _make_abs_path(source_folder, cwd,
default=os.path.dirname(conanfile_path))
for folder, path in {"source": source_folder, "build": build_folder,
"package": package_folder}.items():
if path and not os.path.exists(path):
raise ConanException("The {} folder '{}' does not exist."
.format(folder, path))
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
# Checks that no both settings and info files are specified
graph_info = get_graph_info(profile_host, profile_build, cwd, install_folder,
self.app.cache, self.app.out, lockfile=lockfile)
new_ref = cmd_export(self.app, conanfile_path, name, version, user, channel, True,
graph_lock=graph_info.graph_lock, ignore_dirty=ignore_dirty)
ref = new_ref.copy_clear_rev()
# new_ref has revision
recorder.recipe_exported(new_ref)
recorder.add_recipe_being_developed(ref)
export_pkg(self.app, recorder, new_ref, source_folder=source_folder,
build_folder=build_folder, package_folder=package_folder,
install_folder=install_folder, graph_info=graph_info, force=force,
remotes=remotes, source_conanfile_path=conanfile_path,
output_folder=output_folder)
if lockfile_out:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file = GraphLockFile(graph_info.profile_host, graph_info.profile_build,
graph_info.graph_lock)
graph_lock_file.save(lockfile_out)
return recorder.get_info(self.app.config.revisions_enabled)
except ConanException as exc:
recorder.error = True
exc.info = recorder.get_info(self.app.config.revisions_enabled)
raise
@api_method
def download(self, reference, remote_name=None, packages=None, recipe=False):
if packages and recipe:
raise ConanException("recipe parameter cannot be used together with packages")
# Install packages without settings (fixed ids or all)
if check_valid_ref(reference):
ref = ConanFileReference.loads(reference)
if ref.revision and not self.app.config.revisions_enabled:
raise ConanException("Revisions not enabled in the client, specify a "
"reference without revision")
if packages and ref.revision is None:
for package_id in packages:
if "#" in package_id:
raise ConanException("It is needed to specify the recipe revision if you "
"specify a package revision")
remotes = self.app.load_remotes(remote_name=remote_name)
remote = remotes.get_remote(remote_name)
recorder = ActionRecorder()
download(self.app, ref, packages, remote, recipe, recorder, remotes=remotes)
else:
raise ConanException("Provide a valid full reference without wildcards.")
@api_method
def workspace_install(self, path, settings=None, options=None, env=None,
remote_name=None, build=None, profile_name=None,
update=False, cwd=None, install_folder=None, profile_build=None,
conf=None):
profile_host = ProfileData(profiles=profile_name, settings=settings, options=options,
env=env, conf=conf)
cwd = cwd or os.getcwd()
abs_path = os.path.normpath(os.path.join(cwd, path))
remotes = self.app.load_remotes(remote_name=remote_name, update=update)
workspace = Workspace(abs_path, self.app.cache)
graph_info = get_graph_info(profile_host, profile_build, cwd, None,
self.app.cache, self.app.out)
self.app.out.info("Configuration:")
self.app.out.writeln(graph_info.profile_host.dumps())
self.app.cache.editable_packages.override(workspace.get_editable_dict())
recorder = ActionRecorder()
deps_graph = self.app.graph_manager.load_graph(workspace.root, None, graph_info, build,
False, update, remotes, recorder)
print_graph(deps_graph, self.app.out)
# Inject the generators before installing
for node in deps_graph.nodes:
if node.recipe == RECIPE_EDITABLE:
generators = workspace[node.ref].generators
if generators is not None:
tmp = list(node.conanfile.generators)
tmp.extend([g for g in generators if g not in tmp])
node.conanfile.generators = tmp
installer = BinaryInstaller(self.app, recorder=recorder)
installer.install(deps_graph, remotes, build, update, graph_info.profile_host,
graph_info.profile_build, graph_lock=graph_info.graph_lock,
keep_build=False)
install_folder = install_folder or cwd
workspace.generate(install_folder, deps_graph, self.app.out)
@api_method
def install_reference(self, reference, settings=None, options=None, env=None,
remote_name=None, verify=None, manifests=None,
manifests_interactive=None, build=None, profile_names=None,
update=False, generators=None, install_folder=None, cwd=None,
lockfile=None, lockfile_out=None, profile_build=None,
lockfile_node_id=None, is_build_require=False, conf=None,
require_overrides=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
recorder = ActionRecorder()
cwd = cwd or os.getcwd()
try:
manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd)
manifest_folder, manifest_interactive, manifest_verify = manifests
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(profile_host, profile_build, cwd, None,
self.app.cache, self.app.out, lockfile=lockfile)
install_folder = _make_abs_path(install_folder, cwd)
mkdir(install_folder)
remotes = self.app.load_remotes(remote_name=remote_name, update=update)
deps_install(self.app, ref_or_path=reference, install_folder=install_folder,
base_folder=cwd, remotes=remotes, graph_info=graph_info, build_modes=build,
update=update, manifest_folder=manifest_folder,
manifest_verify=manifest_verify,
manifest_interactive=manifest_interactive,
generators=generators, recorder=recorder,
lockfile_node_id=lockfile_node_id,
is_build_require=is_build_require,
add_txt_generator=False,
require_overrides=require_overrides)
if lockfile_out:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file = GraphLockFile(graph_info.profile_host, graph_info.profile_build,
graph_info.graph_lock)
graph_lock_file.save(lockfile_out)
return recorder.get_info(self.app.config.revisions_enabled)
except ConanException as exc:
recorder.error = True
exc.info = recorder.get_info(self.app.config.revisions_enabled)
raise
@api_method
def install(self, path="", name=None, version=None, user=None, channel=None,
settings=None, options=None, env=None,
remote_name=None, verify=None, manifests=None,
manifests_interactive=None, build=None, profile_names=None,
update=False, generators=None, no_imports=False, install_folder=None,
output_folder=None, cwd=None,
lockfile=None, lockfile_out=None, profile_build=None, conf=None,
require_overrides=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
recorder = ActionRecorder()
cwd = cwd or os.getcwd()
try:
manifests = _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd)
manifest_folder, manifest_interactive, manifest_verify = manifests
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(profile_host, profile_build, cwd, None,
self.app.cache, self.app.out,
name=name, version=version, user=user, channel=channel,
lockfile=lockfile)
install_folder = _make_abs_path(install_folder, cwd)
if output_folder:
output_folder = _make_abs_path(output_folder, cwd)
conanfile_path = _get_conanfile_path(path, cwd, py=None)
remotes = self.app.load_remotes(remote_name=remote_name, update=update)
deps_install(app=self.app,
ref_or_path=conanfile_path,
install_folder=install_folder,
output_folder=output_folder,
base_folder=cwd,
remotes=remotes,
graph_info=graph_info,
build_modes=build,
update=update,
manifest_folder=manifest_folder,
manifest_verify=manifest_verify,
manifest_interactive=manifest_interactive,
generators=generators,
no_imports=no_imports,
recorder=recorder,
require_overrides=require_overrides,
conanfile_path=os.path.dirname(conanfile_path))
if lockfile_out:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file = GraphLockFile(graph_info.profile_host, graph_info.profile_build,
graph_info.graph_lock)
graph_lock_file.save(lockfile_out)
return recorder.get_info(self.app.config.revisions_enabled)
except ConanException as exc:
recorder.error = True
exc.info = recorder.get_info(self.app.config.revisions_enabled)
raise
@api_method
def config_get(self, item):
if item == "storage.path":
result = self.app.config.storage_path
else:
result = self.app.config.get_item(item)
self.app.out.info(result)
return result
@api_method
def config_set(self, item, value):
self.app.config.set_item(item, value)
@api_method
def config_rm(self, item):
self.app.config.rm_item(item)
@api_method
def config_install_list(self):
if not os.path.isfile(self.app.cache.config_install_file):
return []
return json.loads(load(self.app.cache.config_install_file))
@api_method
def config_install_remove(self, index):
if not os.path.isfile(self.app.cache.config_install_file):
raise ConanException("There is no config data. Need to install config first.")
configs = json.loads(load(self.app.cache.config_install_file))
try:
configs.pop(index)
except Exception as e:
raise ConanException("Config %s can't be removed: %s" % (index, str(e)))
save(self.app.cache.config_install_file, json.dumps(configs))
@api_method
def config_install(self, path_or_url, verify_ssl, config_type=None, args=None,
source_folder=None, target_folder=None):
from conans.client.conf.config_installer import configuration_install
return configuration_install(self.app, path_or_url, verify_ssl,
config_type=config_type, args=args,
source_folder=source_folder, target_folder=target_folder)
@api_method
def config_home(self):
return self.cache_folder
@api_method
def config_init(self, force=False):
if force:
self.app.cache.reset_config()
self.app.cache.registry.reset_remotes()
self.app.cache.reset_default_profile()
self.app.cache.reset_settings()
else:
self.app.cache.initialize_config()
self.app.cache.registry.initialize_remotes()
self.app.cache.initialize_default_profile()
self.app.cache.initialize_settings()
def _info_args(self, reference_or_path, install_folder, profile_host, profile_build,
lockfile=None):
cwd = os.getcwd()
if check_valid_ref(reference_or_path):
ref = ConanFileReference.loads(reference_or_path)
install_folder = _make_abs_path(install_folder, cwd) if install_folder else None
else:
ref = _get_conanfile_path(reference_or_path, cwd=None, py=None)
install_folder = _make_abs_path(install_folder, cwd)
if not os.path.exists(os.path.join(install_folder, GRAPH_INFO_FILE)):
install_folder = None
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(profile_host, profile_build, cwd, install_folder,
self.app.cache, self.app.out, lockfile=lockfile)
return ref, graph_info
@api_method
def info_build_order(self, reference, settings=None, options=None, env=None,
profile_names=None, remote_name=None, build_order=None, check_updates=None,
install_folder=None, profile_build=None, conf=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
reference, graph_info = self._info_args(reference, install_folder, profile_host,
profile_build)
recorder = ActionRecorder()
remotes = self.app.load_remotes(remote_name=remote_name, check_updates=check_updates)
deps_graph = self.app.graph_manager.load_graph(reference, None, graph_info, ["missing"],
check_updates, False, remotes, recorder)
return deps_graph.build_order(build_order)
@api_method
def info_nodes_to_build(self, reference, build_modes, settings=None, options=None, env=None,
profile_names=None, remote_name=None, check_updates=None,
install_folder=None, profile_build=None, conf=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
reference, graph_info = self._info_args(reference, install_folder, profile_host,
profile_build)
recorder = ActionRecorder()
remotes = self.app.load_remotes(remote_name=remote_name, check_updates=check_updates)
deps_graph = self.app.graph_manager.load_graph(reference, None, graph_info, build_modes,
check_updates, False, remotes, recorder)
nodes_to_build = deps_graph.nodes_to_build()
return nodes_to_build, deps_graph.root.conanfile
@api_method
def info(self, reference_or_path, remote_name=None, settings=None, options=None, env=None,
profile_names=None, update=False, install_folder=None, build=None, lockfile=None,
profile_build=None, conf=None):
profile_host = ProfileData(profiles=profile_names, settings=settings, options=options,
env=env, conf=conf)
reference, graph_info = self._info_args(reference_or_path, install_folder, profile_host,
profile_build,
lockfile=lockfile)
recorder = ActionRecorder()
# FIXME: Using update as check_update?
remotes = self.app.load_remotes(remote_name=remote_name, check_updates=update)
deps_graph = self.app.graph_manager.load_graph(reference, None, graph_info, build,
update, False, remotes, recorder)
if install_folder:
output_folder = _make_abs_path(install_folder)
graph_info.save(output_folder)
self.app.out.info("Generated graphinfo")
return deps_graph, deps_graph.root.conanfile
@api_method
def build(self, conanfile_path, source_folder=None, package_folder=None, build_folder=None,
install_folder=None, should_configure=True, should_build=True, should_install=True,
should_test=True, cwd=None):
self.app.load_remotes()
cwd = cwd or os.getcwd()
conanfile_path = _get_conanfile_path(conanfile_path, cwd, py=True)
layout_build_folder = _make_abs_path(build_folder, cwd) if build_folder else None
layout_source_folder = _make_abs_path(source_folder, cwd) if source_folder else None
build_folder = _make_abs_path(build_folder, cwd)
install_folder = _make_abs_path(install_folder, cwd, default=build_folder)
source_folder = _make_abs_path(source_folder, cwd, default=os.path.dirname(conanfile_path))
default_pkg_folder = os.path.join(build_folder, "package")
package_folder = _make_abs_path(package_folder, cwd, default=default_pkg_folder)
cmd_build(self.app, conanfile_path, base_path=cwd,
source_folder=source_folder, build_folder=build_folder,
package_folder=package_folder, install_folder=install_folder,
should_configure=should_configure, should_build=should_build,
should_install=should_install, should_test=should_test,
layout_source_folder=layout_source_folder, layout_build_folder=layout_build_folder)
@api_method
def package(self, path, build_folder, package_folder, source_folder=None, install_folder=None,
cwd=None):
self.app.load_remotes()
cwd = cwd or os.getcwd()
conanfile_path = _get_conanfile_path(path, cwd, py=True)
build_folder = _make_abs_path(build_folder, cwd)
install_folder = _make_abs_path(install_folder, cwd, default=build_folder)
source_folder = _make_abs_path(source_folder, cwd, default=os.path.dirname(conanfile_path))
conanfile = self.app.graph_manager.load_consumer_conanfile(conanfile_path, install_folder,
deps_info_required=True)
default_pkg_folder = os.path.join(build_folder, "package")
package_folder = _make_abs_path(package_folder, cwd, default=default_pkg_folder)
if hasattr(conanfile, "layout"):
raise ConanException("The usage of the 'conan package' local method is disabled when "
"using layout(). Use 'export-pkg' to test if the recipe is "
"packaging the files correctly or use the cpp.info.local object "
"if you are going to use this package as editable package.")
else:
conanfile.folders.set_base_build(build_folder)
conanfile.folders.set_base_source(source_folder)
conanfile.folders.set_base_package(package_folder)
conanfile.folders.set_base_install(install_folder)
run_package_method(conanfile, None, self.app.hook_manager, conanfile_path, None,
copy_info=True)
@api_method
def source(self, path, source_folder=None, info_folder=None, cwd=None):
self.app.load_remotes()
cwd = cwd or os.getcwd()
conanfile_path = _get_conanfile_path(path, cwd, py=True)
source_folder = _make_abs_path(source_folder, cwd)
info_folder = _make_abs_path(info_folder, cwd)
mkdir(source_folder)
if not os.path.exists(info_folder):
raise ConanException("Specified info-folder doesn't exist")
# only infos if exist
conanfile = self.app.graph_manager.load_consumer_conanfile(conanfile_path, info_folder)
conanfile.folders.set_base_source(source_folder)
conanfile.folders.set_base_export_sources(source_folder)
conanfile.folders.set_base_build(None)
conanfile.folders.set_base_package(None)
config_source_local(conanfile, conanfile_path, self.app.hook_manager)
@api_method
def imports(self, path, dest=None, info_folder=None, cwd=None):
"""
:param path: Path to the conanfile
:param dest: Dir to put the imported files. (Abs path or relative to cwd)
:param info_folder: Dir where the conaninfo.txt and conanbuildinfo.txt files are
:param cwd: Current working directory
:return: None
"""
cwd = cwd or os.getcwd()
info_folder = _make_abs_path(info_folder, cwd)
dest = _make_abs_path(dest, cwd)
self.app.load_remotes()
conanfile_abs_path = _get_conanfile_path(path, cwd, py=None)
conanfile = self.app.graph_manager.load_consumer_conanfile(conanfile_abs_path, info_folder,
deps_info_required=True)
conanfile.folders.set_base_imports(dest)
run_imports(conanfile)
@api_method
def imports_undo(self, manifest_path):
cwd = os.getcwd()
manifest_path = _make_abs_path(manifest_path, cwd)
undo_imports(manifest_path, self.app.out)
@api_method
def export(self, path, name, version, user, channel, keep_source=False, cwd=None,
lockfile=None, lockfile_out=None, ignore_dirty=False):
conanfile_path = _get_conanfile_path(path, cwd, py=True)
graph_lock, graph_lock_file = None, None
if lockfile:
lockfile = _make_abs_path(lockfile, cwd)
graph_lock_file = GraphLockFile.load(lockfile, self.app.config.revisions_enabled)
graph_lock = graph_lock_file.graph_lock
self.app.out.info("Using lockfile: '{}'".format(lockfile))
self.app.load_remotes()
cmd_export(self.app, conanfile_path, name, version, user, channel, keep_source,
graph_lock=graph_lock, ignore_dirty=ignore_dirty)
if lockfile_out and graph_lock_file:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file.save(lockfile_out)
@api_method
def remove(self, pattern, query=None, packages=None, builds=None, src=False, force=False,
remote_name=None, outdated=False):
remotes = self.app.cache.registry.load_remotes()
remover = ConanRemover(self.app.cache, self.app.remote_manager, self.app.user_io, remotes)
remover.remove(pattern, remote_name, src, builds, packages, force=force,
packages_query=query, outdated=outdated)
@api_method
def copy(self, reference, user_channel, force=False, packages=None):
"""
param packages: None=No binaries, True=All binaries, else list of IDs
"""
from conans.client.cmd.copy import cmd_copy
remotes = self.app.load_remotes()
# FIXME: conan copy does not support short-paths in Windows
ref = ConanFileReference.loads(reference)
cmd_copy(ref, user_channel, packages, self.app.cache,
self.app.user_io, self.app.remote_manager, self.app.loader, remotes, force=force)
@api_method
def authenticate(self, name, password, remote_name, skip_auth=False):
# FIXME: 2.0 rename "name" to "user".
# FIXME: 2.0 probably we should return also if we have been authenticated or not (skipped)
# FIXME: 2.0 remove the skip_auth argument, that behavior will be done by:
# "conan user USERNAME -r remote" that will use the local credentials (
# and verify that are valid)
# against the server. Currently it only "associate" the USERNAME with the remote
# without checking anything else
remote = self.get_remote_by_name(remote_name)
if skip_auth and token_present(self.app.cache.localdb, remote, name):
return remote.name, name, name
if not password:
name, password = self.app.user_io.request_login(remote_name=remote_name, username=name)
remote_name, prev_user, user = self.app.remote_manager.authenticate(remote, name, password)
return remote_name, prev_user, user
@api_method
def user_set(self, user, remote_name=None):
remote = (self.get_default_remote() if not remote_name
else self.get_remote_by_name(remote_name))
return user_set(self.app.cache.localdb, user, remote)
@api_method
def users_clean(self):
users_clean(self.app.cache.localdb)
@api_method
def users_list(self, remote_name=None):
info = {"error": False, "remotes": []}
remotes = [self.get_remote_by_name(remote_name)] if remote_name else self.remote_list()
try:
info["remotes"] = users_list(self.app.cache.localdb, remotes)
return info
except ConanException as exc:
info["error"] = True
exc.info = info
raise
@api_method
def search_recipes(self, pattern, remote_name=None, case_sensitive=False,
fill_revisions=False):
search_recorder = SearchRecorder()
remotes = self.app.cache.registry.load_remotes()
search = Search(self.app.cache, self.app.remote_manager, remotes)
try:
references = search.search_recipes(pattern, remote_name, case_sensitive)
except ConanException as exc:
search_recorder.error = True
exc.info = search_recorder.get_info()
raise
for remote_name, refs in references.items():
for ref in refs:
if fill_revisions:
layout = self.app.cache.package_layout(ref)
if isinstance(layout, PackageCacheLayout):
ref = ref.copy_with_rev(layout.recipe_revision())
search_recorder.add_recipe(remote_name, ref, with_packages=False)
return search_recorder.get_info()
@api_method
def search_packages(self, reference, query=None, remote_name=None, outdated=False):
search_recorder = SearchRecorder()
remotes = self.app.cache.registry.load_remotes()
search = Search(self.app.cache, self.app.remote_manager, remotes)
try:
ref = ConanFileReference.loads(reference)
references = search.search_packages(ref, remote_name, query=query, outdated=outdated)
except ConanException as exc:
search_recorder.error = True
exc.info = search_recorder.get_info()
raise
for remote_name, remote_ref in references.items():
search_recorder.add_recipe(remote_name, ref)
if remote_ref.ordered_packages:
for package_id, properties in remote_ref.ordered_packages.items():
package_recipe_hash = properties.get("recipe_hash", None)
# Artifactory uses field 'requires', conan_center 'full_requires'
requires = properties.get("requires", []) or properties.get("full_requires", [])
search_recorder.add_package(remote_name, ref,
package_id, properties.get("options", []),
properties.get("settings", []),
requires,
remote_ref.recipe_hash != package_recipe_hash)
return search_recorder.get_info()
@api_method
def upload(self, pattern, package=None, remote_name=None, all_packages=False, confirm=False,
retry=None, retry_wait=None, integrity_check=False, policy=None, query=None,
parallel_upload=False):
""" Uploads a package recipe and the generated binary packages to a specified remote
"""
upload_recorder = UploadRecorder()
uploader = CmdUpload(self.app.cache, self.app.user_io, self.app.remote_manager,
self.app.loader, self.app.hook_manager)
remotes = self.app.load_remotes(remote_name=remote_name)
try:
uploader.upload(pattern, remotes, upload_recorder, package, all_packages, confirm,
retry, retry_wait, integrity_check, policy, query=query,
parallel_upload=parallel_upload)
return upload_recorder.get_info()
except ConanException as exc:
upload_recorder.error = True
exc.info = upload_recorder.get_info()
raise
@api_method
def remote_list(self):
return list(self.app.cache.registry.load_remotes().all_values())
@api_method
def remote_add(self, remote_name, url, verify_ssl=True, insert=None, force=None):
return self.app.cache.registry.add(remote_name, url, verify_ssl, insert, force)
@api_method
def remote_remove(self, remote_name):
return self.app.cache.registry.remove(remote_name)
@api_method
def remote_set_disabled_state(self, remote_name, state):
return self.app.cache.registry.set_disabled_state(remote_name, state)
@api_method
def remote_update(self, remote_name, url, verify_ssl=True, insert=None):
return self.app.cache.registry.update(remote_name, url, verify_ssl, insert)
@api_method
def remote_rename(self, remote_name, new_new_remote):
return self.app.cache.registry.rename(remote_name, new_new_remote)
@api_method
def remote_list_ref(self, no_remote=False):
if no_remote:
result = {}
for ref in self.app.cache.all_refs():
metadata = self.app.cache.package_layout(ref).load_metadata()
if not metadata.recipe.remote:
result[str(ref)] = None
return result
else:
return {str(r): remote_name for r, remote_name in
self.app.cache.registry.refs_list.items()
if remote_name}
@api_method
def remote_add_ref(self, reference, remote_name):
ref = ConanFileReference.loads(reference, validate=True)
remote = self.app.cache.registry.load_remotes()[remote_name]
with self.app.cache.package_layout(ref).update_metadata() as metadata:
metadata.recipe.remote = remote.name
@api_method
def remote_remove_ref(self, reference):
ref = ConanFileReference.loads(reference, validate=True)
with self.app.cache.package_layout(ref).update_metadata() as metadata:
metadata.recipe.remote = None
@api_method
def remote_update_ref(self, reference, remote_name):
ref = ConanFileReference.loads(reference, validate=True)
remote = self.app.cache.registry.load_remotes()[remote_name]
with self.app.cache.package_layout(ref).update_metadata() as metadata:
metadata.recipe.remote = remote.name
@api_method
def remote_list_pref(self, reference, no_remote=False):
ref = ConanFileReference.loads(reference, validate=True)
if no_remote:
result = {}
metadata = self.app.cache.package_layout(ref).load_metadata()
for pid, pkg_metadata in metadata.packages.items():
if not pkg_metadata.remote:
pref = PackageReference(ref, pid)
result[repr(pref)] = None
return result
else:
ret = {}
tmp = self.app.cache.registry.prefs_list
for pref, remote in tmp.items():
if pref.ref == ref and remote:
ret[repr(pref)] = remote
return ret
@api_method
def remote_add_pref(self, package_reference, remote_name):
pref = PackageReference.loads(package_reference, validate=True)
remote = self.app.cache.registry.load_remotes()[remote_name]
with self.app.cache.package_layout(pref.ref).update_metadata() as metadata:
m = metadata.packages.get(pref.id)
if m and m.remote:
raise ConanException("%s already exists. Use update" % str(pref))
metadata.packages[pref.id].remote = remote.name
@api_method
def remote_remove_pref(self, package_reference):
pref = PackageReference.loads(package_reference, validate=True)
with self.app.cache.package_layout(pref.ref).update_metadata() as metadata:
m = metadata.packages.get(pref.id)
if m:
m.remote = None
@api_method
def remote_update_pref(self, package_reference, remote_name):
pref = PackageReference.loads(package_reference, validate=True)
_ = self.app.cache.registry.load_remotes()[remote_name]
with self.app.cache.package_layout(pref.ref).update_metadata() as metadata:
m = metadata.packages.get(pref.id)
if m:
m.remote = remote_name
@api_method
def remote_clean(self):
return self.app.cache.registry.clear()
@api_method
def remove_system_reqs(self, reference):
try:
ref = ConanFileReference.loads(reference)
self.app.cache.package_layout(ref).remove_system_reqs()
self.app.out.info(
"Cache system_reqs from %s has been removed" % repr(ref))
except Exception as error:
raise ConanException("Unable to remove system_reqs: %s" % error)
@api_method
def remove_system_reqs_by_pattern(self, pattern):
for ref in search_recipes(self.app.cache, pattern=pattern):
self.remove_system_reqs(repr(ref))
@api_method
def remove_locks(self):
self.app.cache.remove_locks()
@api_method
def profile_list(self):
return cmd_profile_list(self.app.cache.profiles_path, self.app.out)
@api_method
def create_profile(self, profile_name, detect=False, force=False):
return cmd_profile_create(profile_name, self.app.cache.profiles_path,
self.app.out, detect, force)
@api_method
def update_profile(self, profile_name, key, value):
return cmd_profile_update(profile_name, key, value, self.app.cache.profiles_path)
@api_method
def get_profile_key(self, profile_name, key):
return cmd_profile_get(profile_name, key, self.app.cache.profiles_path)
@api_method
def delete_profile_key(self, profile_name, key):
return cmd_profile_delete_key(profile_name, key, self.app.cache.profiles_path)
@api_method
def read_profile(self, profile=None):
p, _ = read_profile(profile, os.getcwd(), self.app.cache.profiles_path)
return p
@api_method
def get_path(self, reference, package_id=None, path=None, remote_name=None):
ref = ConanFileReference.loads(reference)
if not path:
path = "conanfile.py" if not package_id else "conaninfo.txt"
if not remote_name:
package_layout = self.app.cache.package_layout(ref, short_paths=None)
return package_layout.get_path(path=path, package_id=package_id), path
else:
remote = self.get_remote_by_name(remote_name)
if self.app.config.revisions_enabled and not ref.revision:
ref = self.app.remote_manager.get_latest_recipe_revision(ref, remote)
if package_id:
pref = PackageReference(ref, package_id)
if self.app.config.revisions_enabled and not pref.revision:
pref = self.app.remote_manager.get_latest_package_revision(pref, remote)
return self.app.remote_manager.get_package_path(pref, path, remote), path
else:
return self.app.remote_manager.get_recipe_path(ref, path, remote), path
@api_method
def export_alias(self, reference, target_reference):
self.app.load_remotes()
ref = ConanFileReference.loads(reference)
target_ref = ConanFileReference.loads(target_reference)
if ref.name != target_ref.name:
raise ConanException("An alias can only be defined to a package with the same name")
# Do not allow to override an existing package
alias_conanfile_path = self.app.cache.package_layout(ref).conanfile()
if os.path.exists(alias_conanfile_path):
conanfile = self.app.loader.load_basic(alias_conanfile_path)
if not getattr(conanfile, 'alias', None):
raise ConanException("Reference '{}' is already a package, remove it before "
"creating and alias with the same name".format(ref))
package_layout = self.app.cache.package_layout(ref)
return export_alias(package_layout, target_ref,
revisions_enabled=self.app.config.revisions_enabled,
output=self.app.out)
@api_method
def get_default_remote(self):
return self.app.cache.registry.load_remotes().default
@api_method
def get_remote_by_name(self, remote_name):
return self.app.cache.registry.load_remotes()[remote_name]
@api_method
def get_recipe_revisions(self, reference, remote_name=None):
if not self.app.config.revisions_enabled:
raise ConanException("The client doesn't have the revisions feature enabled."
" Enable this feature setting to '1' the environment variable"
" 'CONAN_REVISIONS_ENABLED' or the config value"
" 'general.revisions_enabled' in your conan.conf file")
ref = ConanFileReference.loads(reference)
if ref.revision:
raise ConanException("Cannot list the revisions of a specific recipe revision")
if not remote_name:
layout = self.app.cache.package_layout(ref)
try:
rev = layout.recipe_revision()
except RecipeNotFoundException as e:
e.print_rev = True
raise e
# Check the time in the associated remote if any
remote_name = layout.load_metadata().recipe.remote
remote = self.app.cache.registry.load_remotes()[remote_name] if remote_name else None
rev_time = None
if remote:
try:
revisions = self.app.remote_manager.get_recipe_revisions(ref, remote)
except RecipeNotFoundException:
pass
except (NoRestV2Available, NotFoundException):
rev_time = None
else:
tmp = {r["revision"]: r["time"] for r in revisions}
rev_time = tmp.get(rev)
return [{"revision": rev, "time": rev_time}]
else:
remote = self.get_remote_by_name(remote_name)
return self.app.remote_manager.get_recipe_revisions(ref, remote=remote)
@api_method
def get_package_revisions(self, reference, remote_name=None):
if not self.app.config.revisions_enabled:
raise ConanException("The client doesn't have the revisions feature enabled."
" Enable this feature setting to '1' the environment variable"
" 'CONAN_REVISIONS_ENABLED' or the config value"
" 'general.revisions_enabled' in your conan.conf file")
pref = PackageReference.loads(reference, validate=True)
if not pref.ref.revision:
raise ConanException("Specify a recipe reference with revision")
if pref.revision:
raise ConanException("Cannot list the revisions of a specific package revision")
if not remote_name:
layout = self.app.cache.package_layout(pref.ref)
try:
rev = layout.package_revision(pref)
except (RecipeNotFoundException, PackageNotFoundException) as e:
e.print_rev = True
raise e
# Check the time in the associated remote if any
remote_name = layout.load_metadata().recipe.remote
remote = self.app.cache.registry.load_remotes()[remote_name] if remote_name else None
rev_time = None
if remote:
try:
revisions = self.app.remote_manager.get_package_revisions(pref, remote)
except RecipeNotFoundException:
pass
except (NoRestV2Available, NotFoundException):
rev_time = None
else:
tmp = {r["revision"]: r["time"] for r in revisions}
rev_time = tmp.get(rev)
return [{"revision": rev, "time": rev_time}]
else:
remote = self.get_remote_by_name(remote_name)
return self.app.remote_manager.get_package_revisions(pref, remote=remote)
@api_method
def editable_add(self, path, reference, layout, output_folder, cwd):
# Retrieve conanfile.py from target_path
target_path = _get_conanfile_path(path=path, cwd=cwd, py=True)
self.app.load_remotes()
# Check the conanfile is there, and name/version matches
ref = ConanFileReference.loads(reference, validate=True)
target_conanfile = self.app.loader.load_basic(target_path)
if (target_conanfile.name and target_conanfile.name != ref.name) or \
(target_conanfile.version and target_conanfile.version != ref.version):
raise ConanException("Name and version from reference ({}) and target "
"conanfile.py ({}/{}) must match".
format(ref, target_conanfile.name, target_conanfile.version))
layout_abs_path = get_editable_abs_path(layout, cwd, self.app.cache.cache_folder)
if layout_abs_path:
self.app.out.success("Using layout file: %s" % layout_abs_path)
if output_folder is not None:
build_folder = _make_abs_path(output_folder)
self.app.cache.editable_packages.add(ref, target_path, layout_abs_path, output_folder)
@api_method
def editable_remove(self, reference):
ref = ConanFileReference.loads(reference, validate=True)
return self.app.cache.editable_packages.remove(ref)
@api_method
def editable_list(self):
return {str(k): v for k, v in self.app.cache.editable_packages.edited_refs.items()}
@api_method
def lock_update(self, old_lockfile, new_lockfile, cwd=None):
cwd = cwd or os.getcwd()
old_lockfile = _make_abs_path(old_lockfile, cwd)
revisions_enabled = self.app.config.revisions_enabled
old_lock = GraphLockFile.load(old_lockfile, revisions_enabled)
new_lockfile = _make_abs_path(new_lockfile, cwd)
new_lock = GraphLockFile.load(new_lockfile, revisions_enabled)
if old_lock.profile_host is None or new_lock.profile_host is None:
raise ConanException("Lockfiles with --base do not contain profile information, "
"cannot be used. Create a full lockfile")
if old_lock.profile_host.dumps() != new_lock.profile_host.dumps():
raise ConanException("Profiles of lockfiles are different\n%s:\n%s\n%s:\n%s"
% (old_lockfile, old_lock.profile_host.dumps(),
new_lockfile, new_lock.profile_host.dumps()))
old_lock.graph_lock.update_lock(new_lock.graph_lock)
old_lock.save(old_lockfile)
@api_method
def lock_build_order(self, lockfile, cwd=None):
cwd = cwd or os.getcwd()
lockfile = _make_abs_path(lockfile, cwd)
graph_lock_file = GraphLockFile.load(lockfile, self.app.cache.config.revisions_enabled)
if graph_lock_file.profile_host is None:
raise ConanException("Lockfiles with --base do not contain profile information, "
"cannot be used. Create a full lockfile")
graph_lock = graph_lock_file.graph_lock
build_order = graph_lock.build_order()
return build_order
@api_method
def lock_clean_modified(self, lockfile, cwd=None):
cwd = cwd or os.getcwd()
lockfile = _make_abs_path(lockfile, cwd)
graph_lock_file = GraphLockFile.load(lockfile, self.app.cache.config.revisions_enabled)
graph_lock = graph_lock_file.graph_lock
graph_lock.clean_modified()
graph_lock_file.save(lockfile)
@api_method
def lock_install(self, lockfile, remote_name=None, build=None,
generators=None, install_folder=None, cwd=None,
lockfile_out=None, recipes=None):
lockfile = _make_abs_path(lockfile, cwd) if lockfile else None
graph_info = get_graph_info(None, None, cwd, None,
self.app.cache, self.app.out, lockfile=lockfile)
if not generators: # We don't want the default txt
generators = False
install_folder = _make_abs_path(install_folder, cwd)
mkdir(install_folder)
remotes = self.app.load_remotes(remote_name=remote_name)
recorder = ActionRecorder()
graph_lock = graph_info.graph_lock
root_id = graph_lock.root_node_id()
reference = graph_lock.nodes[root_id].ref
if reference is None:
reference = graph_lock.nodes[root_id].path
if recipes:
graph = self.app.graph_manager.load_graph(reference, create_reference=None,
graph_info=graph_info, build_mode=None,
check_updates=False, update=None,
remotes=remotes, recorder=recorder,
lockfile_node_id=root_id)
print_graph(graph, self.app.out)
else:
deps_install(self.app, ref_or_path=reference, install_folder=install_folder,
base_folder=cwd,
remotes=remotes, graph_info=graph_info, build_modes=build,
generators=generators, recorder=recorder, lockfile_node_id=root_id)
if lockfile_out:
lockfile_out = _make_abs_path(lockfile_out, cwd)
graph_lock_file = GraphLockFile(graph_info.profile_host, graph_info.profile_build,
graph_info.graph_lock)
graph_lock_file.save(lockfile_out)
@api_method
def lock_bundle_create(self, lockfiles, lockfile_out, cwd=None):
cwd = cwd or os.getcwd()
result = LockBundle.create(lockfiles, self.app.cache.config.revisions_enabled, cwd)
lockfile_out = _make_abs_path(lockfile_out, cwd)
save(lockfile_out, result.dumps())
@api_method
def lock_bundle_build_order(self, lockfile, cwd=None):
cwd = cwd or os.getcwd()
lockfile = _make_abs_path(lockfile, cwd)
lock_bundle = LockBundle()
lock_bundle.loads(load(lockfile))
build_order = lock_bundle.build_order()
return build_order
@api_method
def lock_bundle_update(self, lock_bundle_path, cwd=None):
cwd = cwd or os.getcwd()
lock_bundle_path = _make_abs_path(lock_bundle_path, cwd)
revisions_enabled = self.app.cache.config.revisions_enabled
LockBundle.update_bundle(lock_bundle_path, revisions_enabled)
@api_method
def lock_bundle_clean_modified(self, lock_bundle_path, cwd=None):
cwd = cwd or os.getcwd()
lock_bundle_path = _make_abs_path(lock_bundle_path, cwd)
revisions_enabled = self.app.cache.config.revisions_enabled
LockBundle.clean_modified(lock_bundle_path, revisions_enabled)
@api_method
def lock_create(self, path, lockfile_out,
reference=None, name=None, version=None, user=None, channel=None,
profile_host=None, profile_build=None, remote_name=None, update=None, build=None,
base=None, lockfile=None):
# profile_host is mandatory
profile_host = profile_host or ProfileData(None, None, None, None, None)
profile_build = profile_build or ProfileData(None, None, None, None, None)
cwd = os.getcwd()
if path and reference:
raise ConanException("Both path and reference arguments were provided. Please provide "
"only one of them")
if path:
ref_or_path = _make_abs_path(path, cwd)
if os.path.isdir(ref_or_path):
raise ConanException("Path argument must include filename "
"like 'conanfile.py' or 'path/conanfile.py'")
if not os.path.isfile(ref_or_path):
raise ConanException("Conanfile does not exist in %s" % ref_or_path)
else: # reference
ref_or_path = ConanFileReference.loads(reference)
phost = pbuild = graph_lock = None
if lockfile:
lockfile = _make_abs_path(lockfile, cwd)
graph_lock_file = GraphLockFile.load(lockfile, self.app.cache.config.revisions_enabled)
phost = graph_lock_file.profile_host
pbuild = graph_lock_file.profile_build
graph_lock = graph_lock_file.graph_lock
graph_lock.relax()
if not phost:
phost = profile_from_args(profile_host.profiles, profile_host.settings,
profile_host.options, profile_host.env, profile_host.conf,
cwd, self.app.cache)
if not pbuild:
# Only work on the profile_build if something is provided
pbuild = profile_from_args(profile_build.profiles, profile_build.settings,
profile_build.options, profile_build.env, profile_build.conf,
cwd, self.app.cache, build_profile=True)
root_ref = ConanFileReference(name, version, user, channel, validate=False)
phost.process_settings(self.app.cache)
if pbuild:
pbuild.process_settings(self.app.cache)
graph_info = GraphInfo(profile_host=phost, profile_build=pbuild, root_ref=root_ref)
graph_info.graph_lock = graph_lock
recorder = ActionRecorder()
# FIXME: Using update as check_update?
remotes = self.app.load_remotes(remote_name=remote_name, check_updates=update)
deps_graph = self.app.graph_manager.load_graph(ref_or_path, None, graph_info, build, update,
update, remotes, recorder)
print_graph(deps_graph, self.app.out)
# The computed graph-lock by the graph expansion
graph_lock = graph_info.graph_lock
# Pure graph_lock, no more graph_info mess
graph_lock_file = GraphLockFile(phost, pbuild, graph_lock)
if lockfile:
new_graph_lock = GraphLock(deps_graph, self.app.config.revisions_enabled)
graph_lock_file = GraphLockFile(phost, pbuild, new_graph_lock)
if base:
graph_lock_file.only_recipes()
lockfile_out = _make_abs_path(lockfile_out or "conan.lock")
graph_lock_file.save(lockfile_out)
self.app.out.info("Generated lockfile: %s" % lockfile_out)
Conan = ConanAPIV1
def get_graph_info(profile_host, profile_build, cwd, install_folder, cache, output,
name=None, version=None, user=None, channel=None, lockfile=None):
if lockfile:
try:
graph_info_folder = lockfile if os.path.isdir(lockfile) else os.path.dirname(lockfile)
graph_info = GraphInfo.load(graph_info_folder)
if name or version or user or channel:
root_ref = ConanFileReference(name, version, user, channel, validate=False)
graph_info.root = root_ref
except IOError: # Only if file is missing
graph_info = GraphInfo()
root_ref = ConanFileReference(name, version, user, channel, validate=False)
graph_info.root = root_ref
lockfile = lockfile if os.path.isfile(lockfile) else os.path.join(lockfile, LOCKFILE)
graph_lock_file = GraphLockFile.load(lockfile, cache.config.revisions_enabled)
graph_info.profile_host = graph_lock_file.profile_host
graph_info.profile_build = graph_lock_file.profile_build
if graph_info.profile_host is None:
raise ConanException("Lockfiles with --base do not contain profile information, "
"cannot be used. Create a full lockfile")
graph_info.profile_host.process_settings(cache, preprocess=False)
if graph_info.profile_build is not None:
graph_info.profile_build.process_settings(cache, preprocess=False)
graph_info.graph_lock = graph_lock_file.graph_lock
output.info("Using lockfile: '{}'".format(lockfile))
return graph_info
try:
graph_info = GraphInfo.load(install_folder)
except IOError: # Only if file is missing
if install_folder:
raise ConanException("Failed to load graphinfo file in install-folder: %s"
% install_folder)
graph_info = None
else:
lockfilename = os.path.join(install_folder, LOCKFILE)
graph_lock_file = GraphLockFile.load(lockfilename, cache.config.revisions_enabled)
graph_info.profile_host = graph_lock_file.profile_host
graph_info.profile_host.process_settings(cache, preprocess=False)
if profile_host or profile_build or not graph_info:
if graph_info:
# FIXME: Convert to Exception in Conan 2.0
output.warn("Settings, options, env or profile specified. "
"GraphInfo found from previous install won't be used: %s\n"
"Don't pass settings, options or profile arguments if you want to reuse "
"the installed graph-info file."
% install_folder)
phost = profile_from_args(profile_host.profiles, profile_host.settings, profile_host.options,
profile_host.env, profile_host.conf, cwd, cache)
phost.process_settings(cache)
profile_build = profile_build or ProfileData(None, None, None, None, None)
# Only work on the profile_build if something is provided
pbuild = profile_from_args(profile_build.profiles, profile_build.settings,
profile_build.options, profile_build.env, profile_build.conf,
cwd, cache, build_profile=True)
if pbuild is not None:
pbuild.process_settings(cache)
root_ref = ConanFileReference(name, version, user, channel, validate=False)
graph_info = GraphInfo(profile_host=phost, profile_build=pbuild, root_ref=root_ref)
# Preprocess settings and convert to real settings
# Apply the new_config to the profiles the global one, so recipes get it too
# TODO: This means lockfiles contain whole copy of the config here?
# FIXME: Apply to locked graph-info as well
graph_info.profile_host.conf.rebase_conf_definition(cache.new_config)
if graph_info.profile_build is not None:
graph_info.profile_build.conf.rebase_conf_definition(cache.new_config)
return graph_info
def _parse_manifests_arguments(verify, manifests, manifests_interactive, cwd):
if manifests and manifests_interactive:
raise ConanException("Do not specify both manifests and "
"manifests-interactive arguments")
if verify and (manifests or manifests_interactive):
raise ConanException("Do not specify both 'verify' and "
"'manifests' or 'manifests-interactive' arguments")
manifest_folder = verify or manifests or manifests_interactive
if manifest_folder:
if not os.path.isabs(manifest_folder):
if not cwd:
raise ConanException("'cwd' should be defined if the manifest folder is relative.")
manifest_folder = os.path.join(cwd, manifest_folder)
manifest_verify = verify is not None
manifest_interactive = manifests_interactive is not None
else:
manifest_verify = manifest_interactive = False
return manifest_folder, manifest_interactive, manifest_verify
def existing_info_files(folder):
return os.path.exists(os.path.join(folder, CONANINFO)) and \
os.path.exists(os.path.join(folder, BUILD_INFO))