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

Retrieve package installation size #2209

Open
RomanSoloweow opened this issue Feb 11, 2025 · 11 comments
Open

Retrieve package installation size #2209

RomanSoloweow opened this issue Feb 11, 2025 · 11 comments

Comments

@RomanSoloweow
Copy link

RomanSoloweow commented Feb 11, 2025

Hi, I couldn't find a command to get the installation size of a package, i.e., the installation size including uninstalled dependencies.

The calculation occurs when calling dnf install --assumeno, but running this command doesn't seem convenient, especially since I need to do it for a list of packages, getting the installation size per package.

Is there such functionality available?

@m-blaha
Copy link
Member

m-blaha commented Feb 11, 2025

Some information is available in the dnf info <package> output. But it's only installation size of that particular package, dependencies are not included.

If you need to run this calculation regularly, it will be probably pretty easy to write a plugin command or python script that uses libdnf5 API, that will:

  • prepare a temporary goal
  • add installation job of packages you need to calculate the installation size with dependencies
  • resolve the transaction
  • do the calculation - sum(installation sizes of all incomming packages) - sum(instllation sizes of all outgoing packages)

@m-blaha
Copy link
Member

m-blaha commented Feb 11, 2025

Here is how such script can look like:

#!/usr/bin/python3

import libdnf5

# initialize libdnf5
base = libdnf5.base.Base()
base.load_config()
base.setup()

# read all repositories
repo_sack = base.get_repo_sack()
repo_sack.create_repos_from_system_configuration()
repo_sack.load_repos()

# Create a goal, which is a class that allows to add items for resolving into
# a transaction.
goal = libdnf5.base.Goal(base)

# Add packages for installation into the goal.
for pkgspec in ["vim-enhanced", "powerline-fonts", "fzf", "python3-virtualenv", "ruff"]:
    goal.add_rpm_install(pkgspec)

# Resolve the goal, create a transaction object.
transaction = goal.resolve()

# Here you can iterate the resolved transaction and calculate the size
incomming_size = 0
outgoing_size = 0
for tspkg in transaction.get_transaction_packages():
    if libdnf5.transaction.transaction_item_action_is_inbound(tspkg.get_action()):
        incomming_size += tspkg.get_package().get_install_size()
    else:                                    
        outgoing_size += tspkg.get_package().get_install_size()
       
print("Installation size is:", incomming_size - outgoing_size)

@RomanSoloweow
Copy link
Author

Do you have an example of preparing a goal? I couldn't find any complete examples of using libdnf5

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

Do you have an example of preparing a goal? I couldn't find any complete examples of using libdnf5

it's the line goal = libdnf5.base.Goal(base) in the example script above. Nothing fancy :)

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

I just noticed that your issue is filed against dnf4 (which has been replaced by dnf5 since Fedora 41). Since I primarily work on dnf5, I automatically used its API in the example - apologies for that. If you need a dnf4 version, let me know.

Also, the current example is equivalent to: dnf5 install --assumeno vim-enhanced powerline-fonts fzf python3-virtualenv ruff. It calculates the overall installation size. If you need the values per package, you’ll need to modify the code to iterate through the packages. But in case there is an overlap in dependency trees of the packages, the results might not be accurate - the shared dependencies would be included in all packages.

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

This version prints installation size per package:

#!/usr/bin/python3

import libdnf5

# initialize libdnf5
base = libdnf5.base.Base()
base.load_config()
base.setup()

# read all repositories
repo_sack = base.get_repo_sack()
repo_sack.create_repos_from_system_configuration()
repo_sack.load_repos()

for pkgspec in ["vim-enhanced", "powerline-fonts", "fzf", "python3-virtualenv", "ruff"]:
    # Create a goal, which is a class that allows to add items for resolving into
    # a transaction.
    goal = libdnf5.base.Goal(base)

    # Add package for installation into the goal.
    goal.add_rpm_install(pkgspec)

    # Resolve the goal, create a transaction object.
    transaction = goal.resolve()

    # Here you can iterate the resolved transaction and calculate the size
    incomming_size = 0
    outgoing_size = 0
    for tspkg in transaction.get_transaction_packages():
        if libdnf5.transaction.transaction_item_action_is_inbound(tspkg.get_action()):
            incomming_size += tspkg.get_package().get_install_size()
        else:
            outgoing_size += tspkg.get_package().get_install_size()

    print("Installation size of {} is: {}".format(pkgspec, incomming_size - outgoing_size))

@RomanSoloweow
Copy link
Author

Thank you very much for your detailed response and examples. I am seriously considering switching to using the API.

If I understand correctly, libdnf4 is written in C++, which means I can use it directly from C#. Where can I find the library on my Rocky Linux 9.2?

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

Well, Rocky Linux 9 is exactly why you need the dnf4 API. RHEL 8, 9, and 10 all ship with dnf4, and dnf5 isn’t available there.
So your issue is filed in the right place - I’m the one who confused things by bringing up libdnf5. My bad!

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

Here is the version of the script, that uses dnf4 API:

#!/usr/bin/python3

import dnf

base = dnf.Base()
base.read_all_repos()
base.fill_sack(load_system_repo=True)

for pkgspec in ["vim-enhanced", "powerline-fonts", "fzf", "python3-virtualenv", "ruff"]:
    base.install(pkgspec)
    base.resolve()
        
    incomming_size = 0
    outgoing_size = 0
        
    for tsi in base.transaction:
        if tsi.action in dnf.transaction.FORWARD_ACTIONS:
            incomming_size += tsi.pkg.installsize
        else:
            outgoing_size += tsi.pkg.installsize
       
    print("Installation size of {} is: {}".format(pkgspec, incomming_size - outgoing_size))  
    base.reset(sack=False, repos=False, goal=True)

@RomanSoloweow
Copy link
Author

Could you please clarify whether I can use libdnf4 as a C++ library?

@m-blaha
Copy link
Member

m-blaha commented Feb 12, 2025

Let me clarify it.

  1. there is "dnf4 stack", package manager used in Fedora till version 40, and in RHEL 8, 9, and 10. It consists of

    • the python dnf module and the command line tool written also in Python
    • the libdnf library written in C / C++
    • underlying libsolv library written in C
  2. then there is "dnf5 stack", default package manager in Fedora since version 41, it consists of

    • dnf5 command line tool and libdnf5 library, both written in C++
    • uses underlying libsolv library written in C

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

No branches or pull requests

2 participants