Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] "No such file or directory" when manipulating build_id and package_id #14537

Closed
robert-shade opened this issue Aug 21, 2023 · 11 comments · Fixed by #14555
Closed

[bug] "No such file or directory" when manipulating build_id and package_id #14537

robert-shade opened this issue Aug 21, 2023 · 11 comments · Fixed by #14555
Assignees
Labels
Milestone

Comments

@robert-shade
Copy link

robert-shade commented Aug 21, 2023

Environment details

  • Operating System+version: Linux RHEL 8.8
  • Compiler+version: gcc 8.5
  • Conan version: 2.0.0
  • Python version: 3.9.16

Steps to reproduce

I am packaging some existing binaries from a vendor, so I'm trying to reuse the build_id to avoid running their installer multiple times.

conanfile.py

from conan import ConanFile
from conan.tools.scm import Version

import os
import glob

class BuildIdTestConan(ConanFile):
    settings = "os", "arch", "compiler", "build_type"

    options = {
        "shared": [True, False] }

    default_options = {
        "shared": False, }

    def build_id(self):
        self.info_build.settings.build_type = "Any"
        self.info_build.options.shared = "Any"

        if self.settings.os == "Windows":
            self.info_build.settings.compiler.runtime = "Any"

    def build(self):
        pass

    def package_id(self):
        # Since we only get gcc7 and vs2017 binaries, we re-use
        # the same ones for future versions.
        compilerVer = Version(str(self.info.settings.compiler.version))

        if self.info.settings.compiler == "gcc":
            if compilerVer >= "7":
                self.info.settings.compiler.version = "7+"
        elif str(self.info.settings.compiler) == "Visual Studio":
            if compilerVer >= "15":
                self.info.settings.compiler.version = "15+"
        elif str(self.info.settings.compiler) == "msvc":
            if compilerVer >= "191":
                self.info.settings.compiler.version = "191+"

Command

conan create --name="test_build_id" --version="1.0.0" --user="my" --channel="test" --settings="build_type=Release" --options="test_build_id/*:shared=False" .

Note that this only occurs with a clean cache (${CONAN_HOME}/p/b/ is empty)

Also interesting is that a build folder is created in the cache, just not the one it's looking for (it has a different hash)

Logs

======== Exporting recipe to the cache ========
test_build_id/1.0.0@my/test: Exporting package recipe: /home/rshade/test-build-id/conanfile.py
test_build_id/1.0.0@my/test: Copied 1 '.py' file: conanfile.py
test_build_id/1.0.0@my/test: Exported to cache folder: /home/rshade/.conan2/p/test_8bc4690a4706b/e
test_build_id/1.0.0@my/test: Exported: test_build_id/1.0.0@my/test#d1760f2d4a06d4df9522da13a3caba37 (2023-08-21 20:34:49 UTC)

======== Input profiles ========
Profile host:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=8
os=Linux
[options]
test_build_id/*:shared=False

Profile build:
[settings]
arch=x86_64
build_type=Debug
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=8
os=Linux


======== Computing dependency graph ========
Graph root
    cli
Requirements
    test_build_id/1.0.0@my/test#d1760f2d4a06d4df9522da13a3caba37 - Cache

======== Computing necessary packages ========
test_build_id/1.0.0@my/test: Forced build from source
Requirements
    test_build_id/1.0.0@my/test#d1760f2d4a06d4df9522da13a3caba37:978c5442906f96846ccb201129ba1559071ce4ab - Build

======== Installing packages ========

-------- Installing package test_build_id/1.0.0@my/test (1 of 1) --------
test_build_id/1.0.0@my/test: Building from source
test_build_id/1.0.0@my/test: Package test_build_id/1.0.0@my/test:978c5442906f96846ccb201129ba1559071ce4ab
ERROR: [Errno 2] No such file or directory: '/home/rshade/.conan2/p/b/test_f5b8c2eae5de3/b'
Traceback (most recent call last):
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conan/cli/cli.py", line 272, in main
    cli.run(args)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conan/cli/cli.py", line 172, in run
    command.run(self._conan_api, args[0][1:])
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conan/cli/command.py", line 125, in run
    info = self._method(conan_api, parser, *args)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conan/cli/commands/create.py", line 81, in create
    conan_api.install.install_binaries(deps_graph=deps_graph, remotes=remotes)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conan/api/subapi/install.py", line 21, in install_binaries
    installer.install(deps_graph, remotes)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conans/client/installer.py", line 252, in install
    self._handle_package(package, install_reference, handled_count, package_count)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conans/client/installer.py", line 315, in _handle_package
    self._handle_node_build(package, package_layout)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conans/client/installer.py", line 386, in _handle_node_build
    pref = builder.build_package(node, pkg_layout)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conans/client/installer.py", line 139, in build_package
    with chdir(base_build):
  File "/usr/lib64/python3.9/contextlib.py", line 119, in __enter__
    return next(self.gen)
  File "/home/rshade/.cache/pypoetry/virtualenvs/conantest-XKbvyNKU-py3.9/lib/python3.9/site-packages/conans/util/files.py", line 59, in chdir
    os.chdir(newdir)
FileNotFoundError: [Errno 2] No such file or directory: '/home/rshade/.conan2/p/b/test_f5b8c2eae5de3/b'
@memsharded
Copy link
Member

I am trying to reproduce this, putting it as a test:

def test_build_id_error():
    c = TestClient()
    myconan = textwrap.dedent("""
        from conan import ConanFile
        from conan.tools.scm import Version

        import os
        import glob

        class BuildIdTestConan(ConanFile):
            settings = "os", "arch", "compiler", "build_type"

            options = {"shared": [True, False]}

            default_options = {
                "shared": False, }

            def build_id(self):
                self.info_build.settings.build_type = "Any"
                self.info_build.options.shared = "Any"

                if self.settings.os == "Windows":
                    self.info_build.settings.compiler.runtime = "Any"

            def build(self):
                pass

            def package_id(self):
                # Since we only get gcc7 and vs2017 binaries, we re-use
                # the same ones for future versions.
                compilerVer = Version(str(self.info.settings.compiler.version))

                if self.info.settings.compiler == "gcc":
                    if compilerVer >= "7":
                        self.info.settings.compiler.version = "7+"
                elif str(self.info.settings.compiler) == "Visual Studio":
                    if compilerVer >= "15":
                        self.info.settings.compiler.version = "15+"
                elif str(self.info.settings.compiler) == "msvc":
                    if compilerVer >= "191":
                        self.info.settings.compiler.version = "191+"
        """)
    c.save({"conanfile.py": myconan})
    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 "
          "--settings=build_type=Release --options=test_build_id/*:shared=False")

And it is passing successfully. As an automated test, it guarantees that the cache is completely empty.

Could you please double check it? A couple of hints:

  • Please make sure to use latest 2.0.9.
  • Double check your command, make sure to copy the exact thing (the above pasted one is missing the <path> argument

@robert-shade
Copy link
Author

I'm using 2.0.9. Oops, left that off when I copy/pasted.

Also try the other permutations of Release/Debug and shared. Some of them work and some don't.

@memsharded
Copy link
Member

I am trying adding to the test some more combinations like:

    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=7 -s compiler.libcxx=libstdc++11 "
          "-s build_type=Debug --options=test_build_id/*:shared=False")
    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 "
          "-s build_type=Debug --options=test_build_id/*:shared=False")
    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 "
          "-s build_type=Release --options=test_build_id/*:shared=False")
    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 "
          "-s build_type=Debug --options=test_build_id/*:shared=True")
    c.run("create . --name=test_build_id --version=1.0.0  "
          "-s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 "
          "-s build_type=Release --options=test_build_id/*:shared=True")

Including changing the compiler version, build_type, and shared, but still couldn't make it fail. Not sure what else to try, any further suggestion?

@robert-shade
Copy link
Author

Did some more digging with a default profile. It has something to do with setting the cppstd (I had it set to gnu17 in my profile) and the order of the arguments (?).

This does not make it happen:

conan create . --name=test_build_id --version=1.0.0 -s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 -s build_type=Release --options=test_build_id/*:shared=True -s compiler.cppstd=gnu17 --user="my" --channel="test"

But this does:

conan create . --name="test_build_id" --version="1.0.0" --user="my" --channel="test" --settings="build_type=Release" --options="test_build_id/*:shared=False" -s os=Linux -s compiler=gcc -s compiler.version=8 -s compiler.libcxx=libstdc++11 -s compiler.cppstd=gnu17

@memsharded memsharded added this to the 2.0.10 milestone Aug 22, 2023
@robert-shade
Copy link
Author

It's so strange. I tried to make a test for you, but I can't get it to fail in there. I can consistently do it on the command line :/

@memsharded
Copy link
Member

Are you trying the test in the release/2.0 branch? Maybe something changed and fixed it in the PR already merged but not released after 2.0.9? Maybe it is worth to run the test in the 2.0.9 tag? (I think I did that, but it was still green)

I don't know where the difference could be otherwise, do you have a command line repro, like the above conanfile.py in a single folder, and a bash script with the exact commands to reproduce? Do you want to put it in a .zip file and share it, just in case?

@robert-shade
Copy link
Author

robert-shade commented Aug 22, 2023

Ok, try this: (fails in both release/2.0 and 2.0.9)

def test_build_id_error():
    c = TestClient()
    myconan = textwrap.dedent("""
        from conan import ConanFile
        from conan.tools.scm import Version

        import os
        import glob

        class BuildIdTestConan(ConanFile):
            settings = "os", "arch", "compiler", "build_type"

            options = {"shared": [True, False]}

            default_options = {
                "shared": False, }

            def build_id(self):
                self.info_build.settings.build_type = "Any"
                self.info_build.options.shared = "Any"

                if self.settings.os == "Windows":
                    self.info_build.settings.compiler.runtime = "Any"

            def build(self):
                pass

            def package_id(self):
                # Since we only get gcc7 and vs2017 binaries, we re-use
                # the same ones for future versions.
                compilerVer = Version(str(self.info.settings.compiler.version))

                if self.info.settings.compiler == "gcc":
                    if compilerVer >= "7":
                        self.info.settings.compiler.version = "7+"
                elif str(self.info.settings.compiler) == "Visual Studio":
                    if compilerVer >= "15":
                        self.info.settings.compiler.version = "15+"
                elif str(self.info.settings.compiler) == "msvc":
                    if compilerVer >= "191":
                        self.info.settings.compiler.version = "191+"
 
    host_profile = textwrap.dedent("""
        [settings]
        arch=x86_64
        build_type=Release
        compiler=gcc
        compiler.cppstd=gnu17
        compiler.libcxx=libstdc++11
        compiler.version=8
        os=Linux
        """)

    build_profile = textwrap.dedent("""
        [settings]
        arch=x86_64
        build_type=Release
        compiler=gcc
        compiler.cppstd=gnu17
        compiler.libcxx=libstdc++11
        compiler.version=8
        os=Linux
        """)
    c.save({"conanfile.py": myconan, "myhostprofile" : host_profile, "mybuildprofile" : build_profile })
    c.run("create . "
          "--profile:host myhostprofile --profile:build mybuildprofile "
          "--name=test_build_id --version=1.0.0 --user=my --channel=test --settings=build_type=Debug --options=test_build_id/*:shared=True")
    c.run("create . "
          "--profile:host myhostprofile --profile:build mybuildprofile "
          "--name=test_build_id --version=1.0.0 --user=my --channel=test --settings=build_type=Debug --options=test_build_id/*:shared=False")
    c.run("create . "
          "--profile:host myhostprofile --profile:build mybuildprofile "
          "--name=test_build_id --version=1.0.0 --user=my --channel=test --settings=build_type=Release --options=test_build_id/*:shared=True")
    c.run("create . "
          "--profile:host myhostprofile --profile:build mybuildprofile "
          "--name=test_build_id --version=1.0.0 --user=my --channel=test --settings=build_type=Release --options=test_build_id/*:shared=False")

@memsharded
Copy link
Member

It seems myconan is missing closing quotes?

@memsharded
Copy link
Member

After fixing the quotes, I have managed to reproduce! :)
Thanks very much this is very good news.

@memsharded
Copy link
Member

memsharded commented Aug 23, 2023

This was a challenging one! But thanks to your efforts in reproducing, I managed to come up with an understanding of the root causes and a fix in #14555, proposing it for next 2.0.10. Thanks very much again for your feedback and hard work to reproduce it! 🙂

The root cause was the relative order in the DB of the different packages reusing the same build-folder, and this is the reason it was so challenging to reproduce. As all the different package-ids were storing the coordinates of the build_id build-folder, when a new package_id that was alphabetically newer could come up with a solution for the same build_id, when in reality it doesn't contain the build_folder. I have fixed it by guaranteeing that only the package build that does the build from source contains the build_id in the DB, and making sure than removing the build folder (conan cache clean), removes the entry from the DB too.

@robert-shade
Copy link
Author

Excellent! Glad it was helpful and you were able to squash it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants