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.
1.1. The command header
dnf5/command/template.hpp
1#ifndef DNF5_COMMANDS_TEMPLATE_TEMPLATE_HPP
2#define DNF5_COMMANDS_TEMPLATE_TEMPLATE_HPP
3
4#include "arguments.hpp"
5
6#include <dnf5/context.hpp>
7#include <libdnf5/conf/option_bool.hpp>
8
9#include <memory>
10#include <vector>
11
12namespace dnf5 {
13
14// This template aims to explain how to add a new command to dnf5.
15// This is the definition of a command called `template` with two options:
16// --foo and --bar.
17class TemplateCommand : public Command {
18public:
19 // The command name is set directly in the constructor.
20 explicit TemplateCommand(Context & context) : Command(context, "template") {}
21
22 // This method needs to be overridden to register the command with
23 // the dnf5 command-line parser, and place the command in one of
24 // the standard command groups.
25 //
26 // Every top-level command will have a set_parent_command method.
27 // Subcommands will not, their parent command's register_subcommands()
28 // method is used to register them.
29 void set_parent_command() override;
30
31 // This method needs to be overridden to define the structure of the
32 // command such as description and options.
33 void set_argument_parser() override;
34
35 // This method MAY be overridden in a top-level command which has
36 // subcommands, to register them using register_subcommand().
37 // void register_subcommands() override;
38
39 // This method needs to be overridden to run the command.
40 void run() override;
41
42 // This method MAY be overridden to perform any configuration
43 // needed by the command.
44 // void configure() override;
45
46 // This method MAY be overridden to perform any pre-configuration
47 // needed by the command.
48 // void pre_configure() override;
49
50 // Not every command will need a pre_configure or configure method,
51 // and few will need both.
52 //
53 // A common pattern in commands that have subcommands is to define
54 // the top-level command's pre_configure as containing only
55 // throw_missing_command(). This ensures that using the command
56 // without one of its subcommands will be treated as an error.
57
58private:
59 // There are two ways to specify options for a command.
60
61 // Option 1: C++ Pointer.
62 // Create an OptionBool pointer for the option you need.
63 //
64 // Using a pure C++ pointer is safe here since the OptionBool class
65 // will take care of moving the pointer ownership to the parser, which will
66 // be in charge of handling the memory deallocation.
67 libdnf5::OptionBool * foo_option{nullptr};
68
69 // Option 2: STL Unique Pointer
70 // Create a std::unique_ptr<BarOption>
71 //
72 // This might be needed in case you need full control over the options and
73 // do not want dnf5's parser to handle it.
74 // To use a unique_ptr, BarOption has to be defined.
75 // A boolean BarOption is defined in the accompanying "arguments.hpp".
76 std::unique_ptr<BarOption> bar_option{nullptr};
77};
78
79} // namespace dnf5
80
81#endif // DNF5_COMMANDS_TEMPLATE_TEMPLATE_HPP
1.2. The command source
dnf5/command/template.cpp
1#include "template.hpp"
2
3#include <iostream>
4
5namespace dnf5 {
6
7using namespace libdnf5::cli;
8
9void TemplateCommand::set_parent_command() {
10 auto * parent_cmd = get_session().get_argument_parser().get_root_command();
11 auto * this_cmd = get_argument_parser_command();
12 parent_cmd->register_command(this_cmd);
13
14 auto & group = parent_cmd->get_group("software_management_commands");
15 group.register_argument(this_cmd);
16}
17
18void TemplateCommand::set_argument_parser() {
19 // Context is the main object in dnf5.
20 // It contains useful functions and pieces of information necessary to run
21 // and interact with dnf5 capabilities.
22 //
23 // For example, here we need the parser object which is necessary to define
24 // commands and options.
25 auto & ctx = get_context();
26 auto & parser = ctx.get_argument_parser();
27
28 auto & this_cmd = *get_argument_parser_command();
29
30 // Configure 'template' command in the Parser
31 //
32 // Set a description that would be displayed in the second column of
33 // the main help message.
34 this_cmd.set_description("A command that prints its name and arguments' name");
35
36 // Add '--foo' and '--bar' options
37 //
38 // Option 1: as said in the header file here you should handle the pointer
39 // by giving up the ownership to dnf5's parser.
40 // Set the default value here.
41 foo_option = dynamic_cast<libdnf5::OptionBool *>(
42 parser.add_init_value(
43 std::unique_ptr<libdnf5::OptionBool>(
44 new libdnf5::OptionBool(false))));
45
46 // Create an option by giving it a name. It will be shown in the help message.
47 auto foo = parser.add_new_named_arg("foo");
48 // Set the long name for the option.
49 foo->set_long_name("foo");
50 // Set the option's description (for the help message).
51 foo->set_description("print foo");
52 // Set the constant value. This is the value assigned when an option
53 // which takes no value arguments appears on the command line.
54 foo->set_const_value("true");
55 // Link the option to the TemplateCommand's class member.
56 foo->link_value(foo_option);
57
58 // Register the '--foo' argument to the 'template' command.
59 this_cmd.register_named_arg(foo);
60
61 // Option 2: create a unique_ptr of type BarOption.
62 // The long name, description, and other values were set in
63 // template.hpp when we derived the BarOption class.
64 bar_option = std::make_unique<BarOption>(*this);
65}
66
67void TemplateCommand::run() {
68 // The behavior of the command is a simple hello world.
69 std::cout << "Template Command" << std::endl;
70
71 if (foo_option->get_value()) {
72 std::cout << "foo" << std::endl;
73 }
74 if (bar_option->get_value()) {
75 std::cout << "bar" << std::endl;
76 }
77}
78
79} // namespace dnf5
1.3. The argument class(es)
dnf5/command/arguments.hpp
1#ifndef DNF5_COMMANDS_TEMPLATE_ARGUMENTS_HPP
2#define DNF5_COMMANDS_TEMPLATE_ARGUMENTS_HPP
3
4#include <libdnf5-cli/session.hpp>
5#include <libdnf5/utils/bgettext/bgettext-lib.h>
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#endif // DNF_COMMANDS_TEMPLATE_ARGUMENTS_HPP
1.4. Direct integration into dnf5 codebase
Caution
If you are writing an external command to be included in a dnf5
plugin, STOP here and move on to the DNF5 Plugin Template.
The remainder of this page is only applicable when writing commands
to be included directly in the dnf5 codebase (in the dnf5/commands/
subdirectory).
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 are registered in the add_commands() function
context.add_and_initialize_command(
std::make_unique<TemplateCommand>(context));
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 [GLOBAL OPTIONS] template [OPTIONS]
Options:
--bar print bar
--foo print foo