1. DNF5 Command Template

This page focuses on how to write a command with two options in dnf5.

Note

This code is thought to be self explanatory. You should be able to copy the snippets, following the directory structure and naming shown above each one.

dnf5/command/template.hpp

 1#define DNF5_COMMANDS_TEMPLATE_TEMPLATE_HPP
 2#include "arguments.hpp"
 3
 4#include <dnf5/context.hpp>
 5#include <libdnf5/conf/option_bool.hpp>
 6
 7#include <memory>
 8#include <vector>
 9
10
11namespace dnf5 {
12
13// This template aims to explain how to add a new command to dnf5.
14// This is the definition of a command called `template` with two options:
15// --foo and --bar.
16class TemplateCommand : public Command {
17public:
18    // The command name is set directly in the constructor.
19    explicit TemplateCommand(Command & parent) : Command(parent, "template") {}
20
21    // This method needs to be overridden to define the structure of the
22    // command such as description, options or sub-commands.
23    void set_argument_parser() override;
24
25    // This method needs to be overridden to run the command.
26    void run() override;
27
28private:
29    // There are two ways to specify options for a command.
30
31    // Option 1: C++ Pointer.
32    // Create a OptionBool pointer for the option you need.
33    //
34    // Using a pure C++ pointer is safe here since the OptionBool class
35    // will take care of moving the pointer ownership to the parser, which will
36    // be in charge of handling the memory deallocation.
37    libdnf5::OptionBool * foo_option{nullptr};
38
39    // Option 2: STL Unique Pointer
40    // Create a unique_ptr<BarOption>
41    //
42    // This might be needed in case you need full control over the options and
43    // do not want dnf5's parser to handle it.
44    // To use a unique_ptr, BarOption has to be defined (see the file
45    // dnf5/commands/arguments.hpp)
46    std::unique_ptr<BarOption> bar_option{nullptr};
47};
48
49
50}  // namespace dnf5
51
52
53#endif  // DNF5_COMMANDS_TEMPLATE_TEMPLATE_HPP

dnf5/command/template.cpp

 1namespace dnf5 {
 2
 3using namespace libdnf5::cli;
 4
 5void TemplateCommand::set_argument_parser() {
 6    // Context is the main object in dnf5.
 7    // It contains useful functions and pieces of information necessary to run
 8    // and interact with dnf5 capabilities.
 9    //
10    // For example, here we need the parser object which is necessary to define
11    // commands and options.
12    auto & ctx = get_context();
13    auto & parser = ctx.get_argument_parser();
14
15    auto & cmd = *get_argument_parser_command();
16
17    // Add template command to the Parser
18    //
19    // Set a description that would be displayed in the second column of the
20    // help message would also add the command to the parser
21    cmd.set_short_description("A command that prints its name and arguments' name");
22
23    // Add foo and bar options
24    //
25    // Option 1: as said in the header file here you should handle the pointer
26    // by giving up the ownership to dnf5's parser.
27    // Set the default value here.
28    foo_option = dynamic_cast<libdnf5::OptionBool *>(
29        parser.add_init_value(std::unique_ptr<libdnf5::OptionBool>(new libdnf5::OptionBool(false))));
30
31    // Create an option by giving it a name. It will be shown in the help message.
32    // Set long name, description and constant value.
33    // Link the option to the TemplateCommand's class member.
34    auto foo = parser.add_new_named_arg("foo");
35    foo->set_long_name("foo");
36    foo->set_short_description("print foo");
37    foo->set_const_value("true");
38    foo->link_value(foo_option);
39
40    // Register the argument to the command template.
41    cmd.register_named_arg(foo);
42
43    // Option 2: create a unique_ptr of type BarOption.
44    // The long name, description, and other values were set in
45    // dnf5/command/arguments.hpp
46    bar_option = std::make_unique<BarOption>(*this);
47}
48
49void TemplateCommand::run() {
50    // The behavior of the command is a simple hello world.
51    std::cout << "Template Command" << std::endl;
52
53    if (foo_option->get_value()) {
54        std::cout << "foo" << std::endl;
55    }
56    if (bar_option->get_value()) {
57        std::cout << "bar" << std::endl;
58    }
59}
60
61}  // namespace dnf5

dnf5/command/arguments.hpp

 1#define DNF5_COMMANDS_TEMPLATE_ARGUMENTS_HPP
 2
 3#include <libdnf5-cli/session.hpp>
 4#include <libdnf5/utils/bgettext/bgettext-lib.h>
 5
 6
 7namespace dnf5 {
 8
 9// This implementation is needed only if you are using unique_ptr as the type
10// for the option of your command.
11// You will have to initialize the Option yourself.
12class BarOption : public libdnf5::cli::session::BoolOption {
13public:
14    // Initialize the constructor passing command, long name, short name,
15    // description and default value.
16    explicit BarOption(libdnf5::cli::session::Command & command)
17        : BoolOption(command, "bar", '\0', _("print bar"), false) {}
18};
19
20}  // namespace dnf5
21
22
23#endif  // DNF_COMMANDS_DOWNLOAD_TEMPLATE_ARGUMENTS_HPP

The command must be included and registered in dnf5/main.cpp

// new commands must be included in main.cpp
#include "commands/template/template.hpp"

// commands must be registered like this
register_subcommand(std::make_unique<TemplateCommand>(*this), software_management_commands_group);

Following this example you should have an output like this.

$ dnf5 --help
...
Software Management Commands:
  install                                Install software
  upgrade                                Upgrade software
  ...
  template                               A command that prints its name and arguments' name
$ dnf5 template --help
Usage:
  dnf5 template [GLOBAL OPTIONS] [OPTIONS] [ARGUMENTS]

Options:
  --bar                         print bar
  --foo                         print foo