summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-07-24 09:13:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-07-24 09:13:21 +0200
commitb0396809c19f436e051430d5e81d59a725611195 (patch)
tree29e374c30aef241b4edfa604a131b4c33ecedc3f
parentd19d20fbd638c3edb0e2dea1589455546a309382 (diff)
Convert to build2-based build
Also add symbol exporting, missing const in a few places in the API, as well as a basic test.
-rw-r--r--.gitattributes19
-rw-r--r--.gitignore6
-rw-r--r--README.md24
-rw-r--r--buildfile5
-rw-r--r--libgenx/.gitignore19
l---------libgenx/LICENSE1
l---------libgenx/README.md1
-rw-r--r--libgenx/build/.gitignore4
-rw-r--r--libgenx/build/bootstrap.build7
-rw-r--r--libgenx/build/export.build6
-rw-r--r--libgenx/build/root.build8
-rw-r--r--libgenx/buildfile5
-rw-r--r--libgenx/libgenx/.gitignore3
-rw-r--r--libgenx/libgenx/buildfile51
-rw-r--r--libgenx/libgenx/char-props.c (renamed from char-props.c)2
-rw-r--r--libgenx/libgenx/export.h33
-rw-r--r--libgenx/libgenx/genx.c (renamed from genx.c)19
-rw-r--r--libgenx/libgenx/genx.h (renamed from genx.h)73
-rw-r--r--libgenx/libgenx/version.h.in37
-rw-r--r--libgenx/manifest12
-rw-r--r--libgenx/tests/.gitignore8
-rw-r--r--libgenx/tests/basics/buildfile3
-rw-r--r--libgenx/tests/basics/driver.c41
-rw-r--r--libgenx/tests/basics/testscript3
-rw-r--r--libgenx/tests/build/.gitignore4
-rw-r--r--libgenx/tests/build/bootstrap.build5
-rw-r--r--libgenx/tests/build/root.build12
-rw-r--r--libgenx/tests/buildfile1
-rw-r--r--makefile20
-rw-r--r--packages.manifest2
30 files changed, 396 insertions, 38 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1631641
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,19 @@
+# This is a good default: files that are auto-detected by git to be text are
+# converted to the platform-native line ending (LF on Unix, CRLF on Windows)
+# in the working tree and to LF in the repository.
+#
+* text=auto
+
+# Use `eol=crlf` for files that should have the CRLF line ending both in the
+# working tree (even on Unix) and in the repository.
+#
+#*.bat text eol=crlf
+
+# Use `eol=lf` for files that should have the LF line ending both in the
+# working tree (even on Windows) and in the repository.
+#
+#*.sh text eol=lf
+
+# Use `binary` to make sure certain files are never auto-detected as text.
+#
+#*.png binary
diff --git a/.gitignore b/.gitignore
index 5ae09b2..dd04439 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
-# Compiler/linker output.
+.bdep/
+
+# Local default options files.
#
-*.o
+.build2/local/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a0d8c4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# libgenx - XML serializer C library
+
+A portable, dependency-free, MIT-licensed XML push-serializer library for C.
+
+```
+#include <libgenx/genx.h>
+
+int main()
+{
+ genxWriter w = genxNew(NULL, NULL, stdout);
+ genxStartDocSender(w, &stdioSender);
+ genxStartElementLiteral(w, (utf8)"http://example.org/1", (utf8)"greeting");
+ genxAddAttributeLiteral(w, (utf8)"http://example.com/2", (utf8)"type", (utf8)"well-formed");
+ genxAddText(w, (utf8)"Hello world!");
+ genxEndElement(w);
+ genxEndDocument(w);
+ genxDispose(w);
+}
+```
+
+This is a cleaned up, fixed, and enhanced version of [Tim Bray's
+Genx](https://www.tbray.org/ongoing/genx/docs/Guide.html). Note that support
+for `genxStartDocFile()` (which depends on `<stdio.h>`) has been removed.
+See the `basics` test for an example of how to achieve the same yourself.
diff --git a/buildfile b/buildfile
new file mode 100644
index 0000000..aad5e21
--- /dev/null
+++ b/buildfile
@@ -0,0 +1,5 @@
+# Glue buildfile that "pulls" all the packages in the project.
+#
+import pkgs = */
+
+./: $pkgs
diff --git a/libgenx/.gitignore b/libgenx/.gitignore
new file mode 100644
index 0000000..cece09c
--- /dev/null
+++ b/libgenx/.gitignore
@@ -0,0 +1,19 @@
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.ii
+*.o
+*.obj
+*.so
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libgenx/LICENSE b/libgenx/LICENSE
new file mode 120000
index 0000000..ea5b606
--- /dev/null
+++ b/libgenx/LICENSE
@@ -0,0 +1 @@
+../LICENSE \ No newline at end of file
diff --git a/libgenx/README.md b/libgenx/README.md
new file mode 120000
index 0000000..32d46ee
--- /dev/null
+++ b/libgenx/README.md
@@ -0,0 +1 @@
+../README.md \ No newline at end of file
diff --git a/libgenx/build/.gitignore b/libgenx/build/.gitignore
new file mode 100644
index 0000000..974e01d
--- /dev/null
+++ b/libgenx/build/.gitignore
@@ -0,0 +1,4 @@
+/config.build
+/root/
+/bootstrap/
+build/
diff --git a/libgenx/build/bootstrap.build b/libgenx/build/bootstrap.build
new file mode 100644
index 0000000..9240e9d
--- /dev/null
+++ b/libgenx/build/bootstrap.build
@@ -0,0 +1,7 @@
+project = libgenx
+
+using version
+using config
+using test
+using install
+using dist
diff --git a/libgenx/build/export.build b/libgenx/build/export.build
new file mode 100644
index 0000000..e254e62
--- /dev/null
+++ b/libgenx/build/export.build
@@ -0,0 +1,6 @@
+$out_root/
+{
+ include libgenx/
+}
+
+export $out_root/libgenx/$import.target
diff --git a/libgenx/build/root.build b/libgenx/build/root.build
new file mode 100644
index 0000000..451fbcd
--- /dev/null
+++ b/libgenx/build/root.build
@@ -0,0 +1,8 @@
+using c
+
+h{*}: extension = h
+c{*}: extension = c
+
+# The test target for cross-testing (running tests under Wine, etc).
+#
+test.target = $c.target
diff --git a/libgenx/buildfile b/libgenx/buildfile
new file mode 100644
index 0000000..d82ab19
--- /dev/null
+++ b/libgenx/buildfile
@@ -0,0 +1,5 @@
+./: {*/ -build/} doc{README.md} legal{LICENSE} manifest
+
+# Don't install tests.
+#
+tests/: install = false
diff --git a/libgenx/libgenx/.gitignore b/libgenx/libgenx/.gitignore
new file mode 100644
index 0000000..c2f1607
--- /dev/null
+++ b/libgenx/libgenx/.gitignore
@@ -0,0 +1,3 @@
+# Generated version header.
+#
+version.h
diff --git a/libgenx/libgenx/buildfile b/libgenx/libgenx/buildfile
new file mode 100644
index 0000000..4331d64
--- /dev/null
+++ b/libgenx/libgenx/buildfile
@@ -0,0 +1,51 @@
+intf_libs = # Interface dependencies.
+impl_libs = # Implementation dependencies.
+#import impl_libs += libhello%lib{hello}
+
+lib{genx}: {h c}{** -version} h{version} $impl_libs $intf_libs
+
+# Include the generated version header into the distribution (so that we don't
+# pick up an installed one) and don't remove it when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+h{version}: in{version} $src_root/manifest
+{
+ dist = true
+ clean = ($src_root != $out_root)
+}
+
+# Build options.
+#
+c.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: c.poptions += -DLIBGENX_STATIC_BUILD
+objs{*}: c.poptions += -DLIBGENX_SHARED_BUILD
+
+# Export options.
+#
+lib{genx}:
+{
+ c.export.poptions = "-I$out_root" "-I$src_root"
+ c.export.libs = $intf_libs
+}
+
+liba{genx}: c.export.poptions += -DLIBGENX_STATIC
+libs{genx}: c.export.poptions += -DLIBGENX_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{genx}: bin.lib.version = @"-$version.project_id"
+else
+ lib{genx}: bin.lib.version = @"-$version.major.$version.minor"
+
+# Install into the libgenx/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+{h}{*}:
+{
+ install = include/libgenx/
+ install.subdirs = true
+}
diff --git a/char-props.c b/libgenx/libgenx/char-props.c
index 334ffc2..1201fce 100644
--- a/char-props.c
+++ b/libgenx/libgenx/char-props.c
@@ -11,7 +11,7 @@
* This version is generated semi-automatically from the source code of the
* XML specification via emacs global replace and keyboard macros
*/
-#include "genx.h"
+#include <libgenx/genx.h>
static void charProp(char * p, int c, int prop)
{
diff --git a/libgenx/libgenx/export.h b/libgenx/libgenx/export.h
new file mode 100644
index 0000000..45109b8
--- /dev/null
+++ b/libgenx/libgenx/export.h
@@ -0,0 +1,33 @@
+#ifndef LIBGENX_EXPORT_H
+#define LIBGENX_EXPORT_H
+
+#if defined(LIBGENX_STATIC) /* Using static. */
+# define LIBGENX_SYMEXPORT
+#elif defined(LIBGENX_STATIC_BUILD) /* Building static. */
+# define LIBGENX_SYMEXPORT
+#elif defined(LIBGENX_SHARED) /* Using shared. */
+# ifdef _WIN32
+# define LIBGENX_SYMEXPORT __declspec(dllimport)
+# else
+# define LIBGENX_SYMEXPORT
+# endif
+#elif defined(LIBGENX_SHARED_BUILD) /* Building shared. */
+# ifdef _WIN32
+# define LIBGENX_SYMEXPORT __declspec(dllexport)
+# else
+# define LIBGENX_SYMEXPORT
+# endif
+#else
+/* If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared libraries
+// provided the library only exports functions (in other words, no global
+// exported data) and for the shared case the result will be sub-optimal
+// compared to having dllimport. If, however, your library does export data,
+// then you will probably want to replace the fallback with the (commented
+// out) error since it won't work for the shared case.
+*/
+# define LIBGENX_SYMEXPORT
+#endif
+
+#endif /* LIBGENX_EXPORT_H */
diff --git a/genx.c b/libgenx/libgenx/genx.c
index 5253f1a..5746c50 100644
--- a/genx.c
+++ b/libgenx/libgenx/genx.c
@@ -5,8 +5,6 @@
* For copying permission, see the accompanying LICENSE file.
*/
-#define GENX_VERSION "cs-1"
-
/* Use snprintf() unless instructed otherwise. */
#ifndef GENX_SNPRINTF
# define GENX_SNPRINTF 1
@@ -20,7 +18,8 @@
#include <stdlib.h>
#include <string.h>
-#include "genx.h"
+#include <libgenx/genx.h>
+#include <libgenx/version.h>
#define Boolean int
#define True 1
@@ -116,7 +115,7 @@ struct genxAttribute_rec
*/
struct genxWriter_rec
{
- genxSender * sender;
+ const genxSender * sender;
genxStatus status;
writerSequence sequence;
char xmlChars[GENX_CHAR_TABLE_SIZE];
@@ -966,11 +965,11 @@ static genxStatus checkNCName(genxWriter w, constUtf8 name)
return GENX_SUCCESS;
}
-char * genxGetErrorMessage(genxWriter w, genxStatus status)
+const char * genxGetErrorMessage(genxWriter w, genxStatus status)
{
return w->etext[status];
}
-char * genxLastErrorMessage(genxWriter w)
+const char * genxLastErrorMessage(genxWriter w)
{
return w->etext[w->status];
}
@@ -1092,7 +1091,7 @@ busted:
/*
* get namespace prefix
*/
-utf8 genxGetNamespacePrefix(genxNamespace ns)
+constUtf8 genxGetNamespacePrefix(genxNamespace ns)
{
if (ns->declaration == NULL)
return NULL;
@@ -1305,7 +1304,7 @@ static genxStatus sendxBounded(genxWriter w, constUtf8 start, constUtf8 end)
* for internal routines.
*/
-genxStatus genxStartDocSender(genxWriter w, genxSender * sender)
+genxStatus genxStartDocSender(genxWriter w, const genxSender * sender)
{
if (w->sequence != SEQUENCE_NO_DOC)
return w->status = GENX_SEQUENCE_ERROR;
@@ -2537,7 +2536,7 @@ genxStatus genxAddNamespaceLiteral(genxWriter w,
/*
* return version
*/
-char * genxGetVersion()
+const char * genxGetVersion()
{
- return GENX_VERSION;
+ return LIBGENX_VERSION_STR;
}
diff --git a/genx.h b/libgenx/libgenx/genx.h
index 041815f..8c03e7e 100644
--- a/genx.h
+++ b/libgenx/libgenx/genx.h
@@ -14,6 +14,8 @@
#include <stddef.h> /* size_t */
+#include <libgenx/export.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -86,17 +88,20 @@ typedef void (*genxDealloc) (void * userData, void* data);
* one document at a time with a writer.
* Returns NULL if it fails, which can only be due to an allocation failure.
*/
+LIBGENX_SYMEXPORT
genxWriter genxNew(genxAlloc alloc, genxDealloc dealloc, void * userData);
/*
* Reset the writer object back into usable state after an error or
* interruption.
*/
+LIBGENX_SYMEXPORT
genxStatus genxReset (genxWriter w);
/*
* Dispose of a writer, freeing all associated memory
*/
+LIBGENX_SYMEXPORT
void genxDispose(genxWriter w);
/*
@@ -107,14 +112,20 @@ void genxDispose(genxWriter w);
* The userdata pointer will be passed to memory-allocation
* and I/O callbacks. If not set, genx will pass NULL
*/
+LIBGENX_SYMEXPORT
void genxSetUserData(genxWriter w, void * userData);
+
+LIBGENX_SYMEXPORT
void * genxGetUserData(genxWriter w);
/*
* Set/get pretty-printing. If indentation is set to 0, then no pretty-
* printing is performed.
*/
+LIBGENX_SYMEXPORT
genxStatus genxSetPrettyPrint(genxWriter w, int indentation);
+
+LIBGENX_SYMEXPORT
int genxGetPrettyPrint(genxWriter w);
/*
@@ -130,8 +141,13 @@ int genxGetPrettyPrint(genxWriter w);
* level at which pretty-printing was suspended, with root element
* being level 1.
*/
+LIBGENX_SYMEXPORT
genxStatus genxSuspendPrettyPrint(genxWriter w);
+
+LIBGENX_SYMEXPORT
genxStatus genxResumePrettyPrint(genxWriter w);
+
+LIBGENX_SYMEXPORT
int genxPrettyPrintSuspended(genxWriter w);
@@ -139,7 +155,10 @@ int genxPrettyPrintSuspended(genxWriter w);
* Set/get canonicalization. If true, then output explicit closing
* tags and sort attributes. Default is false.
*/
+LIBGENX_SYMEXPORT
genxStatus genxSetCanonical(genxWriter w, int flag);
+
+LIBGENX_SYMEXPORT
int genxGetCanonical(genxWriter w);
/*
@@ -151,15 +170,23 @@ int genxGetCanonical(genxWriter w);
* the memory; this would be appropriate in an Apache context.
* If "alloc" is not provided, genx routines use malloc() to allocate memory
*/
+LIBGENX_SYMEXPORT
void genxSetAlloc(genxWriter w, genxAlloc alloc);
+
+LIBGENX_SYMEXPORT
void genxSetDealloc(genxWriter w, genxDealloc dealloc);
+
+LIBGENX_SYMEXPORT
genxAlloc genxGetAlloc(genxWriter w);
+
+LIBGENX_SYMEXPORT
genxDealloc genxGetDealloc(genxWriter w);
/*
* Get the prefix associated with a namespace
*/
-utf8 genxGetNamespacePrefix(genxNamespace ns);
+LIBGENX_SYMEXPORT
+constUtf8 genxGetNamespacePrefix(genxNamespace ns);
/*
* Declaration functions
@@ -171,6 +198,7 @@ utf8 genxGetNamespacePrefix(genxNamespace ns);
* genx will generate one of the form g-%d.
* On error, returns NULL and signals via statusp
*/
+LIBGENX_SYMEXPORT
genxNamespace genxDeclareNamespace(genxWriter w,
constUtf8 uri, constUtf8 prefix,
genxStatus * statusP);
@@ -179,6 +207,7 @@ genxNamespace genxDeclareNamespace(genxWriter w,
* Declare an element
* If something failed, returns NULL and sets the status code via statusP
*/
+LIBGENX_SYMEXPORT
genxElement genxDeclareElement(genxWriter w,
genxNamespace ns, constUtf8 name,
genxStatus * statusP);
@@ -186,6 +215,7 @@ genxElement genxDeclareElement(genxWriter w,
/*
* Declare an attribute
*/
+LIBGENX_SYMEXPORT
genxAttribute genxDeclareAttribute(genxWriter w,
genxNamespace ns,
constUtf8 name, genxStatus * statusP);
@@ -207,17 +237,20 @@ typedef struct
genxStatus (* flush)(void * userData);
} genxSender;
-genxStatus genxStartDocSender(genxWriter w, genxSender * sender);
+LIBGENX_SYMEXPORT
+genxStatus genxStartDocSender(genxWriter w, const genxSender * sender);
/*
* End a document. Calls "flush".
*/
+LIBGENX_SYMEXPORT
genxStatus genxEndDocument(genxWriter w);
/*
* Write XML declaration. If encoding or standalone are NULL, then those
* attributes are omitted.
*/
+LIBGENX_SYMEXPORT
genxStatus genxXmlDeclaration(genxWriter w,
constUtf8 version,
constUtf8 encoding,
@@ -229,6 +262,7 @@ genxStatus genxXmlDeclaration(genxWriter w,
* that only contains the root element and, if not NULL, internal
* subset is written.
*/
+LIBGENX_SYMEXPORT
genxStatus genxDoctypeDeclaration(genxWriter w,
constUtf8 root_element,
constUtf8 public_id,
@@ -238,16 +272,19 @@ genxStatus genxDoctypeDeclaration(genxWriter w,
/*
* Write a comment
*/
+LIBGENX_SYMEXPORT
genxStatus genxComment(genxWriter w, constUtf8 text);
/*
* Write a PI
*/
+LIBGENX_SYMEXPORT
genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text);
/*
* Start an element
*/
+LIBGENX_SYMEXPORT
genxStatus genxStartElementLiteral(genxWriter w,
constUtf8 xmlns, constUtf8 name);
@@ -255,6 +292,7 @@ genxStatus genxStartElementLiteral(genxWriter w,
* Start a predeclared element
* - element must have been declared
*/
+LIBGENX_SYMEXPORT
genxStatus genxStartElement(genxElement e);
/*
@@ -262,29 +300,34 @@ genxStatus genxStartElement(genxElement e);
* element ceases to be current (i.e., EndElement() is called).
* If the element is unqualified, then xmlns is set to NULL.
*/
+LIBGENX_SYMEXPORT
genxStatus genxGetCurrentElement (genxWriter w,
constUtf8* xmlns, constUtf8* name);
/*
* Write an attribute
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns,
constUtf8 name, constUtf8 value);
/*
* Write a predeclared attribute
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddAttribute(genxAttribute a, constUtf8 value);
/*
* Start an attribute
*/
+LIBGENX_SYMEXPORT
genxStatus genxStartAttributeLiteral(genxWriter w,
constUtf8 xmlns, constUtf8 name);
/*
* Start a predeclared attribute
*/
+LIBGENX_SYMEXPORT
genxStatus genxStartAttribute(genxAttribute a);
/*
@@ -292,33 +335,39 @@ genxStatus genxStartAttribute(genxAttribute a);
* attribute ceases to be current (i.e., EndAttribute() is called).
* If the attribute is unqualified, then xmlns is set to NULL.
*/
+LIBGENX_SYMEXPORT
genxStatus genxGetCurrentAttribute (genxWriter w,
constUtf8* xmlns, constUtf8* name);
/*
* End an attribute
*/
+LIBGENX_SYMEXPORT
genxStatus genxEndAttribute(genxWriter w);
/*
* add a namespace declaration
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddNamespaceLiteral(genxWriter w,
constUtf8 uri, constUtf8 prefix);
/*
* add a predefined namespace declaration
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddNamespace(genxNamespace ns, constUtf8 prefix);
/*
* Clear default namespace declaration
*/
+LIBGENX_SYMEXPORT
genxStatus genxUnsetDefaultNamespace(genxWriter w);
/*
* Write an end tag
*/
+LIBGENX_SYMEXPORT
genxStatus genxEndElement(genxWriter w);
/*
@@ -326,14 +375,20 @@ genxStatus genxEndElement(genxWriter w);
* You can't write any text outside the root element, except with
* genxComment and genxPI.
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddText(genxWriter w, constUtf8 start);
+
+LIBGENX_SYMEXPORT
genxStatus genxAddCountedText(genxWriter w, constUtf8 start, size_t byteCount);
+
+LIBGENX_SYMEXPORT
genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end);
/*
* Write one character. The integer value is the Unicode character
* value, as usually expressed in U+XXXX notation.
*/
+LIBGENX_SYMEXPORT
genxStatus genxAddCharacter(genxWriter w, int c);
/*
@@ -347,18 +402,21 @@ genxStatus genxAddCharacter(genxWriter w, int c);
* argument to point at the first byte past the point past the malformed
* ones.
*/
+LIBGENX_SYMEXPORT
int genxNextUnicodeChar(constUtf8 * sp);
/*
* Scan a buffer allegedly full of UTF-8 encoded XML characters; return
* one of GENX_SUCCESS, GENX_BAD_UTF8, or GENX_NON_XML_CHARACTER
*/
+LIBGENX_SYMEXPORT
genxStatus genxCheckText(genxWriter w, constUtf8 s);
/*
* return character status, the OR of GENX_XML_CHAR,
* GENX_LETTER, and GENX_NAMECHAR
*/
+LIBGENX_SYMEXPORT
int genxCharClass(genxWriter w, int c);
/*
@@ -370,18 +428,23 @@ int genxCharClass(genxWriter w, int c);
* The output can never be longer than the input.
* Returns true if any changes were made.
*/
+LIBGENX_SYMEXPORT
int genxScrubText(genxWriter w, constUtf8 in, utf8 out);
/*
* return error messages
*/
-char * genxGetErrorMessage(genxWriter w, genxStatus status);
-char * genxLastErrorMessage(genxWriter w);
+LIBGENX_SYMEXPORT
+const char * genxGetErrorMessage(genxWriter w, genxStatus status);
+
+LIBGENX_SYMEXPORT
+const char * genxLastErrorMessage(genxWriter w);
/*
* return version
*/
-char * genxGetVersion();
+LIBGENX_SYMEXPORT
+const char * genxGetVersion();
#ifdef __cplusplus
}
diff --git a/libgenx/libgenx/version.h.in b/libgenx/libgenx/version.h.in
new file mode 100644
index 0000000..92e990a
--- /dev/null
+++ b/libgenx/libgenx/version.h.in
@@ -0,0 +1,37 @@
+#ifndef LIBGENX_VERSION_H
+#define LIBGENX_VERSION_H
+
+/* The numeric version format is AAAAABBBBBCCCCCDDDE where:
+//
+// AAAAA - major version number
+// BBBBB - minor version number
+// CCCCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
+//
+// Version AAAAABBBBBCCCCCDDDE
+//
+// 0.1.0 0000000001000000000
+// 0.1.2 0000000001000020000
+// 1.2.3 0000100002000030000
+// 2.2.0-a.1 0000200001999990010
+// 3.0.0-b.2 0000299999999995020
+// 2.2.0-a.1.z 0000200001999990011
+*/
+#define LIBGENX_VERSION $libgenx.version.project_number$ULL
+#define LIBGENX_VERSION_STR "$libgenx.version.project$"
+#define LIBGENX_VERSION_ID "$libgenx.version.project_id$"
+#define LIBGENX_VERSION_FULL "$libgenx.version$"
+
+#define LIBGENX_VERSION_MAJOR $libgenx.version.major$
+#define LIBGENX_VERSION_MINOR $libgenx.version.minor$
+#define LIBGENX_VERSION_PATCH $libgenx.version.patch$
+
+#define LIBGENX_PRE_RELEASE $libgenx.version.pre_release$
+
+#define LIBGENX_SNAPSHOT_SN $libgenx.version.snapshot_sn$ULL
+#define LIBGENX_SNAPSHOT_ID "$libgenx.version.snapshot_id$"
+
+#endif /* LIBGENX_VERSION_H */
diff --git a/libgenx/manifest b/libgenx/manifest
new file mode 100644
index 0000000..4f27c59
--- /dev/null
+++ b/libgenx/manifest
@@ -0,0 +1,12 @@
+: 1
+name: libgenx
+version: 0.1.0-a.0.z
+project: genx
+summary: Genx XML serializer C library
+license: MIT
+description-file: README.md
+url: https://git.codesynthesis.com/cgit/genx
+email: boris@codesynthesis.com
+builds: all
+depends: * build2 >= 0.13.0
+depends: * bpkg >= 0.13.0
diff --git a/libgenx/tests/.gitignore b/libgenx/tests/.gitignore
new file mode 100644
index 0000000..662178d
--- /dev/null
+++ b/libgenx/tests/.gitignore
@@ -0,0 +1,8 @@
+# Test executables.
+#
+driver
+
+# Testscript output directories (can be symlinks).
+#
+test
+test-*
diff --git a/libgenx/tests/basics/buildfile b/libgenx/tests/basics/buildfile
new file mode 100644
index 0000000..75fb5e8
--- /dev/null
+++ b/libgenx/tests/basics/buildfile
@@ -0,0 +1,3 @@
+import libs = libgenx%lib{genx}
+
+exe{driver}: {h c}{**} $libs testscript
diff --git a/libgenx/tests/basics/driver.c b/libgenx/tests/basics/driver.c
new file mode 100644
index 0000000..135d5ba
--- /dev/null
+++ b/libgenx/tests/basics/driver.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <libgenx/version.h>
+#include <libgenx/genx.h>
+
+static genxStatus
+genxSend(void * d, constUtf8 s)
+{
+ size_t n = strlen((const char*)s);
+ return fwrite(s, 1, n, (FILE*)d) == n ? GENX_SUCCESS : GENX_IO_ERROR;
+}
+
+static genxStatus
+genxSendBounded(void * d, constUtf8 start, constUtf8 end)
+{
+ size_t n = end - start;
+ return fwrite(start, 1, n, (FILE*)d) == n ? GENX_SUCCESS : GENX_IO_ERROR;
+}
+
+static genxStatus
+genxFlush(void * d)
+{
+ return fflush((FILE*)d) == 0 ? GENX_SUCCESS : GENX_IO_ERROR;
+}
+
+static const genxSender stdioSender = {&genxSend, &genxSendBounded, genxFlush};
+
+int main()
+{
+ genxWriter w = genxNew(NULL, NULL, stdout);
+ genxStartDocSender(w, &stdioSender);
+ genxStartElementLiteral(w, (utf8)"http://example.org/1", (utf8)"greeting");
+ genxAddAttributeLiteral(w, (utf8)"http://example.com/2", (utf8)"type", (utf8)"well-formed");
+ genxAddText(w, (utf8)"Hello world!");
+ genxEndElement(w);
+ genxEndDocument(w);
+ genxDispose(w);
+
+ return 0;
+}
diff --git a/libgenx/tests/basics/testscript b/libgenx/tests/basics/testscript
new file mode 100644
index 0000000..2bbfba1
--- /dev/null
+++ b/libgenx/tests/basics/testscript
@@ -0,0 +1,3 @@
+$* >>EOO
+<g1:greeting xmlns:g2="http://example.com/2" g2:type="well-formed" xmlns:g1="http://example.org/1">Hello world!</g1:greeting>
+EOO
diff --git a/libgenx/tests/build/.gitignore b/libgenx/tests/build/.gitignore
new file mode 100644
index 0000000..974e01d
--- /dev/null
+++ b/libgenx/tests/build/.gitignore
@@ -0,0 +1,4 @@
+/config.build
+/root/
+/bootstrap/
+build/
diff --git a/libgenx/tests/build/bootstrap.build b/libgenx/tests/build/bootstrap.build
new file mode 100644
index 0000000..a07b5ea
--- /dev/null
+++ b/libgenx/tests/build/bootstrap.build
@@ -0,0 +1,5 @@
+project = # Unnamed tests subproject.
+
+using config
+using test
+using dist
diff --git a/libgenx/tests/build/root.build b/libgenx/tests/build/root.build
new file mode 100644
index 0000000..3b975e6
--- /dev/null
+++ b/libgenx/tests/build/root.build
@@ -0,0 +1,12 @@
+using c
+
+h{*}: extension = h
+c{*}: extension = c
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# The test target for cross-testing (running tests under Wine, etc).
+#
+test.target = $c.target
diff --git a/libgenx/tests/buildfile b/libgenx/tests/buildfile
new file mode 100644
index 0000000..aeeab15
--- /dev/null
+++ b/libgenx/tests/buildfile
@@ -0,0 +1 @@
+./: {*/ -build/}
diff --git a/makefile b/makefile
deleted file mode 100644
index 0625c7a..0000000
--- a/makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Make sure everything compiles.
-#
-src := genx.c char-props.c
-
-CC := gcc
-CFLAGS := -Wall -Wextra -Werror
-
-# Compile.
-#
-.PHONY: all
-all: $(src:.c=.o)
-
-%.o: %.c
- $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-# Clean.
-#
-.PHONY: clean
-clean:
- rm -f $(src:.c=.o)
diff --git a/packages.manifest b/packages.manifest
new file mode 100644
index 0000000..9d3a955
--- /dev/null
+++ b/packages.manifest
@@ -0,0 +1,2 @@
+: 1
+location: libgenx/