documentation/writing_fshell_commands.pod
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Wed, 23 Jun 2010 15:52:26 +0100
changeset 0 7f656887cf89
child 36 698ccde15713
permissions -rw-r--r--
First submission to Symbian Foundation staging server.

#!perl
# writing_fshell_commands.pod
#
# Copyright (c) 2010 Accenture. All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Accenture - Initial contribution
#

__END__

=head1 Writing fshell commands

A quick guide to adding a new fshell command, covering the fshell build and configuration system, the internals of commands themselves and some background on the relevant concepts.

=head1 Before you start

=head2 Step 1: RTM!

The first step is to make sure there isn't already a command that does what you need. It may seem obvious but there are quite a lot of commands and some have a lot of options and different functionality. Skim through the L<command list|fshell::commands> to be certain. For example you might note there's no 'bluetooth' command in fshell - however a check of the L<command list|fshell::commands> will reveal the L<btservices|fshell::commands::btservices> command which supports a number of Bluetooth-related operations.

=head2 Check the todo list

Check the todo list (I<TODO: figure out where the todo list is going to live!>). Someone else may have similar ideas or already be working on similar functionality.

=head2 Decide whether you want to add it to fshell

You're free to write whatever tools you like that use fshell and build on top of it. We (Accenture) have a number of specialised tools that use fshell but aren't part of the fshell package itself. You may however wish to contribute back to the fshell package; if so there are some additional constraints regarding where to put the source and the fshell build and configuration system (in addition to the Foundation Contribution guidelines http://developer.symbian.org/wiki/index.php/Contribution_Process ). You can ignore a lot of the below if you don't plan to add your command to the fshell package.

=head1 Getting stuck in

Once you've decided there isn't a suitable existing command, and no-one else is developing one, it's time to get started. Let's assume your new fshell command will be called C<qotd> and displays an insightful Quote Of The Day each time you run it. We're also assuming that you plan to integrate the command into the fshell package, thus will create the source code in the fshell\commands\qotd directory. (Please note we have no plans to actually add a qotd command - attributing the quotes and copyright is far too much like hard work!)

If you are not contributing the command to the fshell package please put the source outside of the fshell tree.

=head2 Built-in vs external

(aka 'thread' vs 'process')

There are two types of fshell command: ones built into fshell.exe itself (such as cd, export, source, etc) and ones that are implemented as standalone exes (such as listapps.exe, drvinfo.exe and so forth). Built-in commands are restricted as to what APIs they can use (so as to not introduce problematic dependancies), so we generally recommend that new commands are external ones that run in a separate process. Contact the fshell maintainers if you think you've a candidate for a new built-in command.

Each external command lives in a separate directory \fshell\commands\<commandname>. Built in commands live in the \fshell\core tree.

=head2 Using C<createsrc> to generate the boilerplate

The createsrc command (installed in \epoc32\tools) is a great way to automate creating the skeleton of a new command. Run it with no arguments to be prompted for every option, for brevity the example below shortcuts it slightly and assumes you've already created the directory F<fshell\commands\qotd>. It also overrides the default copyright message, since you presumably won't want to assign copyright to Accenture!

    M:\fshell\commands\qotd>
    M:\fshell\commands\qotd>createsrc fshellcmd qotd -DCOPYRIGHT="Copyright (c) 2010 N. Seagoon. All Rights Reserved."
    Enter short description of the command (for the .cif file) for qotd: Displays a randomly selected quote of the day.
    M:\fshell\commands\qotd>
    M:\fshell\commands\qotd>dir /B
    bld.inf
    qotd.cif
    qotd.cpp
    qotd.mmp

At this point you have an fshell command that will compile and run - although it won't do very much yet.

The C<.cif> file contains meta-data about the command - its description and what arguments and options it accepts. See L<here|cif_syntax> for more information on the CIF file format. The C<bld.inf> and C<.mmp> are the standard Symbian OS build files. The C<.cpp> is a minimalist implementation of your command, based on the information you passed to createsrc.

=head2 CCommandBase

As you'll see, the C<.cpp> file declares a single class CCmdQotd, which derives from C<IoUtils::CCommandBase> and implements a few derived functions. CCommandBase is the base class for all fshell commands. When your command is executed, fshell converts the command line arguments into member variables of your CCommandBase subclass and calls DoRunL(). (It figures out how to do this from the CIF file and the result of calling the OptionsL() and ArgumentsL() functions). The implementation of your command goes in DoRunL(). For now we will implement a very simple DoRunL that just prints a single string and exits:

    void CCmdQotd::DoRunL()
        {
        Printf(_L("Quote goes here!\r\n"));
        }

If you build your command now, you'll see it in action:

    M:\fshell\commands\qotd>sbs -c winscw_udeb
    ...
    M:\fshell\commands\qotd>fshell
    ...
    c:\>qotd
    Quote goes here!
    c:\>exit
    M:\fshell\commands\qotd>

Note how we've used the fshell bat file to start the winscw emulator inside the DOS command prompt. There's a lot of stuff going on behind the scenes to make this work, but it's a convenient way of testing commands. Normally you'd build the fshell tree (or any part of it) from one of the \fshell\build directories, and you wouldn't use the bld.inf that createsrc generated. I've skipped that for the moment for simplicity's sake.

=head2 Adding options and arguments

Fshell takes care of parsing command lines in a consistant manner, but you have to let it know what your command supports. Let's say that you want to give qotd a C<--shouty> option which, if specified, prints the quote in upper case. Since all options in fshell commands have a long form and a single-letter abbreviation, we'll choose C<-s>. Firstly we need to add a C<==option> section to the CIF file:

    ==option bool s shouty

    Displays the quote in UPPER CASE.

Now we need to update the C++ code. Firstly, to add a TBool to CCmdQotd for fshell to fill in:

     class CCmdQotd : public CCommandBase
        {
    ...
     private:
    -//TODO: arguments/options
    +   TBool iShouty;    
        };

Then we need to associate the --shouty option with the iShouty member variable:

    void CCmdQotd::OptionsL(RCommandOptionList& aOptions)
        {
        aOptions.AppendBoolL(iShouty, _L("shouty"));
        }

If you rebuild and run the help now, you'll see that the option is all configured:

    c:\>qotd --help
    SYNTAX

        qotd [options]

    OPTIONS

         -s (--shouty)  Displays the quote in UPPER CASE.
         -h (--help)    Display help.

    DESCRIPTION

    Displays a randomly selected quote of the day.

    COPYRIGHT

    Copyright (c) 2010 N. Seagoon. All Rights Reserved.

    c:\>

All that remains is to actually do something when --shouty is specified:

    void CCmdQotd::DoRunL()
        {
        if (iShouty)
            {
            Printf(_L("QUOTE GOES HERE!\r\n"));
            }
        else
            {
            Printf(_L("Quote goes here!\r\n"));
            }
        }

After rebuilding, you should be able to do:

    c:\>qotd
    Quote goes here!
    c:\>
    c:\>qotd --shouty
    QUOTE GOES HERE!
    c:\>
    c:\>qotd -s
    QUOTE GOES HERE!
    c:\>

Adding other types of option/argument is done in a similar way, see L<here|cif_syntax/CIF types and CCommandBase types> for the list of CIF types and their equivalent C++ types. The one thing to be aware of is that you are responsible for deleting any string or array arguments in the command's destructor, even though the fshell framework created them. This is mainly for legacy compatability reasons, it should probably be done automatically in ~CCommandBase().

=head2 Integrating with fshell's build system

I<If you're not writing a command that's going to be integrated into the fshell source tree, you can mostly ignore this section.>

To make sure your new command gets built, add the mmp and any exports (eg the CIF) to \fshell\commands\group\bld.inf:

    PRJ_EXPORTS
    ..\qotd\qotd.cif            z:\resource\cif\fshell\qotd.cif
    PRJ_MMPFILES
    ..\qotd\qotd.mmp

The fshell build system makes heavy use of preprocessor conditionals to make sure that the codebase builds on a wide variety of platforms. In this example qotd doesn't have any depenancies so the files can be added unconditionally to the bld.inf. If however you take the USB command as an example, you'll see that in the bld.inf it is surrounded by C<#ifdef FSHELL_CORE_SUPPORT_USB>. This in turn is defined in L<common.mmh|common_mmh> only if C<FSHELL_COMMS_SUPPORT> is available. In other words the USB command relies on comms support being available in the platform that's being build. (You may thing that's overkill but early baseport bringup regularly needs to build minimalist textshell ROMs without any form of comms support other than maybe a uart - this level of configurability means it's possible to include fshell on there too.)

If your command can take advantage of a feature, but doesn't absolutely require it, you may wish to guard only sections of code with an appropriate C<FSHELL_xyz> macro. common.mmh can be included anywhere the preprocessor is used (source files, bld.inf, MMPs, resource files) so you can be as fine-grained as you like. For example fshell itself uses many C<#ifdef FSHELL_MEMORYACCESS_SUPPORT> blocks so that ps can give more detailed process listings if MemoryAccess is available, but falls back gracefully to less detailed info if it isn't.

B<I<To be completed...>>