Writing Your Own Modules
Writing your own modules for Zorba is easy.This guide will cover writing a simple XQuery module; using Zorba's CMake-based build system to deploy the module; versioning your module; and writing a more complex module with external functions implemented in C++.
Creating a Simple XQuery Module
A "module" is simply a library of XQuery functions and variables, usually which perform a set of related functions. Modules are defined by the XQuery language specification.A module exists in a particular
namespace, which is identified by a URI. A module must start with a module
declaration, which specifies the namespace URI and associates that URI with a prefix for easy reference.
module namespace mymod = "http://zorba.io/mymod";
After the module declaration, you may have as many
function or
variable declarations as you like. All of the functions and variables you declare in the modules namespace will be made available to queries that
import your module.Here we declare a module which exposes a string variable (and assign it a value), as well as two simple functions.
module namespace mymod = "http://zorba.io/mymod";
declare variable $mymod:value as xs:string := "my string value";
declare function mymod:hello() as xs:string {
"hello"
};
declare function mymod:get-value() as xs:string {
$mymod:value
};
Assuming for the moment that this file is stored in the current working directory as
mymod.xq, we can write a query which imports the module and uses one of its functions:
import module namespace mymod="http://zorba.io/mymod"
at "mymod.xq";
mymod:hello()
If this query is in a file called
query.xq in the current working directory, we can execute it:
% zorba -f -q query.xq
<?xml version="1.0" encoding="UTF-8"?>
hello
Creating a CMake Module Project
As mentioned in
Importing Modules, using the
at "..." clauses when importing modules is best avoided. There are also many ways to expand upon this example, such as introducing versioning for our module; importing schemas; depending on other modules; writing external function implementations in C++; and creating test cases to ensure your module's functionality.Zorba offers a comprehensive system for module authors to do all of the above easily and consistently, utilizing the same build tool that Zorba itself utilizes: CMake (
http://www.cmake.org/). If you plan to write Zorba modules for use by other developers, we strongly recommend creating a CMake project so that you may easily integrate with Zorba and other modules.(Note: the majority of this section is also relevant if you are developing an entire stand-alone application which uses Zorba as a library.)Assuming that you have cmake installed (it is available in most Linux distributions, and installers are available for Windows and MacOS - it is also part of MacPorts), creating a project for a Zorba module is very easy. First, in a new directory, copy the
mymod.xq file from above, and create a file named
CMakeLists.txt:
# My Zorba Module Project
PROJECT (my_zorba_module)
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
FIND_PACKAGE (Zorba REQUIRED)
INCLUDE ("${Zorba_USE_FILE}")
DECLARE_ZORBA_MODULE (FILE mymod.xq URI "http:
DONE_DECLARING_ZORBA_URIS ()
This is the minimum required "boilerplate" for your CMake project. Here is a brief explanation for each line:
# My Zorba Module Project
This is a CMake comment.
PROJECT (my_zorba_module)
CMake groups build environments into
projects, which must have unique names. The name can be anything you like, but should be descriptive. In future, if other module projects depend on your module project, they will use this name to identify it.
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
CMake has introduced many new features over time; Zorba's CMake support requires at least CMake version 2.6. Without the above line, your project will still work (assuming you are in fact using at least CMake 2.6), but will display a warning.
FIND_PACKAGE (Zorba REQUIRED)
INCLUDE ("${Zorba_USE_FILE}")
CMake has an exhaustive facility for introspecting your development environment and finding dependencies. The
FIND_PACKAGE() command will search for many things, including other CMake projects such as Zorba. Zorba ships CMake configuration files allowing it to be found in this way, and these configuration files tell our project where Zorba is located as well as setting a number of other useful variables describing the Zorba environment.One such variable,
${Zorba_USE_FILE}, is a pointer to a CMake script containing a variety of utility CMake macros that are very useful when developing modules. By using
INCLUDE() to load this file, we gain access to everything we need to build our module. (This "use file" technique is a convention for CMake projects.)
DECLARE_ZORBA_MODULE (FILE mymod.xq URI "http://zorba.io/mymod")
DECLARE_ZORBA_MODULE() is the centerpiece of the Zorba module development environment; most of the rest of this guide will be describing how to use it in detail.
DONE_DECLARING_ZORBA_URIS()
This must be the last thing called by your top-level
CMakeLists.txt file. It collects all the information provided by earlier calls to
DECLARE_ZORBA_MODULE() (and its sister macro,
DECLARE_ZORBA_SCHEMA(), described later) and generates all the necessary build system rules and targets.
Building the Module Project
You have created a small but complete Zorba module CMake project. Now, the project must be built. One of the great things about CMake is that it is capable of creating build environments for a number of different tools. On Unix systems (include Windows with Cygwin), it can create a Makefile-based environment. It can also create projects for the KDevelop, Code::Blocks, and QtCreator IDEs, among others, and has some support for Eclipse as well. On MacOS, it can create Xcode projects. On Windows, it can create Visual Studio projects, and it can also create a Makefile-based environment for use with Visual Studio's nmake utility. See CMake's documentation for more details and other supported build environments.This guide will focus on a Makefile environment, but it should be equally applicable to all others.CMake encourages the use of "out-of-source" builds; that is, all files that are generated by the build are created in a directory which is separate from the source code. This is good practice. One common convention is to create a directory named
build as a subdirectory of the top-level directory of the project, so from the command-line, execute the following commands from your project directory:
mkdir build
cd build
cmake ..
The
cmake .. command tells CMake to create a build environment based on the source directory
.., that is, the parent directory. You could also create your build directory anywhere on the system, in which case, you simply provide the full absolute or relative path to your source directory as the argument to
cmake.If you have Zorba installed in a standard system-wide location, the above will likely be all you need to do. However, if the
cmake step does not find Zorba, you can point CMake in the right direction like this:
cmake -DCMAKE_PREFIX_PATH=/path/to/zorbaroot ..
"zorbaroot" is the root directory of a Zorba installation; it should contain
bin/zorba (
bin/zorba.exe on Windows) among other files.The default build configuration for CMake is "Unix Makefiles". If you are building using a different tool set, you must provide the
-G option to
cmake. For example, if you wish to create a Visual Studio 10 project, use:
cmake -G"Visual Studio 10" ..
No matter what your eventual
cmake command line ends up being, the nice thing is you will only need to execute it once. From that point on, you can simply invoke the default build target in whatever build environment you have created. CMake sets up all dependencies correctly, and will automatically re-invoke itself if you modify the
CMakeLists.txt file or make any other changes which would require
cmake to generate the build environment again.So, go ahead and build! For Unix Makefiles, just type:
make
What Happens When You Build?
For this very simple module project, not much happens when you build. You will probably see output similar to this:
Scanning dependencies of target check_uris
[ 0%] Copying /tmp/myproject/mymod.xq to URI path
[100%] Built target check_uris
The primary result of building this project is that a directory named (by default)
URI_PATH will be created in your build directory, containing all of your declared modules and schemas in an appropriate directory structure for Zorba's URI resolution mechanism (see
URI Resolvers). This means that we can eliminate the
at "..." clause from our test query which imports this module:
import module namespace "http:
mymod:hello()
and then execute Zorba as follows:
% zorba --uri-path /tmp/myproject/build/URI_PATH -f -q query.xq
<?xml version="1.0" encoding="UTF-8"?>
hello
By providing this one
--uri-path argument, Zorba will automatically be able to load any of our modules or schemas by URI, rather than requiring explicit filesystem paths to them. This makes it much easier to develop larger XQuery applications, because the entire application can be moved around easily, and you won't need to edit all of your queries to change the paths to load your modules as you move from development to production.
Module Authoring, ContinuedBuilding Modules Into Zorba