Skip to content

Commit

Permalink
Merge branch 'release/1.18.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
lasote committed Aug 30, 2019
2 parents d0bec57 + 64c447b commit 7b1096a
Show file tree
Hide file tree
Showing 28 changed files with 562 additions and 95 deletions.
2 changes: 1 addition & 1 deletion conans/__init__.py
Expand Up @@ -19,4 +19,4 @@
SERVER_CAPABILITIES = [COMPLEX_SEARCH_CAPABILITY, REVISIONS] # Server is always with revisions
DEFAULT_REVISION_V1 = "0"

__version__ = '1.18.1'
__version__ = '1.18.2'
2 changes: 2 additions & 0 deletions conans/client/build/msbuild.py
Expand Up @@ -154,6 +154,8 @@ def get_command(self, project_file, props_file_path=None, targets=None, upgrade_

if use_env:
command.append('/p:UseEnv=true')
else:
command.append('/p:UseEnv=false')

if msvc_arch:
command.append('/p:Platform="%s"' % msvc_arch)
Expand Down
2 changes: 1 addition & 1 deletion conans/client/cache/cache.py
Expand Up @@ -75,7 +75,7 @@ def __init__(self, cache_folder, output):

def all_refs(self):
subdirs = list_folder_subdirs(basedir=self._store_folder, level=4)
return [ConanFileReference(*folder.split("/")) for folder in subdirs]
return [ConanFileReference.load_dir_repr(folder) for folder in subdirs]

@property
def store(self):
Expand Down
24 changes: 15 additions & 9 deletions conans/client/graph/graph_manager.py
Expand Up @@ -211,15 +211,21 @@ def _get_recipe_build_requires(conanfile):

return conanfile.build_requires

def _recurse_build_requires(self, graph, builder, binaries_analyzer, check_updates, update,
build_mode, remotes, profile_build_requires, recorder,
def _recurse_build_requires(self, graph, subgraph, builder, binaries_analyzer, check_updates,
update, build_mode, remotes, profile_build_requires, recorder,
processed_profile, graph_lock, apply_build_requires=True):
"""
:param graph: This is the full dependency graph with all nodes from all recursions
:param subgraph: A partial graph of the nodes that need to be evaluated and expanded
at this recursion. Only the nodes belonging to this subgraph will get their package_id
computed, and they will resolve build_requires if they need to be built from sources
"""

binaries_analyzer.evaluate_graph(graph, build_mode, update, remotes)
binaries_analyzer.evaluate_graph(subgraph, build_mode, update, remotes)
if not apply_build_requires:
return

for node in graph.ordered_iterate():
for node in subgraph.ordered_iterate():
# Virtual conanfiles doesn't have output, but conanfile.py and conanfile.txt do
# FIXME: To be improved and build a explicit model for this
if node.recipe == RECIPE_VIRTUAL:
Expand Down Expand Up @@ -249,8 +255,8 @@ def _recurse_build_requires(self, graph, builder, binaries_analyzer, check_updat
package_build_requires.values(),
check_updates, update, remotes,
processed_profile, graph_lock)
self._recurse_build_requires(subgraph, builder, binaries_analyzer, check_updates,
update, build_mode,
self._recurse_build_requires(graph, subgraph, builder, binaries_analyzer,
check_updates, update, build_mode,
remotes, profile_build_requires, recorder,
processed_profile, graph_lock)
graph.nodes.update(subgraph.nodes)
Expand All @@ -259,8 +265,8 @@ def _recurse_build_requires(self, graph, builder, binaries_analyzer, check_updat
subgraph = builder.extend_build_requires(graph, node, new_profile_build_requires,
check_updates, update, remotes,
processed_profile, graph_lock)
self._recurse_build_requires(subgraph, builder, binaries_analyzer, check_updates,
update, build_mode,
self._recurse_build_requires(graph, subgraph, builder, binaries_analyzer,
check_updates, update, build_mode,
remotes, {}, recorder,
processed_profile, graph_lock)
graph.nodes.update(subgraph.nodes)
Expand All @@ -277,7 +283,7 @@ def _load_graph(self, root_node, check_updates, update, build_mode, remotes,
binaries_analyzer = GraphBinariesAnalyzer(self._cache, self._output,
self._remote_manager)

self._recurse_build_requires(graph, builder, binaries_analyzer, check_updates, update,
self._recurse_build_requires(graph, graph, builder, binaries_analyzer, check_updates, update,
build_mode, remotes,
profile_build_requires, recorder, processed_profile,
graph_lock,
Expand Down
23 changes: 10 additions & 13 deletions conans/client/graph/range_resolver.py
Expand Up @@ -143,27 +143,24 @@ def _resolve_local(self, search_ref, version_range):
if local_found:
return self._resolve_version(version_range, local_found)

def _search_remotes(self, pattern, remotes):
remote = remotes.selected
if remote:
search_result = self._remote_manager.search_recipes(remote, pattern, ignorecase=False)
return search_result, remote.name

def _search_remotes(self, search_ref, remotes):
for remote in remotes.values():
search_result = self._remote_manager.search_recipes(remote, pattern, ignorecase=False)
if search_result:
return search_result, remote.name
if not remotes.selected or remote == remotes.selected:
search_result = self._remote_manager.search_recipes(remote, search_ref.name,
ignorecase=False)
search_result = [ref for ref in search_result
if ref.user == search_ref.user and
ref.channel == search_ref.channel]
if search_result:
return search_result, remote.name
return None, None

def _resolve_remote(self, search_ref, version_range, remotes):
# We should use ignorecase=False, we want the exact case!
found_refs, remote_name = self._cached_remote_found.get(search_ref, (None, None))
if found_refs is None:
# Searching for just the name is much faster in remotes like Artifactory
found_refs, remote_name = self._search_remotes(search_ref.name, remotes)
if found_refs:
found_refs = [r for r in found_refs
if r.user == search_ref.user and r.channel == search_ref.channel]
found_refs, remote_name = self._search_remotes(search_ref, remotes)
if found_refs:
self._result.append("%s versions found in '%s' remote" % (search_ref, remote_name))
else:
Expand Down
1 change: 1 addition & 0 deletions conans/client/migrations_settings.py
Expand Up @@ -625,3 +625,4 @@

settings_1_18_0 = settings_1_17_2
settings_1_18_1 = settings_1_18_0
settings_1_18_2 = settings_1_18_1
17 changes: 9 additions & 8 deletions conans/client/rest/client_routes.py
Expand Up @@ -6,15 +6,15 @@


def _format_ref(url, ref):
url = url.format(name=ref.name, version=ref.version, username=ref.user,
channel=ref.channel, revision=ref.revision)
url = url.format(name=ref.name, version=ref.version, username=ref.user or "_",
channel=ref.channel or "_", revision=ref.revision)
return url


def _format_pref(url, pref):
ref = pref.ref
url = url.format(name=ref.name, version=ref.version, username=ref.user,
channel=ref.channel, revision=ref.revision, package_id=pref.id,
url = url.format(name=ref.name, version=ref.version, username=ref.user or "_",
channel=ref.channel or "_", revision=ref.revision, package_id=pref.id,
p_revision=pref.revision)
return url

Expand Down Expand Up @@ -241,12 +241,13 @@ def _for_package_root(pref):

@staticmethod
def _format_ref_path(url, ref, path):
return url.format(name=ref.name, version=ref.version, username=ref.user,
channel=ref.channel, revision=ref.revision, path=path)
ret = url.format(name=ref.name, version=ref.version, username=ref.user or "_",
channel=ref.channel or "_", revision=ref.revision, path=path)
return ret

@staticmethod
def _format_pref_path(url, pref, path):
ref = pref.ref
return url.format(name=ref.name, version=ref.version, username=ref.user,
channel=ref.channel, revision=ref.revision, package_id=pref.id,
return url.format(name=ref.name, version=ref.version, username=ref.user or "_",
channel=ref.channel or "_", revision=ref.revision, package_id=pref.id,
p_revision=pref.revision, path=path)
8 changes: 7 additions & 1 deletion conans/client/rest/rest_client.py
Expand Up @@ -4,6 +4,7 @@
from conans.client.rest.rest_client_v1 import RestV1Methods
from conans.client.rest.rest_client_v2 import RestV2Methods
from conans.errors import OnlyV2Available
from conans.util.log import logger


class RestApiClient(object):
Expand Down Expand Up @@ -33,6 +34,7 @@ def _get_api(self):
self.requester, self.verify_ssl, self._put_headers)
_, _, cap = tmp.server_info()
self._cached_capabilities[self.remote_url] = cap
logger.debug("REST: Cached capabilities for the remote: %s" % cap)
if not self._revisions_enabled and ONLY_V2 in cap:
raise OnlyV2Available(self.remote_url)

Expand Down Expand Up @@ -83,7 +85,11 @@ def upload_package(self, pref, files_to_upload, deleted, retry, retry_wait):
return self._get_api().upload_package(pref, files_to_upload, deleted, retry, retry_wait)

def authenticate(self, user, password):
return self._get_api().authenticate(user, password)
apiv1 = RestV1Methods(self.remote_url, self.token, self.custom_headers, self._output,
self.requester, self.verify_ssl, self._put_headers)
# Use v1 for the auth because the "ping" could be also protected so we don't know if
# we can use v2
return apiv1.authenticate(user, password)

def check_credentials(self):
return self._get_api().check_credentials()
Expand Down
4 changes: 2 additions & 2 deletions conans/client/rest/rest_client_common.py
Expand Up @@ -111,9 +111,9 @@ def server_info(self):

ret = self.requester.get(url, auth=self.auth, headers=self.custom_headers,
verify=self.verify_ssl)
if ret.status_code == 404:
raise NotFoundException("Not implemented endpoint")

if not ret.ok:
raise get_exception_from_error(ret.status_code)("")
version_check = ret.headers.get('X-Conan-Client-Version-Check', None)
server_version = ret.headers.get('X-Conan-Server-Version', None)
server_capabilities = ret.headers.get('X-Conan-Server-Capabilities', "")
Expand Down
8 changes: 6 additions & 2 deletions conans/model/ref.py
Expand Up @@ -21,7 +21,7 @@ def _split_pair(pair, split_char):


def _noneize(text):
if not text or text == "None":
if not text or text == "_":
return None
return text

Expand Down Expand Up @@ -158,6 +158,9 @@ def __new__(cls, name, version, user, channel, revision=None, validate=True):
raise InvalidNameException("Specify the 'user' and the 'channel' or neither of them")

version = Version(version) if version is not None else None
user = _noneize(user)
channel = _noneize(channel)

obj = super(cls, ConanFileReference).__new__(cls, name, version, user, channel, revision)
if validate:
obj._validate()
Expand Down Expand Up @@ -207,7 +210,8 @@ def __str__(self):

def __repr__(self):
str_rev = "#%s" % self.revision if self.revision else ""
return "%s/%s@%s/%s%s" % (self.name, self.version, self.user, self.channel, str_rev)
return "%s/%s@%s/%s%s" % (self.name, self.version, self.user or "_", self.channel or "_",
str_rev)

def full_str(self):
str_rev = "#%s" % self.revision if self.revision else ""
Expand Down
96 changes: 69 additions & 27 deletions conans/model/scm.py
@@ -1,34 +1,51 @@
import json
import os
import subprocess

from six import string_types

from conans.client.tools.scm import Git, SVN
from conans.errors import ConanException
from conans.util.files import rmdir


def get_scm_data(conanfile):
try:
data = getattr(conanfile, "scm", None)
if data is not None and isinstance(data, dict):
return SCMData(conanfile)
except ConanException:
else:
return None


def _get_dict_value(data, key, expected_type, default=None, disallowed_type=None):
if key in data:
r = data.get(key)
if r is None: # None is always a valid value
return r
if not isinstance(r, expected_type) or (disallowed_type and isinstance(r, disallowed_type)):
type_str = "' or '".join([it.__name__ for it in expected_type]) \
if isinstance(expected_type, tuple) else expected_type.__name__
raise ConanException("SCM value for '{}' must be of type '{}'"
" (found '{}')".format(key, type_str, type(r).__name__))
return r
return default


class SCMData(object):
VERIFY_SSL_DEFAULT = True
SHALLOW_DEFAULT = True

def __init__(self, conanfile):
data = getattr(conanfile, "scm", None)
if data is not None and isinstance(data, dict):
self.type = data.get("type")
self.url = data.get("url")
self.revision = data.get("revision")
self.verify_ssl = data.get("verify_ssl")
self.username = data.get("username")
self.password = data.get("password")
self.subfolder = data.get("subfolder")
self.submodule = data.get("submodule")
else:
raise ConanException("Not SCM enabled in conanfile")
data = getattr(conanfile, "scm")
self.type = _get_dict_value(data, "type", string_types)
self.url = _get_dict_value(data, "url", string_types)
self.revision = _get_dict_value(data, "revision", string_types + (int, ),
disallowed_type=bool) # bool is subclass of integer
self.verify_ssl = _get_dict_value(data, "verify_ssl", bool, SCMData.VERIFY_SSL_DEFAULT)
self.username = _get_dict_value(data, "username", string_types)
self.password = _get_dict_value(data, "password", string_types)
self.subfolder = _get_dict_value(data, "subfolder", string_types)
self.submodule = _get_dict_value(data, "submodule", string_types)
self.shallow = _get_dict_value(data, "shallow", bool, SCMData.SHALLOW_DEFAULT)

@property
def capture_origin(self):
Expand All @@ -46,10 +63,22 @@ def recipe_revision(self):

def __repr__(self):
d = {"url": self.url, "revision": self.revision, "username": self.username,
"password": self.password, "type": self.type, "verify_ssl": self.verify_ssl,
"password": self.password, "type": self.type,
"subfolder": self.subfolder, "submodule": self.submodule}
if self.shallow != self.SHALLOW_DEFAULT:
d.update({"shallow": self.shallow})
if self.verify_ssl != self.VERIFY_SSL_DEFAULT:
d.update({"verify_ssl": self.verify_ssl})
d = {k: v for k, v in d.items() if v is not None}
return json.dumps(d, sort_keys=True)

def _kv_to_string(key, value):
if isinstance(value, bool):
return '"{}": {}'.format(key, value)
else:
value_str = str(value).replace('"', r'\"')
return '"{}": "{}"'.format(key, value_str)

return '{' + ', '.join([_kv_to_string(k, v) for k, v in sorted(d.items())]) + '}'


class SCM(object):
Expand Down Expand Up @@ -88,18 +117,31 @@ def excluded_files(self):
def checkout(self):
output = ""
if self._data.type == "git":
try:
output += self.repo.clone(url=self._data.url, branch=self._data.revision,
def use_not_shallow():
out = self.repo.clone(url=self._data.url, shallow=False)
out += self.repo.checkout(element=self._data.revision,
submodule=self._data.submodule)
return out

def use_shallow():
try:
out = self.repo.clone(url=self._data.url, branch=self._data.revision,
shallow=True)
except subprocess.CalledProcessError:
# remove the .git directory, otherwise, fallback clone cannot be successful
# it's completely safe to do here, as clone without branch expects empty directory
rmdir(os.path.join(self.repo_folder, ".git"))
output += self.repo.clone(url=self._data.url, shallow=False)
output += self.repo.checkout(element=self._data.revision,
submodule=self._data.submodule)
except subprocess.CalledProcessError:
# remove the .git directory, otherwise, fallback clone cannot be successful
# it's completely safe to do here, as clone without branch expects
# empty directory
rmdir(os.path.join(self.repo_folder, ".git"))
out = use_not_shallow()
else:
out += self.repo.checkout_submodules(submodule=self._data.submodule)
return out

if self._data.shallow:
output += use_shallow()
else:
output += self.repo.checkout_submodules(submodule=self._data.submodule)
output += use_not_shallow()

else:
output += self.repo.checkout(url=self._data.url, revision=self._data.revision)
return output
Expand Down

0 comments on commit 7b1096a

Please sign in to comment.