Building Applications
Grace has a powerful application model that can take care of a lot of grind work. In cases where your application needs to carry a lot of internal resources, Grace can also build OSX-style .app bundles. This makes it easier for your application to refrain from leaving scent marks all over the filesystem.
Command Line Options
Resources Inside the Application Bundle
The Configuration File
Building Standalone Executables
Initialization
Using mkproject
The grace mkproject command creates a project directory skeleton for new Grace projects. Here's how you use it:
$ grace mkproject Application domain [nl.madscience.unregistered.apps]: com.openpanel.apps Application name: storpelcount Creating com.openpanel.apps.storpelcount $ cd com.openpanel.apps.storpelcount/ $ ls -l -rw-r--r-- 1 pi pi 471 Jun 26 22:38 Makefile -rwxr-xr-x 1 pi pi 18229 Jun 26 22:38 configure -rw-r--r-- 1 pi pi 43 Jun 26 22:38 configure.in -rw-r--r-- 1 pi pi 293 Jun 26 22:38 main.cpp -rwxr-xr-x 1 pi pi 126 Jun 26 22:38 makeinstall drwxr-xr-x 6 pi pi 204 Jun 26 22:38 rsrc -rw-r--r-- 1 pi pi 485 Jun 26 22:38 storpelcount.h $
A skeleton headerfile storpelcount.h has been created that will look like this:
#ifndef _storpelcount_H #define _storpelcount_H 1 #include <grace/application.h> // ------------------------------------------------------------------------- /// Implementation template for application config. // ------------------------------------------------------------------------- typedef configdb<class storpelcountApp> appconfig; // ------------------------------------------------------------------------- /// Main application class. // ------------------------------------------------------------------------- class storpelcountApp : public application { public: storpelcountApp (void) : application ("com.openpanel.apps.storpelcount") { } ~storpelcountApp (void) { } int main (void); protected: appconfig conf; }; #endif
A skeleton implementation file main.cpp contains a stub application::main virtual method implementation:
#include "storpelcount.h" $appobject(storpelcountApp); // ========================================================================= /// Main method. // ========================================================================= int storpelcountApp::main (void) { string conferr; if (! conf.load ("com.openpanel.apps.storpelcount", conferr)) { ferr.writeln ("%% Error loading configuration: %s" %format (conferr)); return 1; } return 0; }
Command Line Options
The application class takes care of a lot of initialization, including the command line argument parser. You can define command line flags by tweaking the application::opt value object. If your project builds ass an app-bundle, you can define further command line options in rsrc/grace.runoptions.xml. Through the mkapp tool, this file will get copied to Contents/Resources/ inside the app bundle. The application constructor will attempt to load "rsrc:grace.runoptions.xml". Here's a sample:
<?xml version="1.0"?>
<grace.runoptions>
<grace.option id="-h">
<grace.long>--help</grace.long>
</grace.option>
<grace.option id="-i">
<grace.long>--input-file</grace.long>
</grace.option>
<grace.option id="--help">
<grace.argc>0</grace.argc>
</grace.option>
<grace.option id="--input-file">
<grace.argc>1</grace.argc>
<grace.default>/var/db/storpels</grace.default>
<grace.help>Storpel file to process</grace.help>
</grace.option>
</grace.runoptions>
In the main method, we can access the parsed command line options through application::argv:
int storpelcountApp::main (void) { string infile = argv["--input-file"]; string conferr; if (! conf.load ("com.openpanel.apps.storpelcount", conferr)) { ferr.writeln ("%% Error loading configuration: %s" %format (conferr)); return 1; } if (! fs.exists (infile)) { ferr.writeln ("File '%s' not found" %format (infile)); return 1; } fout.writeln ("Processing '%s'" %format (infile)); return 0; }
Resources Inside the Application Bundle
If you look at the project Makefile, you will see that it goes through a somewhat more elaborate process to create your target application:
- Source files are turned into object files the usual way.
- The object files are linked into appname.exe.
- The grace mkapp tool is used to create a bundle named appname.app and a softlink appname that points to the executable inside the bundle directory.
$ make /usr/bin/g++ -I/sw/include -c main.cpp /usr/bin/g++ -o storpelcount.exe main.o -L/sw/lib -lgrace grace mkapp storpelcount * Building storpelcount for Darwin.powerpc: > Creating ./storpelcount.app... OK > Installing executable... OK > Installing rsrc/com.openpanel.apps.storpelcount.conf.xml... OK > Installing rsrc/com.openpanel.apps.storpelcount.schema.xml... OK > Installing rsrc/com.openpanel.apps.storpelcount.validator.xml... OK > Installing rsrc/grace.runoptions.xml... OK $ ls -l total 152 -rw-r--r-- 1 pi pi 471 Jun 26 22:57 Makefile -rwxr-xr-x 1 pi pi 18229 Jun 26 22:57 configure -rw-r--r-- 1 pi pi 43 Jun 26 22:57 configure.in -rw-r--r-- 1 pi pi 185 Jun 26 22:57 configure.paths -rw-r--r-- 1 pi pi 293 Jun 26 22:57 main.cpp -rw-r--r-- 1 pi pi 6496 Jun 26 22:57 main.o -rw-r--r-- 1 pi pi 383 Jun 26 22:57 makeinclude -rwxr-xr-x 1 pi pi 126 Jun 26 22:57 makeinstall -rw-r--r-- 1 pi pi 87 Jun 26 22:57 platform.h drwxr-xr-x 6 pi pi 204 Jun 26 22:57 rsrc lrwxr-xr-x 1 pi pi 21 Jun 26 22:57 storpelcount -> storpelcount.app/exec drwxr-xr-x 4 pi pi 136 Jun 26 22:57 storpelcount.app -rwxr-xr-x 1 pi pi 10472 Jun 26 22:57 storpelcount.exe -rw-r--r-- 1 pi pi 485 Jun 26 22:57 storpelcount.h
Files in the rsrc directory are installed using the following rules:
| File matches | Description | Installed to | Alias Path |
|---|---|---|---|
| *.conf.xml | Default configuration files | Contents/Configuration Defaults | conf: |
| *.schema.xml *.validator.xml |
XML Schema definitions | Contents/Schemas | schema: |
| *.thtml | Scripted HTML templates | Contents/Templates | tmpl: |
| * | Anything else | Contents/Resources | rsrc: |
$ cd storpelcount.app $ cd Contents $ find . -type f ./Configuration Defaults/com.openpanel.apps.storpelcount.conf.xml ./Darwin.powerpc/storpelcount.exe ./Resources/grace.runoptions.xml ./Schemas/com.openpanel.apps.storpelcount.schema.xml ./Schemas/com.openpanel.apps.storpelcount.validator.xml $
The Configuration File
You my have noticed that the conf.xml got installed inside the application bundle. Obviously this isn't really useful for user configuration. This is where the conf: alias path comes to the rescue. The grace mkconf tool installs a copy of the default configuration into a suitable location:
| Condition | Install Location |
|---|---|
| If the current user is root: | /etc/conf/$appid |
| If the platform is Mac OS X: | home:Library/Preferences/$appid |
| The first of these locations if the parent folder exists: | home:.conf/$appid home:conf/$appid home:etc/conf/$appid |
| If all else fails: | home:.$appid |
- A configuration inside the user's homedir
- A global configuration file in /etc/conf
- The Contents/Resources directory inside the application bundle.
#include "myapp.h" #include "storpelclient.h" int myApp::main (void) { string conferr; if (! conf.load ("com.openpanel.apps.myapp", conferr)) { ferr.writeln ("%% Error loading config: %s" %format (conferr)); return 1; } storpelclient cl; cl.sethost (conf["storpel"]["connecthost"]); cl.storpelize (); return 0; }
The configdb has more powerful features that we will investigate when we're building a daemon.
Building Standalone Executables
You can also set up your project as a standalone executble using the --noapp option to grace mkproject. If your project is set like this, there is no grace.runoptions.xml file — only app-bundles support resource files. You can still influence the command line argument parsing by filling the application::opt object in your application's constructor:
class storpelcountApp : public application { public: storpelcountApp (void) : application ("com.openpanel.apps.storpelcount") { opt = $("-h", $("long", "--help")) -> $("-i", $("long", "--input-file")) -> $("--help", $("argc", 0) ) -> $("--input-file", $("argc", 1) -> $("default", "/var/db/storpels") -> $("help", "Storpel file to process") ); } ... };
Standalone executables can have no resource files or default configuration. The grace mkproject template will assume configuration will be kept in an INI-style configuration file in /etc.
Initialization
Grace applications don't use the C standard main() function
— this function is already provided by the libgrace shared object.
The
void grace_init (void) { defaults::sz::file::readbuf = 16384; }
| Previous Chapter | Table of Contents | Next Chapter |