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

[question] Full Requires with conan2 python API #16259

Closed
1 task done
lstoffer opened this issue May 15, 2024 · 7 comments
Closed
1 task done

[question] Full Requires with conan2 python API #16259

lstoffer opened this issue May 15, 2024 · 7 comments
Assignees

Comments

@lstoffer
Copy link

What is your question?

Hi! I have a question regarding the conan2 python API.
I have a PkgReference and want to determine all the dependencies that this package has (also transitive dependencies). Is there a way to achieve this with the current state of the conan2 python API.

What I found and seems to be on the right track is the GraphAPI and its function load_graph. However I struggle to see how to get the root_node that has to be passed to this function from the PkgReference I already have.

Have you read the CONTRIBUTING guide?

  • I've read the CONTRIBUTING guide
@memsharded memsharded self-assigned this May 15, 2024
@memsharded
Copy link
Member

Hi @lstoffer

Thanks for your question.

If we are talking about a PkgReference, including the package_id, please note that there is not a correlation 1:1 with a package binary and its dependencies. There are lots of scenarios when 1 package can have multiple different dependencies:

  • A package that contain version ranges for its requires, and with the right binary compatibility can depend on different dependencies, for example it can depend on different patch versions of its dependencies if those are shared libraries, with exactly the same app package-id
  • An header-only library can depend on different versions and different binaries of its dependencies without changing its package_id.

Now, if for a given reference like mypkg/version#recipe-revision you want to construct its dependency graph, you will need several inputs, like the profiles, something like (not tested):

    remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
    lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, cwd=cwd, partial=args.lockfile_partial)
    profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)

    deps_graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires,
                                                         profile_host, profile_build, lockfile, remotes)

Where you can input your references in args.requires or args.tool_requires depending if you want to compute the graph of a require or a tool. That would be a list ["mypkg/version#recipe-revision"].

Is this what you are looking for? You shouldn't need to compute the root_node yourself to get the graph.

@lstoffer
Copy link
Author

Hi @memsharded

Thank you very much for your reply. I am not totally sure this is what I am looking for.
The thing that I am trying to do is to find all the dependencies for a certain revision of a recipe that is in my Artifactory. So what I am doing is looking for the reference of the newest revision for my project and then try to find all dependencies based on this reference.

The reference I am currently obtaining like this:

conan2 = ConanAPI()
remote = conan2.remotes.get("inhouse")
recipes = conan2.search.recipes("someproject", remote)
latest_recipe_revision = conan2.list.latest_recipe_revision(recipes[0], remote)
package_configurations = conan2.list.packages_configurations(latest_recipe_revision, remote)
filtered_package_configurations = conan2.list.filter_packages_configurations(package_configurations, query='os=Windows AND arch=x86_64 AND build_type=Release')

This leads to the following output:

OrderedDict({someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4%1714131016.762:6d774737773e35689f5ec580bd8ccd30aef5afd7: {'settings': {'arch': 'x86_64', 'build_type': 'Release', 'compiler': 'msvc', 'compiler.runtime': 'dynamic', 'compiler.runtime_type': 'Release', 'compiler.version': '193', 'os': 'Windows'}, 'options': {'shared': 'True'}, 'requires': [...]}})

So I basically want to find all the references for all the dependencies (including the transitive ones) of someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4%1714131016.762:6d774737773e35689f5ec580bd8ccd30aef5afd7

@memsharded
Copy link
Member

So I basically want to find all the references for all the dependencies (including the transitive ones) of someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4%1714131016.762:6d774737773e35689f5ec580bd8ccd30aef5afd7

For finding the dependencies of a given reference, it is necessary to build a dependency graph, and this is more or less the code that I showed above.

There is not a 1:1 correlation for a given package reference someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4%1714131016.762:6d774737773e35689f5ec580bd8ccd30aef5afd7 with dependencies, what you can do is to compute the dependency graph of a given reference, excluding its package_id, that is someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4, and the input profiles:

refs = ["someproject/0.30.0-alpha.1@lst#399a71260a37756d2d54673984f799c4"]
deps_graph = conan_api.graph.load_graph_requires(refs, None, profile_host, profile_build, lockfile, remotes)

@lstoffer
Copy link
Author

Thank you very much.

remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, cwd=cwd, partial=args.lockfile_partial)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)
deps_graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires,
profile_host, profile_build, lockfile, remotes)

Just one more thing that I still do not understand about the code you shared. How are the lockfile, profile_host and profile_build generated. Or more precisely where does the args used to do it come from?

@memsharded
Copy link
Member

The args are the inputs from user from command line arguments. It is true that the profile one is only suited for cli args, but you can replace them with your own inputs, like:

remotes = conan_api.remotes.list(["myremote1", "myremote2"])
lockfile = conan_api.lockfile.get_lockfile(lockfile="mylockfile.lock", partial=True)
profile_host = conan_api.profiles.get_profile(["myprofilehost"]
profile_build = conan_api.profiles.get_profile(["myprofilebuild"])

@lstoffer
Copy link
Author

Thank you very much! I was able to load the dependency graph.
Is there an easy way I can get all the transitive dependencies of my package from this loaded dependency graph?
Because it seems that in the resolved_ranges part of the dependency graph are only the direct dependencies and the transitive ones are missing.

@memsharded
Copy link
Member

Yes, this dependency graph, you can call graph.serialize() and you will get a serialized representation totally equivalent to the one that you obtain with conan install ... --format=json > graph.json, so that is relatively easy to understand and traverse. You can dump it with json.dumps(graph.serialize(), indent=2) to easily inspect it.

This format is documented in https://docs.conan.io/2/reference/commands/formatters/graph_info_json_formatter.html, you can see for any node, its dependencies will report its actual direct and transitive dependencies (the ones this node needs)

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

No branches or pull requests

2 participants