Repository: conan-io/conan
Pull request #14836 (2023-09-27 18:38:48)

fix error with lock install when lockfile uses a path
Changelog: Bugfix: Solve crash of ``conan lock install`` when the provided lockfile was created from a ``conanfile.txt``
Docs: Omit



Patch:
diff --git a/conan/tools/google/bazeldeps.py b/conan/tools/google/bazeldeps.py
index 4436993de..7c6527f03 100644
--- a/conan/tools/google/bazeldeps.py
+++ b/conan/tools/google/bazeldeps.py
@@ -56,53 +56,53 @@ class BazelDeps(object):
         return filegroup
 
     def _get_dependency_buildfile_content(self, dependency):
-        template = textwrap.dedent("""
+        template = textwrap.dedent("""\
             load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library")
 
-            {% for libname, filepath in libs.items() %}
+            {%- for libname, filepath in libs.items() %}
             cc_import(
                 name = "{{ libname }}_precompiled",
                 {{ library_type }} = "{{ filepath }}",
             )
-            {% endfor %}
+            {%- endfor %}
 
-            {% for libname, (lib_path, dll_path) in shared_with_interface_libs.items() %}
+            {%- for libname, (lib_path, dll_path) in shared_with_interface_libs.items() %}
             cc_import(
                 name = "{{ libname }}_precompiled",
                 interface_library = "{{ lib_path }}",
                 shared_library = "{{ dll_path }}",
             )
-            {% endfor %}
+            {%- endfor %}
 
             cc_library(
                 name = "{{ name }}",
-                {% if headers %}
+                {%- if headers %}
                 hdrs = glob([{{ headers }}]),
-                {% endif %}
-                {% if includes %}
+                {%- endif %}
+                {%- if includes %}
                 includes = [{{ includes }}],
-                {% endif %}
-                {% if defines %}
+                {%- endif %}
+                {%- if defines %}
                 defines = [{{ defines }}],
                 {% endif %}
-                {% if linkopts %}
+                {%- if linkopts %}
                 linkopts = [{{ linkopts }}],
-                {% endif %}
+                {%- endif %}
                 visibility = ["//visibility:public"],
-                {% if libs or shared_with_interface_libs %}
+                {%- if libs or shared_with_interface_libs %}
                 deps = [
                     # do not sort
-                {% for lib in libs %}
-                ":{{ lib }}_precompiled",
-                {% endfor %}
-                {% for lib in shared_with_interface_libs %}
-                ":{{ lib }}_precompiled",
-                {% endfor %}
-                {% for dep in dependencies %}
-                "@{{ dep }}",
-                {% endfor %}
+                    {%- for lib in libs %}
+                    ":{{ lib }}_precompiled",
+                    {%- endfor %}
+                    {%- for lib in shared_with_interface_libs %}
+                    ":{{ lib }}_precompiled",
+                    {%- endfor %}
+                    {%- for dep in dependencies %}
+                    "@{{ dep }}",
+                    {%- endfor %}
                 ],
-                {% endif %}
+                {%- endif %}
             )
 
         """)
diff --git a/conans/client/command.py b/conans/client/command.py
index 5b548b805..0a7036104 100644
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -4,6 +4,7 @@ import json
 import os
 import signal
 import sys
+import textwrap
 from argparse import ArgumentError
 from difflib import get_close_matches
 
@@ -318,6 +319,7 @@ class Command(object):
         that the package has been created correctly. Check 'conan test' command
         to know more about 'test_folder' project.
         """
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.create.__doc__,
                                          prog="conan create",
                                          formatter_class=SmartFormatter)
@@ -469,6 +471,7 @@ class Command(object):
         the package is installed, Conan will write the files for the specified
         generators.
         """
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.install.__doc__,
                                          prog="conan install",
                                          formatter_class=SmartFormatter)
@@ -859,7 +862,7 @@ class Command(object):
         using a build helper, like CMake(), the --package-folder will be
         configured as the destination folder for the install step.
         """
-
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.build.__doc__,
                                          prog="conan build",
                                          formatter_class=SmartFormatter)
@@ -2177,6 +2180,12 @@ class Command(object):
                               front=Color.BRIGHT_RED)
             self._out.writeln("*"*width, front=Color.BRIGHT_RED)
 
+    def _warn_conan_version(self):
+        width = 70
+        self._out.writeln(textwrap.fill("Conan 1 is on a deprecation path, "
+                                        "please consider migrating to Conan 2", width),
+                          front=Color.BRIGHT_YELLOW)
+
     def run(self, *args):
         """HIDDEN: entry point for executing commands, dispatcher to class
         methods
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
index 1c46eabb6..0b42b3952 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -1396,6 +1396,8 @@ class ConanAPIV1(object):
         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,
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 31c194317..88559f7ff 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -92,10 +92,10 @@ _t_default_settings_yml = Template(textwrap.dedent("""
                       "7", "7.1", "7.2", "7.3", "7.4", "7.5",
                       "8", "8.1", "8.2", "8.3", "8.4", "8.5",
                       "9", "9.1", "9.2", "9.3", "9.4", "9.5",
-                      "10", "10.1", "10.2", "10.3", "10.4",
-                      "11", "11.1", "11.2", "11.3",
+                      "10", "10.1", "10.2", "10.3", "10.4", "10.5",
+                      "11", "11.1", "11.2", "11.3", "11.4",
                       "12", "12.1", "12.2", "12.3",
-                      "13", "13.1"]
+                      "13", "13.1", "13.2"]
             libcxx: [libstdc++, libstdc++11]
             threads: [None, posix, win32]  # Windows MinGW
             exception: [None, dwarf2, sjlj, seh]  # Windows MinGW
diff --git a/conans/client/loader.py b/conans/client/loader.py
index 916509364..7eae666bb 100644
--- a/conans/client/loader.py
+++ b/conans/client/loader.py
@@ -1,5 +1,4 @@
 import fnmatch
-import imp
 import inspect
 import os
 import re
@@ -40,6 +39,9 @@ class ConanFileLoader(object):
         sys.modules["conans"].python_requires = python_requires
         self._cached_conanfile_classes = {}
         self._requester = requester
+        if sys.version_info.major >= 3 and sys.version_info.minor >= 12:
+            from importlib import invalidate_caches
+            invalidate_caches()
 
     def load_basic(self, conanfile_path, lock_python_requires=None, user=None, channel=None,
                    display=""):
@@ -439,8 +441,15 @@ def _parse_conanfile(conan_file_path):
             old_dont_write_bytecode = sys.dont_write_bytecode
             try:
                 sys.dont_write_bytecode = True
-                # FIXME: imp is deprecated in favour of implib
-                loaded = imp.load_source(module_id, conan_file_path)
+                # imp is deprecated in favour of importlib, removed in 3.12
+                if sys.version_info.major >= 3 and sys.version_info.minor >= 12:
+                    from importlib import util as imp_util
+                    spec = imp_util.spec_from_file_location(module_id, conan_file_path)
+                    loaded = imp_util.module_from_spec(spec)
+                    spec.loader.exec_module(loaded)
+                else:
+                    import imp
+                    loaded = imp.load_source(module_id, conan_file_path)
                 sys.dont_write_bytecode = old_dont_write_bytecode
             except ImportError:
                 version_txt = _get_required_conan_version_without_loading(conan_file_path)
diff --git a/conans/client/migrations_settings.py b/conans/client/migrations_settings.py
index e9bcc0d48..b110f8f75 100644
--- a/conans/client/migrations_settings.py
+++ b/conans/client/migrations_settings.py
@@ -4658,4 +4658,158 @@ build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel]
 cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]  # Deprecated, use compiler.cppstd
 """
 
-settings_1_62_0 = settings_1_61_0
+settings_1_62_0 = """
+# Only for cross building, 'os_build/arch_build' is the system that runs Conan
+os_build: [Windows, WindowsStore, Linux, Macos, FreeBSD, SunOS, AIX, VxWorks]
+arch_build: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7]
+
+# Only for building cross compilation tools, 'os_target/arch_target' is the system for
+# which the tools generate code
+os_target: [Windows, Linux, Macos, Android, iOS, watchOS, tvOS, FreeBSD, SunOS, AIX, Arduino, Neutrino]
+arch_target: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7, xtensalx6, xtensalx106, xtensalx7]
+
+# Rest of the settings are "host" settings:
+# - For native building/cross building: Where the library/program will run.
+# - For building cross compilation tools: Where the cross compiler will run.
+os:
+    Windows:
+        subsystem: [None, cygwin, msys, msys2, wsl]
+    WindowsStore:
+        version: ["8.1", "10.0"]
+    WindowsCE:
+        platform: ANY
+        version: ["5.0", "6.0", "7.0", "8.0"]
+    Linux:
+    iOS:
+        version: &ios_version
+                 ["7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3",
+                  "11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4",
+                  "13.0", "13.1", "13.2", "13.3", "13.4", "13.5", "13.6", "13.7",
+                  "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7", "14.8",
+                  "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6", "16.0", "16.1"]
+        sdk: [None, "iphoneos", "iphonesimulator"]
+        sdk_version: [None, "11.3", "11.4", "12.0", "12.1", "12.2", "12.4",
+                      "13.0", "13.1", "13.2", "13.4", "13.5", "13.6", "13.7",
+                      "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "15.0", "15.2", "15.4", "15.5", "16.0", "16.1"]
+    watchOS:
+        version: ["4.0", "4.1", "4.2", "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1", "6.2",
+                  "7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "8.0", "8.1", "8.3", "8.4", "8.5", "8.6", "8.7", "9.0", "9.1"]
+        sdk: [None, "watchos", "watchsimulator"]
+        sdk_version: [None, "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1", "6.2",
+                      "7.0", "7.1", "7.2", "7.4", "8.0", "8.0.1", "8.3", "8.5", "9.0", "9.1"]
+    tvOS:
+        version: ["11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4",
+                  "13.0", "13.2", "13.3", "13.4", "14.0", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7",
+                  "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6", "16.0", "16.1"]
+        sdk: [None, "appletvos", "appletvsimulator"]
+        sdk_version: [None, "11.3", "11.4", "12.0", "12.1", "12.2", "12.4",
+                      "13.0", "13.1", "13.2", "13.4", "14.0", "14.2", "14.3", "14.5", "15.0", "15.2", "15.4", "16.0", "16.1"]
+    Macos:
+        version: [None, "10.6", "10.7", "10.8", "10.9", "10.10", "10.11", "10.12", "10.13", "10.14", "10.15", "11.0", "12.0", "13.0"]
+        sdk: [None, "macosx"]
+        sdk_version: [None, "10.13", "10.14", "10.15", "11.0", "11.1", "11.3", "12.0", "12.1", "12.3", "13.0", "13.1"]
+        subsystem:
+            None:
+            catalyst:
+                ios_version: *ios_version
+    Android:
+        api_level: ANY
+    FreeBSD:
+    SunOS:
+    AIX:
+    Arduino:
+        board: ANY
+    Emscripten:
+    Neutrino:
+        version: ["6.4", "6.5", "6.6", "7.0", "7.1"]
+    baremetal:
+    VxWorks:
+        version: ["7"]
+arch: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv4, armv4i, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le, e2k-v2, e2k-v3, e2k-v4, e2k-v5, e2k-v6, e2k-v7, xtensalx6, xtensalx106, xtensalx7]
+compiler:
+    sun-cc:
+        version: ["5.10", "5.11", "5.12", "5.13", "5.14", "5.15"]
+        threads: [None, posix]
+        libcxx: [libCstd, libstdcxx, libstlport, libstdc++]
+    gcc: &gcc
+        version: ["4.1", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9",
+                  "5", "5.1", "5.2", "5.3", "5.4", "5.5",
+                  "6", "6.1", "6.2", "6.3", "6.4", "6.5",
+                  "7", "7.1", "7.2", "7.3", "7.4", "7.5",
+                  "8", "8.1", "8.2", "8.3", "8.4", "8.5",
+                  "9", "9.1", "9.2", "9.3", "9.4", "9.5",
+                  "10", "10.1", "10.2", "10.3", "10.4", "10.5",
+                  "11", "11.1", "11.2", "11.3", "11.4",
+                  "12", "12.1", "12.2", "12.3",
+                  "13", "13.1", "13.2"]
+        libcxx: [libstdc++, libstdc++11]
+        threads: [None, posix, win32]  # Windows MinGW
+        exception: [None, dwarf2, sjlj, seh]  # Windows MinGW
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+    Visual Studio: &visual_studio
+        runtime: [MD, MT, MTd, MDd]
+        version: ["8", "9", "10", "11", "12", "14", "15", "16", "17"]
+        toolset: [None, v90, v100, v110, v110_xp, v120, v120_xp,
+                  v140, v140_xp, v140_clang_c2, LLVM-vs2012, LLVM-vs2012_xp,
+                  LLVM-vs2013, LLVM-vs2013_xp, LLVM-vs2014, LLVM-vs2014_xp,
+                  LLVM-vs2017, LLVM-vs2017_xp, v141, v141_xp, v141_clang_c2, v142,
+                  llvm, ClangCL, v143]
+        cppstd: [None, 14, 17, 20, 23]
+    msvc:
+        version: [170, 180, 190, 191, 192, 193]
+        update: [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+        runtime: [static, dynamic]
+        runtime_type: [Debug, Release]
+        cppstd: [98, 14, 17, 20, 23]
+        toolset: [None, v110_xp, v120_xp, v140_xp, v141_xp]
+    clang:
+        version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0",
+                  "5.0", "6.0", "7.0", "7.1",
+                  "8", "9", "10", "11", "12", "13", "14", "15", "16"]
+        libcxx: [None, libstdc++, libstdc++11, libc++, c++_shared, c++_static]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+        runtime: [None, MD, MT, MTd, MDd, static, dynamic]
+        runtime_type: [None, Debug, Release]
+        runtime_version: [None, v140, v141, v142, v143]
+    apple-clang: &apple_clang
+        version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1",
+                  "10.0", "11.0", "12.0", "13", "13.0", "13.1", "14", "14.0", "15", "15.0"]
+        libcxx: [libstdc++, libc++]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+    intel:
+        version: ["11", "12", "13", "14", "15", "16", "17", "18", "19", "19.1"]
+        update: [None, ANY]
+        base:
+            gcc:
+                <<: *gcc
+                threads: [None]
+                exception: [None]
+            Visual Studio:
+                <<: *visual_studio
+            apple-clang:
+                <<: *apple_clang
+    intel-cc:
+        version: ["2021.1", "2021.2", "2021.3"]
+        update: [None, ANY]
+        mode: ["icx", "classic", "dpcpp"]
+        libcxx: [None, libstdc++, libstdc++11, libc++]
+        cppstd: [None, 98, gnu98, 03, gnu03, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
+        runtime: [None, static, dynamic]
+        runtime_type: [None, Debug, Release]
+    qcc:
+        version: ["4.4", "5.4", "8.3"]
+        libcxx: [cxx, gpp, cpp, cpp-ne, accp, acpp-ne, ecpp, ecpp-ne]
+        cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17]
+    mcst-lcc:
+        version: ["1.19", "1.20", "1.21", "1.22", "1.23", "1.24", "1.25"]
+        base:
+            gcc:
+                <<: *gcc
+                threads: [None]
+                exceptions: [None]
+
+build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel]
+
+
+cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]  # Deprecated, use compiler.cppstd
+"""


Test patch:
diff --git a/conans/test/functional/graph_lock/lock_install_test.py b/conans/test/functional/graph_lock/lock_install_test.py
index b7561387c..6631a9cea 100644
--- a/conans/test/functional/graph_lock/lock_install_test.py
+++ b/conans/test/functional/graph_lock/lock_install_test.py
@@ -48,3 +48,28 @@ def test_install_recipes():
     assert "pkga/0.1@user/channel:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9 - Download" in client.out
     assert "pkgb/0.1@user/channel:cfd10f60aeaa00f5ca1f90b5fe97c3fe19e7ec23 - Download" in client.out
 
+
+def test_install_txt():
+    """ lock install from a lockfile created from a conanfile path needs that conanfile to be
+    in the place it was to create the lockfile, otherwise it fails. So not really a great
+    use case
+    """
+    client = TestClient()
+    client.save({"conanfile.py": GenConanfile("pkga", "0.1").with_package_file("file.h", "0.1")})
+    client.run("create . user/channel")
+
+    # Use a consumer with a version range
+    client.save({"conanfile.py":
+                 GenConanfile("pkgb", "0.1").with_require("pkga/[>=0.1]@user/channel")})
+    client.run("create . user/channel")
+    client.save({"conanfile.txt": "[requires]\npkgb/0.1@user/channel"}, clean_first=True)
+    client.run("lock create conanfile.txt --lockfile-out=lock1.lock")
+
+    # We can create a pkga/0.2, but it will not be used
+    client.save({"conanfile.py": GenConanfile("pkga", "0.2").with_package_file("file.h", "0.2")})
+    client.run("create . user/channel")
+
+    client.run("lock install lock1.lock -g deploy")
+    assert "pkga/0.1@user/channel from local cache" in client.out
+    file_h = client.load("pkga/file.h")
+    assert file_h == "0.1"
diff --git a/conans/test/unittests/tools/google/test_bazeldeps.py b/conans/test/unittests/tools/google/test_bazeldeps.py
index be8a7a71e..7d02e6155 100644
--- a/conans/test/unittests/tools/google/test_bazeldeps.py
+++ b/conans/test/unittests/tools/google/test_bazeldeps.py
@@ -58,7 +58,7 @@ def test_bazeldeps_dependency_buildfiles():
                 assert 'linkopts = ["/DEFAULTLIB:system_lib1"]' in dependency_content
             else:
                 assert 'linkopts = ["-lsystem_lib1"],' in dependency_content
-            assert """deps = [\n        # do not sort\n    \n    ":lib1_precompiled",""" in dependency_content
+            assert """deps = [\n        # do not sort\n        ":lib1_precompiled",""" in dependency_content
 
 
 def test_bazeldeps_get_lib_file_path_by_basename():
@@ -93,7 +93,7 @@ def test_bazeldeps_get_lib_file_path_by_basename():
                 assert 'linkopts = ["/DEFAULTLIB:system_lib1"]' in dependency_content
             else:
                 assert 'linkopts = ["-lsystem_lib1"],' in dependency_content
-            assert 'deps = [\n        # do not sort\n    \n    ":liblib1.a_precompiled",' in dependency_content
+            assert 'deps = [\n        # do not sort\n        ":liblib1.a_precompiled",' in dependency_content
 
 
 def test_bazeldeps_dependency_transitive():