Skip to content

Commit e133080

Browse files
authored
Merge branch 'develop2' into refactor/hook_manager_api
2 parents 44def53 + 0fd242b commit e133080

File tree

9 files changed

+181
-15
lines changed

9 files changed

+181
-15
lines changed

conan/api/conan_api.py

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class ConanAPI:
3232
not be created directly.
3333
"""
3434
def __init__(self, cache_folder=None):
35+
"""
36+
:param cache_folder: Conan cache/home folder. It will have less priority than the
37+
"home_folder" defined in a Workspace.
38+
"""
3539

3640
version = sys.version_info
3741
if version.major == 2 or version.minor < 6:

conan/internal/conan_app.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
22

3+
from conan.internal.api.local.editable import EditablePackages
34
from conan.internal.cache.cache import PkgCache
45
from conan.internal.cache.home_paths import HomePaths
6+
from conan.internal.model.conf import ConfDefinition
57
from conans.client.graph.proxy import ConanProxy
68
from conans.client.graph.python_requires import PyRequireLoader
79
from conans.client.graph.range_resolver import RangeResolver
@@ -69,3 +71,22 @@ def __init__(self, conan_api):
6971
conanfile_helpers = ConanFileHelpers(conan_api.remotes.requester, cmd_wrap, self.global_conf,
7072
self.cache, self.cache_folder)
7173
self.loader = ConanFileLoader(self.pyreq_loader, conanfile_helpers)
74+
75+
76+
class LocalRecipesIndexApp:
77+
"""
78+
Simplified one, without full API, for the LocalRecipesIndex. Only publicly used fields are:
79+
- cache
80+
- loader (for the export phase of local-recipes-index)
81+
The others are internally use by other collaborators
82+
"""
83+
def __init__(self, cache_folder):
84+
self.global_conf = ConfDefinition()
85+
self.cache = PkgCache(cache_folder, self.global_conf)
86+
self.remote_manager = RemoteManager(self.cache, auth_manager=None, home_folder=cache_folder)
87+
editable_packages = EditablePackages()
88+
self.proxy = ConanProxy(self, editable_packages)
89+
self.range_resolver = RangeResolver(self, self.global_conf, editable_packages)
90+
pyreq_loader = PyRequireLoader(self, self.global_conf)
91+
helpers = ConanFileHelpers(None, CmdWrapper(""), self.global_conf, self.cache, cache_folder)
92+
self.loader = ConanFileLoader(pyreq_loader, helpers)

conan/tools/env/environment.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def dumps(self):
8989
result.append("{}=!".format(self._name))
9090
elif _EnvVarPlaceHolder in self._values:
9191
index = self._values.index(_EnvVarPlaceHolder)
92-
for v in self._values[:index]:
92+
for v in reversed(self._values[:index]): # Reverse to prepend
9393
result.append("{}=+{}{}{}".format(self._name, path, sep, v))
9494
for v in self._values[index+1:]:
9595
result.append("{}+={}{}{}".format(self._name, path, sep, v))

conans/client/graph/graph_builder.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ def _conflicting_version(require, node,
137137
version_range = require.version_range
138138
prev_version_range = prev_require.version_range if prev_node is None else None
139139
if version_range:
140-
# TODO: Check user/channel conflicts first
140+
if require.ref.user != prev_require.ref.user or \
141+
require.ref.channel != prev_require.ref.channel:
142+
raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
141143
if prev_version_range is not None:
142144
# It it is not conflicting, but range can be incompatible, restrict range
143145
restricted_version_range = version_range.intersection(prev_version_range)
@@ -149,9 +151,10 @@ def _conflicting_version(require, node,
149151
require.ref = prev_ref
150152
else:
151153
raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
152-
153154
elif prev_version_range is not None:
154-
# TODO: Check user/channel conflicts first
155+
if require.ref.user != prev_require.ref.user or \
156+
require.ref.channel != prev_require.ref.channel:
157+
raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
155158
if not prev_version_range.contains(require.ref.version, resolve_prereleases):
156159
raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
157160
else:

conans/client/rest_client_local_recipe_index.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,8 @@ def __init__(self, remote, home_folder):
6464
local_recipes_index_path = os.path.join(local_recipes_index_path, remote.name, ".conan")
6565
repo_folder = self._remote.url
6666

67-
from conan.internal.conan_app import ConanApp
68-
from conan.api.conan_api import ConanAPI
69-
conan_api = ConanAPI(local_recipes_index_path)
70-
self._app = ConanApp(conan_api)
67+
from conan.internal.conan_app import LocalRecipesIndexApp
68+
self._app = LocalRecipesIndexApp(local_recipes_index_path)
7169
self._hook_manager = HookManager(HomePaths(local_recipes_index_path).hooks_path)
7270
self._layout = _LocalRecipesIndexLayout(repo_folder)
7371

test/functional/toolchains/cmake/test_cmake_toolchain_win_clang.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,14 @@ def test_clang_visual_studio_generator(self, client):
150150
""" This is using the embedded ClangCL compiler, not the external one"""
151151
generator = "Visual Studio 17"
152152
client.run("create . --name=pkg --version=0.1 -pr=clang -s compiler.runtime=dynamic "
153-
"-s compiler.cppstd=17 -s compiler.runtime_version=v143 "
153+
"-s compiler.cppstd=17 -s compiler.runtime_version=v144 "
154154
'-c tools.cmake.cmaketoolchain:generator="{}"'.format(generator))
155155
assert 'cmake -G "{}"'.format(generator) in client.out
156156
assert "MSVC-like command-line" in client.out
157-
# My local is 17, but CI ClangCL still 16
158-
assert "main __clang_major__18" in client.out
157+
assert "main __clang_major__19" in client.out
159158
# Check this! Clang compiler in Windows is reporting MSC_VER and MSVC_LANG!
160-
assert "main _MSC_VER1938" in client.out
159+
# CI forced the installation of 19.38, seems to prevail there
160+
assert "main _MSC_VER193" in client.out
161161
assert "main _MSVC_LANG201703" in client.out
162162
assert "main _M_X64 defined" in client.out
163163
assert "main __x86_64__ defined" in client.out

test/integration/graph/conflict_diamond_test.py

+32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
13
from conan.test.assets.genconanfile import GenConanfile
24
from conan.test.utils.tools import TestClient
35

@@ -68,3 +70,33 @@ def _game_conanfile(version, reverse=False):
6870
c.run("install --requires=engine/1.0 --requires=ai/1.0", assert_error=True)
6971
assert "Conflict between math/1.0.1 and math/1.0 in the graph"
7072
assert "Conflict originates from ai/1.0"
73+
74+
75+
@pytest.mark.parametrize("version_range", [True, False])
76+
def test_conflict_user(version_range):
77+
# https://github.com/conan-io/conan/issues/17875
78+
v = "[^1.0]" if version_range else "1.0"
79+
c = TestClient(light=True)
80+
c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
81+
"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires(f"dep/{v}@user1"),
82+
"app/conanfile.py": GenConanfile("app", "1.0").with_requires(f"pkg/{v}@user1",
83+
f"dep/{v}@user2")})
84+
c.run("create dep --user=user1")
85+
c.run("create dep --user=user2")
86+
c.run("create pkg --user=user1")
87+
c.run("install app", assert_error=True)
88+
assert f"Version conflict: Conflict between dep/{v}@user1 and dep/{v}@user2" in c.out
89+
90+
91+
def test_conflict_user_order():
92+
# https://github.com/conan-io/conan/issues/17875
93+
c = TestClient(light=True)
94+
c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
95+
"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0@user1"),
96+
"app/conanfile.py": GenConanfile("app", "1.0").with_requires("pkg/1.0@user1",
97+
"dep/[>=1.0]@user2")})
98+
c.run("create dep --user=user1")
99+
c.run("create dep --user=user2")
100+
c.run("create pkg --user=user1")
101+
c.run("install app", assert_error=True)
102+
assert "ERROR: Version conflict: Conflict between dep/1.0@user1 and dep/[>=1.0]@user2" in c.out

test/integration/workspace/test_workspace.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from conan.test.utils.scm import create_local_git_repo
1010
from conan.test.utils.test_files import temp_folder
1111
from conan.test.utils.tools import TestClient
12-
from conans.util.files import save
12+
from conans.util.files import save, save_files
1313

1414
WorkspaceAPI.TEST_ENABLED = "will_break_next"
1515

@@ -499,7 +499,6 @@ def root_conanfile(self):
499499
"conanws.py": conanfilews})
500500
c.run("workspace add dep")
501501
c.run("workspace install -of=build")
502-
print(c.out)
503502
files = os.listdir(os.path.join(c.current_folder, "build"))
504503
assert "conandeps.props" in files
505504

@@ -524,3 +523,25 @@ def root_conanfile(self):
524523
c.run("workspace add dep")
525524
c.run("workspace install", assert_error=True)
526525
assert "ERROR: Conanfile in conanws.py shouldn't have 'requires'" in c.out
526+
527+
528+
def test_workspace_with_local_recipes_index():
529+
c3i_folder = temp_folder()
530+
recipes_folder = os.path.join(c3i_folder, "recipes")
531+
zlib_config = textwrap.dedent("""
532+
versions:
533+
"1.2.11":
534+
folder: all
535+
""")
536+
save_files(recipes_folder, {"zlib/config.yml": zlib_config,
537+
"zlib/all/conanfile.py": str(GenConanfile("zlib")),
538+
"zlib/all/conandata.yml": ""})
539+
540+
c = TestClient(light=True)
541+
c.save({"conanws.yml": 'home_folder: "deps"'})
542+
c.run(f'remote add local "{c3i_folder}"')
543+
544+
c.run("list zlib/1.2.11#* -r=local")
545+
assert "zlib/1.2.11" in c.out # It doesn't crash
546+
c.run("list zlib/1.2.11#*")
547+
assert "zlib/1.2.11" not in c.out

test/unittests/tools/env/test_env.py

+88-1
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,24 @@ def test_append(self):
368368
MyPath1+=(path)/my/path1
369369
""")
370370

371+
def test_append_multiple(self):
372+
myprofile = textwrap.dedent("""
373+
# define
374+
MyVar1+=MyValue1
375+
MyVar1+=MyValue2
376+
MyPath1 +=(path)/my/path1
377+
MyPath1 +=(path)/my/path2
378+
""")
379+
380+
env = ProfileEnvironment.loads(myprofile)
381+
text = env.dumps()
382+
assert text == textwrap.dedent("""\
383+
MyVar1+=MyValue1
384+
MyVar1+=MyValue2
385+
MyPath1+=(path)/my/path1
386+
MyPath1+=(path)/my/path2
387+
""")
388+
371389
def test_prepend(self):
372390
myprofile = textwrap.dedent("""
373391
# define
@@ -382,6 +400,24 @@ def test_prepend(self):
382400
MyPath1=+(path)/my/path1
383401
""")
384402

403+
def test_prepend_multiple(self):
404+
myprofile = textwrap.dedent("""
405+
# define
406+
MyVar1=+MyValue1
407+
MyVar1=+MyValue2
408+
MyPath1=+(path)/my/path1
409+
MyPath1=+(path)/my/path2
410+
""")
411+
412+
env = ProfileEnvironment.loads(myprofile)
413+
text = env.dumps()
414+
assert text == textwrap.dedent("""\
415+
MyVar1=+MyValue1
416+
MyVar1=+MyValue2
417+
MyPath1=+(path)/my/path1
418+
MyPath1=+(path)/my/path2
419+
""")
420+
385421
def test_combined(self):
386422
myprofile = textwrap.dedent("""
387423
MyVar1=+MyValue11
@@ -399,22 +435,73 @@ def test_combined(self):
399435
MyPath1+=(path)/my/path12
400436
""")
401437

402-
def test_combined2(self):
438+
def test_combined_multiple(self):
439+
myprofile = textwrap.dedent("""
440+
MyVar1=+MyValue11
441+
MyVar1=+MyValue12
442+
MyVar1+=MyValue13
443+
MyVar1+=MyValue14
444+
MyPath1=+(path)/my/path11
445+
MyPath1=+(path)/my/path12
446+
MyPath1+=(path)/my/path13
447+
MyPath1+=(path)/my/path12
448+
""")
449+
450+
env = ProfileEnvironment.loads(myprofile)
451+
text = env.dumps()
452+
assert text == textwrap.dedent("""\
453+
MyVar1=+MyValue11
454+
MyVar1=+MyValue12
455+
MyVar1+=MyValue13
456+
MyVar1+=MyValue14
457+
MyPath1=+(path)/my/path11
458+
MyPath1=+(path)/my/path12
459+
MyPath1+=(path)/my/path13
460+
MyPath1+=(path)/my/path12
461+
""")
462+
463+
def test_combined_prepend_first(self):
464+
myprofile = textwrap.dedent("""
465+
MyVar1+=MyValue11
466+
MyVar1=+MyValue12
467+
MyPath1+=(path)/my/path11
468+
MyPath1=+(path)/my/path12
469+
""")
470+
471+
env = ProfileEnvironment.loads(myprofile)
472+
text = env.dumps()
473+
# NOTE: This is reversed order compared to origin, prepend always first
474+
assert text == textwrap.dedent("""\
475+
MyVar1=+MyValue12
476+
MyVar1+=MyValue11
477+
MyPath1=+(path)/my/path12
478+
MyPath1+=(path)/my/path11
479+
""")
480+
481+
def test_combined_prepend_first_multiple(self):
403482
myprofile = textwrap.dedent("""
404483
MyVar1+=MyValue11
405484
MyVar1=+MyValue12
485+
MyVar1+=MyValue13
486+
MyVar1=+MyValue14
406487
MyPath1+=(path)/my/path11
407488
MyPath1=+(path)/my/path12
489+
MyPath1+=(path)/my/path13
490+
MyPath1=+(path)/my/path14
408491
""")
409492

410493
env = ProfileEnvironment.loads(myprofile)
411494
text = env.dumps()
412495
# NOTE: This is reversed order compared to origin, prepend always first
413496
assert text == textwrap.dedent("""\
414497
MyVar1=+MyValue12
498+
MyVar1=+MyValue14
415499
MyVar1+=MyValue11
500+
MyVar1+=MyValue13
416501
MyPath1=+(path)/my/path12
502+
MyPath1=+(path)/my/path14
417503
MyPath1+=(path)/my/path11
504+
MyPath1+=(path)/my/path13
418505
""")
419506

420507

0 commit comments

Comments
 (0)