Installing ooo-build for development

In order to install the freshly built ooo-build, run

make dev-install

This installs ooo-build in the build/install directory of the source tree. Once done, you can launch ooo-build this way:

cd build/install/program
source ooenv
./soffice

This is equivalent to the following with the installation dir path set to build/install

bin/ooinstall -l <installation dir path>

Notice the -l option given to the ooinstall script, which sets symbolic links to the shared libraries in the build directory instead of copying them into the installation directory. This saves disk space since it avoids making duplicated binaries. But more importantly, it makes it more efficient to make changes to the code, re-build it and test the change in the installation since you don't have to overwrite the libraries after each re-build. But keep in mind that, you do need to source the ooenv script when you install ooo-build with an -l option, or else the application would fail to start and probably crash right after the splash screen.

Up-stream/ooo-build boundary

When hacking, you have to use the up-stream tools to build parts of OOo. The boundary is the build/ subdirectory of ooo-build, ie. when you get to build/ooo320-m12, you are leaving ooo-build, and have to use the up-stream tools.

The ooo-build additions are stored as patches in the patches/ subdirectory of ooo-build. Read the Managing patches section of the Contributing page for more info about this.

My first hack

So - we've built and run OO.o, and we want to prove to ourselves that it is in fact possible to hack on it. So in a new terminal do this:

cd build/ooo320-m12
. ./LinuxX86Env.Set.sh
cd vcl

Now have a hack at vcl/source/window/menu.cxx (Menu::SetItemText); (near line 1770) I suggest manually applying this change:

-               pData->aText = rStr;
+               pData->aText = String(rStr).Reverse();

Then save. (You can find more things to hack in the Tutorials.)

You're still in vcl/ yes ? then type 'build debug=true'; wait for the scrolling text to stop; (5 seconds?). Now re-run soffice -writer. You should notice the effect in the menus. If not, ensure the previous soffice.bin was dead with killall -9 soffice.bin

Note: for day to day hacking you want to just run 'build' inside the source tree. It is also highly recommended to work inside a copy of the build tree, and generate / test patches in an un-hacked version. To copy just the build/ooo300-m12 directory elsewhere, you need to use the bin/relocate tool.

My real hack

Where to start with hacking? The best is to fix something you personally hate in OOo. Do you have a document that crashes, but nobody seems to care? Is there a feature you'd love using, but nobody has implemented it yet? Do it yourself! And when you have finished, please let us know about that - please see the Contributing page.

Coding conventions

First thing you'll notice is that the tabs are not 8 spaces, but 4 - please setup your favorite text editor to reflect that, otherwise you'll see the source code distorted. The second thing you'll notice is that the coding style is not consistent; we don't like that situation, and try to enforce the most usual style where possible:

  • For aligning code, never use tab stops, just spaces [vi: set tabstop=4 expandtab]
  • 4 spaces for alignment
  • '{'s (beginnings of blocks) on new lines, newlines after '}'s
  • Spaces after ',', '(', and keywords
  • Spaces before ')'
  • Variables are named like xName [where x is 'p' for pointers, 'r' for references, 'n' for integer types, 'e' for enums, 'a' for other types]
  • Attributes are prefixed with m_ doing m_xName
  • Methods use CapitalFirstLetters(), static functions impl_TheFunction() So, something like:
void Blah::DoBleh( const rtl::OUString &rString )
{
    m_aBlah = rString;

    for ( sal_uInt32 nIndex = 0; nIndex < 10; ++nIndex )
    {
        impl_DoBleh( nIndex, m_aBlah );
        if ( nIndex > 8 )
            impl_DontDoUgh( nIndex );
    }
}

Read the Fine manual

With the power of C++ comes the ability to shoot yourself in the foot all the more easily; (and implicitly), cf. Holub, Rules for C and C++ programming, McGraw-Hill, 95.

The best way to prepare yourself for battle is to read the OpenOffice coding guidelines, and for the easily confused c'tor / d'tor is short for constructor / destructor.

Also, there's an extensive list of recommended literature about C++ - but that's of course no prerequisite to start coding.

Starting the right app

As you start soffice.bin, there are several useful parameters to use to accelerate your debugging experience; particularly -writer, -calc, -draw, and (the wizardly painful) -impress arguments.

Understanding D' make (man)

While the build system is in similar to many other systems, it is also perhaps slightly different. The overview is that each module is built, and then the results are delivered into the solver. Each module builds against the headers in the solver. Thus there are a few intricacies.

  • build — this perl script solenv/bin/build.pl is used in conjunction with prj/build.lst to ensure that every module that is needed is built first. build then un-winds internal module dependencies, and builds each module with a chdir, dmake pair.
  • deliver — this perl script solenv/bin/deliver.pl installs headers, and libraries (etc.) into the solver, as informed by prj/d.lst. Crucially deliver ensures that the date stamp on any file that is installed to the solver is the same as that in the module's directory. This ensures, that (particularly for headers) that there is no dependency cascade triggering re-builds in other modules.

Standard directories

There are various standard directories and files in most of the modules that make up OO.o, here are some of the more useful:

  • prj
    • build.lst — this lists directories to be made, '^#' comments are allowed, the order of the list is immaterial see the prj/build.lst description below, it is dictates build's operation.
    • d.lst — this file describes the deliver process, see prj/d.lst details below.
  • util — typically the util directory is charged with glueing together the sub-libraries for each sub-module into a single large library, adding system library dependencies, building GUI resource files etc. All the work is described in makefile.mk, this is usually the last directory to be built in a project.
  • inc — public headers are typically separated into an 'inc' directory, these will be installed into the solver by the 'deliver' phase (using prj/d.lst) Build's mode of operation is to invoke 'dmake' in each of the projects' directories with a given dependency order. dmake then executes the rules in makefile.mk.

prj/build.lst

On first view build.lst looks scary:

vc vcl : NAS:nas FREETYPE:freetype psprint rsc sot ucbhelper unotools sysui NULL
vc vcl usr1 - all vc_mkout NULL
vc vcl\source\unotypes nmake - all vc_unot NULL
vc vcl\source\glyphs nmake - all vc_glyphs vc_unot NULL
...
vc vcl\mac\source\src nmake - m vc__srcm vc_unot NULL
vc vcl\util nmake - all
vc_util vc__plug.u vc__appa.u vc__appm.m vc__appu.u vc__appw.w vc__gdim.m
vc__gdiu.u vc__gdiw.w vc__srcm.m vc__srcu.u vc__srcw.w
vc__wina.u vc__winm.m vc__winu.u vc__winw.w vc__du.u
vc__gtka.u vc__gtkw.u vc__gtkg.u vc__kde.u vc_app
vc_ctrl vc_gdi vc_hlp vc_src vc_win vc_glyphs NULL

so we need to try and un-pack what's going on here, which is in fact not as odd as it might seem at first glance. Firstly lists are terminated by the 'NULL' string. Every line is prefixed by a shortcut which has to be unique, no two modules are allowed to have the same shortcut.

  • First active line contains a ':', this describes the fact that this project (vcl) depends on these other modules nas, freetype, psprint etc. to be built first. This is for inter-project dependencies.
  • Some modules have a CAPITALS:lowercase arrangement; the first segment is a conditional, driven by a space delimited list in the BUILD_TYPE environment variable at build time.
  • Then we have a redundant line 'usr1' [ for fun ? ], in fact only lines containing the magic string 'nmake' are valid after this.
  • The next lines describe internal project directory dependencies and look like:
[shortcut] [path to dir to build] nmake - [flags] [unique-name] [deps...] NULL
vc vcl\source\glyphs nmake - all vc_glyphs vc_unot NULL

  • shortcut is not used; flags determines which platforms this builds on; usually single char platform codes: 'dnpum' 'u' being Unix. The higher up the system, the more stuff is flagged 'all'. unique-name this is a magic name, used by other lines to describe an internal dependency. deps... any number of names of other directories in this file, that must be built before this one. If such prerequisite dir is built for special platforms only, the platform code has to be appended to its magic name. So we see in the vcl case that vcl\source\unotypes (vc_unot) has to be built before vcl\source\glyphs (vc_glyphs). For Mac vcl\mac\source\src (vcsrcm) has to be built before vcl\util (vc_util) It is important to understand that the order of the list is ~immaterial, and instead of a simple ordered list, we have a more complex internal dependency system — this contrasts with most other make systems.
There is also documentation here on it.

deliver, deliver.log and prj/d.lst

The syntax of d.lst is more comprehensible than build.lst, it omits some default actions, such as copying build.lst into inc/&lt;module&gt;/build.lst.

A line is of the form:

[action]: [arguments]
mkdir: %_DEST%\inc%_EXT%\external

where if '[action]:' is omitted, it defaults to the 'copy' action. Typical actions are copy, mkdir, touch, hedabu, dos and linklib.

The 'hedabu' action is particularly interesting, inasmuch that it cosmetically re-formats the header to shrink it on install and adds the module name to the include path (#include "myheader.hxx"_ becomes #include ). Otherwise it's much like the copy action.

During the action, various macro variables are expanded some of which are:

  • %SRC% — distribution directory name eg. unxlngi4.pro
  • %_DEST% — absolute path into solver eg. /opt/OpenOffice/OOO_STABLE_1/solver/641/unxlngi4.pro
  • %_EXT% — (unusual) way of having minor updates eg. 641.1, typically used to version every base sub-directory. Typically then, if indeed you need to add a rule (cf. implicit directory copies), it will be of the form:
..\%__SRC%\inc\sal\*.h %_DEST%\inc%_EXT%\sal\*.h

NB. relative paths are relative to the 'prj/' directory.

Sometimes you might run into the situation where you'd like to remove the delivered files from solver. Your configuration might have changed and they are no longer needed. "deliver -delete" does just that, however you might still need to delete "deliver.log" files manually as well.

Can I get a char *, please?

Just barely. OO.o has at least six string wrappers, although the C implementations are of little interest:
rtl_String` (sal/inc/rtl/string.h)
"Normal" string plus reference counting. rtlstring->buffer is useful, as is rtlstring->length. This object encapsulates an generic 8bit string - of unknown encoding. Feel free to treat rtlstring->buffer as your beloved char *. If you really want to look at the implementation of some rtl_String function and lxr nor grep can help you, have a look at sal/rtl/source/strtmpl.c.
OString (sal/inc/rtl/string.hxx)
Simply a rtl_String wrapped inside a class; you can use ostring.pData to get at the rtl_String (it's public). OString has reasonably useful methods for if you need them.
rtl_uString (sal/inc/rtl/ustring.h)
"Normal" Unicode string, similar to rtl_String, and refcounted as well. However, this one always comes in UCS-2 encoding, presumably to be compatible with Java's questionable choices. See rtl_String above to find where the implementation of some rtl_uStringfunctions is hidden.
OUString (sal/inc/rtl/ustring.hxx)
An rtl_uString wrapped inside a class. This is what most of the OO.o code uses to pass strings around. To convert an OString to an OUString it is necessary to specify the character set of the OString see; sal/inc/rtl/textenc.h - the only interesting case is RTL_TEXTENCODING_UTF8
String (tools/inc/string.hxx)
This is an obsolete string class, aliased to '?UniString'. It has a number of limitations such as a 64k length limit. You can have the buffer with GetBuffer(), but it's Utf-16 encoded.

A couple of conversion functions are really useful here, particularly:

// OUString -> OString
rtl::OString aOString = ::rtl::OUStringToOString( aOUString, RTL_TEXTENCODING_UTF8 );
// OString -> OUString
rtl::OUString aOUString = ::rtl::OStringToOUString( aOString, RTL_TEXTENCODING_UTF8 );

If you just want to programattically print out a string for debugging purposes you probably want define a macro like:

#define CHAR_POINTER(THE_OUSTRING) ::rtl::OUStringToOString( THE_OUSTRING, RTL_TEXTENCODING_UTF8 ).getStr()
// Use it:
printf( "SvXMLNamespaceMap::AddIfKnown : %s / %s\n", CHAR_POINTER(rPrefix), CHAR_POINTER(rName) );
// For the obsolete String class, aliased UniString, it's like:
printf( "rGrfName : %s\n", ByteString( rGrfName, RTL_TEXTENCODING_UTF8).GetBuffer() );

To print the value of rtl::OUString directly in the debugger, you can use dbg_dump(). It is intended to be called interactively from the debugger, in both debug and non-debug builds of OOo. You can see the definition in sal/rtl/source/debugprint.cxx.

Some code snippets about manipulating those objects can be found on the codesnippets service page : [http://codesnippets.services.openoffice.org/Office/cpp.xml]

Linkoo & Limitations

Linkoo is the tool that implements the -l functionality of bin/ooinstall. It essentially sym-links files of similar names into your local tree, allowing a fast development iteration.

It is however slightly limited - some of the modules cannot be linked for various reasons; these are: cppuhelper and configmgr, thus in the rare case that these are altered, they must be copied manually into /opt/OOInstall/program.

In addition symlinks cannot be used for soffice.bin, and this is more commonly altered - it has to be installed from desktop/unxlngi4.pro/bin/soffice NB. with an appended '.bin'

References

Now you probably want to continue with the DebuggingIt pages.