Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
283a5648a8 | |||
8641748274 | |||
134f2aa3fb | |||
af30c9e597 | |||
9bfd637a19 |
13 changed files with 782 additions and 53 deletions
|
@ -1,12 +1,11 @@
|
||||||
# SConstruct
|
# SConstruct
|
||||||
# vim: set ft=python:
|
# vim: set ft=python:
|
||||||
|
#
|
||||||
|
# Toplevel Scons build script. This should be mostly complete and generic enough
|
||||||
|
# for most builds.
|
||||||
|
#
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
'''
|
|
||||||
Toplevel Scons build script. This should be mostly complete and generic enough
|
|
||||||
for most builds.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
4
lib/core/SConscript
Normal file
4
lib/core/SConscript
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# SConscript
|
||||||
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
Library('erw', ['src/File.cc'])
|
112
lib/core/include/core/File.hh
Normal file
112
lib/core/include/core/File.hh
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/* File.hh
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* File interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "String.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
|
||||||
|
struct File
|
||||||
|
{
|
||||||
|
enum class SeekFrom {
|
||||||
|
/** Seek from the beginning of the file. */
|
||||||
|
Begin,
|
||||||
|
/** Seek from the current file offset. */
|
||||||
|
Here,
|
||||||
|
/** Seek from the end of the file. */
|
||||||
|
End,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::bitset<3> Mode;
|
||||||
|
|
||||||
|
static constexpr Mode In = Mode(1);
|
||||||
|
static constexpr Mode Out = Mode(2);
|
||||||
|
static constexpr Mode Binary = Mode(4);
|
||||||
|
|
||||||
|
/** Destructor. */
|
||||||
|
virtual ~File();
|
||||||
|
|
||||||
|
String path() const noexcept;
|
||||||
|
|
||||||
|
/** Seek to an absolute position in the file. */
|
||||||
|
virtual File& seek(size_t pos) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to an `offset` from the given anchor point in the file.
|
||||||
|
* @see SeekFrom
|
||||||
|
*/
|
||||||
|
virtual File& seek(ssize_t offset, SeekFrom from) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const String mPath;
|
||||||
|
|
||||||
|
/** Convert a File::Mode to an iostream openmode bitset. */
|
||||||
|
virtual std::ios_base::openmode modeToIOSMode(Mode mode);
|
||||||
|
|
||||||
|
File(const String& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** File handle for reading. */
|
||||||
|
struct InFile
|
||||||
|
: public File
|
||||||
|
{
|
||||||
|
/** Open a file at `path` for reading. */
|
||||||
|
InFile(const String& path, File::Mode mode);
|
||||||
|
|
||||||
|
/** Deleted copy constructor. File handles cannot be copied. */
|
||||||
|
InFile(const InFile& other) = delete;
|
||||||
|
|
||||||
|
/** Move `other` to this InFile. */
|
||||||
|
InFile(InFile&& other);
|
||||||
|
|
||||||
|
virtual ~InFile();
|
||||||
|
|
||||||
|
/** Move `other` to this InFile. File handles cannot be copied. */
|
||||||
|
InFile& operator=(InFile& other);
|
||||||
|
|
||||||
|
String path() const;
|
||||||
|
|
||||||
|
/** Read up to `count` characters into the provided `buffer`. */
|
||||||
|
InFile& read(char* buffer, ssize_t count);
|
||||||
|
|
||||||
|
/** @see File::seek */
|
||||||
|
InFile& seek(size_t pos) override;
|
||||||
|
|
||||||
|
/** @See File::seek */
|
||||||
|
InFile& seek(ssize_t pos, File::SeekFrom from) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String mPath;
|
||||||
|
std::ifstream mStream;
|
||||||
|
|
||||||
|
std::ios_base::openmode modeToIOSMode(File::Mode mode) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct OutFile
|
||||||
|
: public File
|
||||||
|
{
|
||||||
|
/** Write `count` characters from the provided `buffer` into this file. */
|
||||||
|
InFile& write(char* buffer, ssize_t count);
|
||||||
|
|
||||||
|
/** @see File::seek */
|
||||||
|
InFile& seek(size_t pos) override;
|
||||||
|
|
||||||
|
/** @See File::seek */
|
||||||
|
InFile& seek(ssize_t pos, File::SeekFrom from) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ofstream stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace erw */
|
16
lib/core/include/core/String.hh
Normal file
16
lib/core/include/core/String.hh
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/* String.hh
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Strings are fun.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
|
||||||
|
typedef std::string String;
|
||||||
|
|
||||||
|
} /* namespace erw */
|
47
lib/core/include/core/xml/Document.hh
Normal file
47
lib/core/include/core/xml/Document.hh
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* XMLDocument.hh
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* An XML document and related structures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/File.hh"
|
||||||
|
#include "core/String.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
namespace xml {
|
||||||
|
|
||||||
|
struct Document;
|
||||||
|
|
||||||
|
|
||||||
|
/** An XML document. */
|
||||||
|
struct Document
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor. Parse an XML document out of the given file. Doing so takes
|
||||||
|
* ownership of the file.
|
||||||
|
*/
|
||||||
|
Document(InFile&& file);
|
||||||
|
|
||||||
|
/** Constructor. Parse an XML document from the given string. */
|
||||||
|
Document(const String& string);
|
||||||
|
|
||||||
|
~Document();
|
||||||
|
|
||||||
|
const Node& root() const noexcept;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** The root of the XML tree. The document owns its root. */
|
||||||
|
Node mRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace xml */
|
||||||
|
} /* namespace erw */
|
41
lib/core/include/core/xml/Node.hh
Normal file
41
lib/core/include/core/xml/Node.hh
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/* Node.hh
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* An XML node.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "String.hh"
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
namespace xml {
|
||||||
|
|
||||||
|
/** A node in an XML tree. */
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
typedef std::vector<Node> List;
|
||||||
|
typedef std::map<String, String> AttributeMap;
|
||||||
|
|
||||||
|
Node();
|
||||||
|
Node(const String& name, const List& children);
|
||||||
|
Node(const Node& other);
|
||||||
|
~Node();
|
||||||
|
|
||||||
|
String name() const noexcept;
|
||||||
|
List children() const noexcept;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** The name of the node. */
|
||||||
|
String mName;
|
||||||
|
/** Children of this node. The node owns its children. */
|
||||||
|
List mChildren;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace xml */
|
||||||
|
} /* namespace erw */
|
162
lib/core/src/File.cc
Normal file
162
lib/core/src/File.cc
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/* File.cc
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Implementation of file handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/File.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
|
||||||
|
#pragma mark - File
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File::File --
|
||||||
|
*/
|
||||||
|
File::File(const String& path)
|
||||||
|
: mPath(path)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File::~File --
|
||||||
|
*/
|
||||||
|
File::~File()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File::path --
|
||||||
|
*/
|
||||||
|
String
|
||||||
|
File::path()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
return mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File::modeToIOSMode --
|
||||||
|
*/
|
||||||
|
std::ios_base::openmode
|
||||||
|
File::modeToIOSMode(Mode mode)
|
||||||
|
{
|
||||||
|
std::ios_base::openmode openmode = 0;
|
||||||
|
if ((mode & In).any()) {
|
||||||
|
openmode |= std::ios_base::in;
|
||||||
|
}
|
||||||
|
if ((mode & Out).any()) {
|
||||||
|
openmode |= std::ios_base::out;
|
||||||
|
}
|
||||||
|
if ((mode & Binary).any()) {
|
||||||
|
openmode |= std::ios_base::binary;
|
||||||
|
}
|
||||||
|
return openmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - InFile
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::InFile --
|
||||||
|
*/
|
||||||
|
InFile::InFile(const String& path,
|
||||||
|
InFile::Mode mode)
|
||||||
|
: File(path),
|
||||||
|
mStream(path, modeToIOSMode(mode))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::InFile --
|
||||||
|
*/
|
||||||
|
InFile::InFile(InFile&& other)
|
||||||
|
: File(other.mPath),
|
||||||
|
mStream(std::move(other.mStream))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::~InFile --
|
||||||
|
*/
|
||||||
|
InFile::~InFile()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::operator= --
|
||||||
|
*/
|
||||||
|
InFile&
|
||||||
|
InFile::operator=(InFile& other)
|
||||||
|
{
|
||||||
|
mStream = std::move(other.mStream);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::read --
|
||||||
|
*/
|
||||||
|
InFile&
|
||||||
|
InFile::read(char* buffer,
|
||||||
|
ssize_t count)
|
||||||
|
{
|
||||||
|
mStream.read(buffer, count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::seek --
|
||||||
|
*/
|
||||||
|
InFile&
|
||||||
|
InFile::seek(size_t pos)
|
||||||
|
{
|
||||||
|
mStream.seekg(pos);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::seek --
|
||||||
|
*/
|
||||||
|
InFile&
|
||||||
|
InFile::seek(ssize_t off,
|
||||||
|
File::SeekFrom from)
|
||||||
|
{
|
||||||
|
std::ios_base::seekdir dir;
|
||||||
|
switch (from) {
|
||||||
|
case File::SeekFrom::Begin:
|
||||||
|
dir = std::ios_base::beg;
|
||||||
|
break;
|
||||||
|
case File::SeekFrom::Here:
|
||||||
|
dir = std::ios_base::cur;
|
||||||
|
break;
|
||||||
|
case File::SeekFrom::End:
|
||||||
|
dir = std::ios_base::end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mStream.seekg(off, dir);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InFile::modeToIOSMode --
|
||||||
|
*/
|
||||||
|
std::ios_base::openmode
|
||||||
|
InFile::modeToIOSMode(File::Mode mode)
|
||||||
|
{
|
||||||
|
// Ensure In flag is always set.
|
||||||
|
if (!(mode & File::In).any()) {
|
||||||
|
mode |= File::In;
|
||||||
|
}
|
||||||
|
return File::modeToIOSMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - OutFile
|
||||||
|
|
||||||
|
} /* namespace erw */
|
275
lib/core/src/xml/Document.cc
Normal file
275
lib/core/src/xml/Document.cc
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
/* XMLParser.cc
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Implementation of an XML parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
#include "XMLParser.hh"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initLibrary --
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
initLibrary()
|
||||||
|
{
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, []() {
|
||||||
|
xmlInitParser();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parseFile --
|
||||||
|
*/
|
||||||
|
XMLNode
|
||||||
|
parseFile(File&& file)
|
||||||
|
{
|
||||||
|
const size_t kInitialBufferSize = 16;
|
||||||
|
const size_t kBufferSize = 1024;
|
||||||
|
|
||||||
|
static_assert(kInitialBufferSize < kBufferSize,
|
||||||
|
"XML parser initial buffer size must be smaller than the "
|
||||||
|
"total buffer size");
|
||||||
|
|
||||||
|
initLibrary();
|
||||||
|
|
||||||
|
char buffer[kBufferSize];
|
||||||
|
ssize_t bytesRead = 0;
|
||||||
|
|
||||||
|
bytesRead = file.read(buffer, kInitialBufferSize);
|
||||||
|
xmlParserCtxtPtr context = xmlCreatePushParserCtxt(nullptr, nullptr,
|
||||||
|
buffer, bytesRead,
|
||||||
|
file.path().c_str());
|
||||||
|
if (!context) {
|
||||||
|
// TODO: Throw an appropriate error...
|
||||||
|
throw 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read chunks until we're done. Once all data has been read, indicate that
|
||||||
|
* the parser should terminate by calling xmlParseChunk() with a last
|
||||||
|
* argument of 1 rather than 0.
|
||||||
|
*/
|
||||||
|
while ((bytesRead = file.read(buffer, kBufferSize)) > 0) {
|
||||||
|
xmlParseChunk(context, buffer, bytesRead, 0);
|
||||||
|
}
|
||||||
|
xmlParseChunk(context, buffer, 0, 1);
|
||||||
|
|
||||||
|
bool succeeded = bool(context->wellFormed);
|
||||||
|
xmlDocPtr document = context->myDoc;
|
||||||
|
|
||||||
|
xmlFreeParserCtxt(context);
|
||||||
|
|
||||||
|
return succeeded ? XMLDocument::UCPtr(new XML2Document(document)) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
|
||||||
|
struct XML2Node
|
||||||
|
: public XMLNode
|
||||||
|
{
|
||||||
|
XML2Node(xmlNodePtr node);
|
||||||
|
|
||||||
|
virtual ~XML2Node();
|
||||||
|
|
||||||
|
virtual String name() const noexcept override;
|
||||||
|
virtual String content() const noexcept override;
|
||||||
|
virtual XMLNode::AttributeMap attributes() const noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
xmlNodePtr mNode;
|
||||||
|
|
||||||
|
/** Make a list of children of the node, excluding text nodes. */
|
||||||
|
XMLNode::List childrenOfXML2Node(xmlNodePtr node) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** An XML parser that uses libxml2. */
|
||||||
|
struct XML2Document
|
||||||
|
: public XMLDocument
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialize the xml2 library, if needed. This function may be called more
|
||||||
|
* than once with no adverse affects.
|
||||||
|
*/
|
||||||
|
static void initLibrary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a file into a libxml2 document object. Note: because of move
|
||||||
|
* semantics related to the file UPtr, the file will be closed after this
|
||||||
|
* method completes.
|
||||||
|
*
|
||||||
|
* @param [in] file The file to parse.
|
||||||
|
* @return A libxml2 document, or nullptr if the parse fails.
|
||||||
|
*/
|
||||||
|
static XMLDocument::UCPtr parseFile(File::UPtr file);
|
||||||
|
|
||||||
|
static XMLDocument::UCPtr parseString(const String& string);
|
||||||
|
|
||||||
|
XML2Document(xmlDocPtr document);
|
||||||
|
|
||||||
|
virtual ~XML2Document();
|
||||||
|
|
||||||
|
virtual XMLNode::WCPtr root() const noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
xmlDocPtr mDocument;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma mark - erw::XMLDocument
|
||||||
|
|
||||||
|
/* static */ XMLDocument::UCPtr
|
||||||
|
XMLDocument::parseFile(File::UPtr file)
|
||||||
|
{
|
||||||
|
return XML2Document::parseFile(std::move(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ XMLDocument::UCPtr
|
||||||
|
XMLDocument::parseString(const String& string)
|
||||||
|
{
|
||||||
|
return XML2Document::parseString(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XMLDocument(InFile&& file)
|
||||||
|
: mRoot()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
XMLDocument::~XMLDocument()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
#pragma mark - erw::XML2Document
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ XMLDocument::UCPtr
|
||||||
|
XML2Document::parseString(const String& string)
|
||||||
|
{
|
||||||
|
initLibrary();
|
||||||
|
xmlDocPtr document = xmlReadMemory(string.c_str(), string.size(), "memory.xml", NULL, 0);
|
||||||
|
return document ? XMLDocument::UCPtr(new XML2Document(document)) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XML2Document::XML2Document(xmlDocPtr document)
|
||||||
|
: XMLDocument(XMLNode::Ptr(new XML2Node(xmlDocGetRootElement(document)))),
|
||||||
|
mDocument(document)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
XML2Document::~XML2Document()
|
||||||
|
{
|
||||||
|
if (mDocument) {
|
||||||
|
xmlFreeDoc(mDocument);
|
||||||
|
mDocument = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XMLNode::WCPtr
|
||||||
|
XML2Document::root()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
return mRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - XMLNode
|
||||||
|
|
||||||
|
XMLNode::~XMLNode()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
XMLNode::XMLNode(XMLNode::List&& children)
|
||||||
|
: mChildren(children)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
XMLNode::WCList
|
||||||
|
XMLNode::children()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
WCList weakChildren;
|
||||||
|
for (Ptr child : mChildren) {
|
||||||
|
weakChildren.push_back(WCPtr(child));
|
||||||
|
}
|
||||||
|
return weakChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - XML2Node
|
||||||
|
|
||||||
|
XML2Node::XML2Node(xmlNodePtr node)
|
||||||
|
: XMLNode(childrenOfXML2Node(node)),
|
||||||
|
mNode(node)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
XML2Node::~XML2Node()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
String
|
||||||
|
XML2Node::name()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
return (const char *)mNode->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String
|
||||||
|
XML2Node::content()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
xmlChar *content = xmlNodeGetContent(mNode);
|
||||||
|
String contentString((const char *)content);
|
||||||
|
xmlFree(content);
|
||||||
|
return contentString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XMLNode::AttributeMap
|
||||||
|
XML2Node::attributes()
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
AttributeMap attrs;
|
||||||
|
for (xmlAttrPtr attr = mNode->properties; attr && attr->name && attr->children; attr = attr->next) {
|
||||||
|
xmlChar *value = xmlNodeListGetString(mNode->doc, attr->children, 1);
|
||||||
|
attrs[(const char *)attr->name] = (const char *)value;
|
||||||
|
xmlFree(value);
|
||||||
|
}
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XMLNode::List
|
||||||
|
XML2Node::childrenOfXML2Node(xmlNodePtr node)
|
||||||
|
const noexcept
|
||||||
|
{
|
||||||
|
XMLNode::List children;
|
||||||
|
for (xmlNodePtr c = node->children; c != nullptr; c = c->next) {
|
||||||
|
if (c->type != XML_ELEMENT_NODE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
children.push_back(XMLNode::Ptr(new XML2Node(c)));
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace erw */
|
76
lib/core/src/xml/Node.cc
Normal file
76
lib/core/src/xml/Node.cc
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/* XMLNode.cc
|
||||||
|
* vim: set tw=80:
|
||||||
|
* Eryn Wells <eryn@erynwells.me>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Implementation of a node in an XML tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/XMLDocument.hh"
|
||||||
|
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace erw {
|
||||||
|
namespace xml {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::Node --
|
||||||
|
*/
|
||||||
|
Node::Node()
|
||||||
|
: mName(),
|
||||||
|
mChildren()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::Node --
|
||||||
|
*/
|
||||||
|
Node::Node(const String& name,
|
||||||
|
const List& children)
|
||||||
|
: mName(name),
|
||||||
|
mChildren(children)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::Node --
|
||||||
|
*/
|
||||||
|
Node::Node(const Node& other)
|
||||||
|
: mName(other.name),
|
||||||
|
mChildren(other.children)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::~Node --
|
||||||
|
*/
|
||||||
|
Node::~Node()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
#pragma mark Properties
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::name --
|
||||||
|
*/
|
||||||
|
String
|
||||||
|
Node::name()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node::children --
|
||||||
|
*/
|
||||||
|
List
|
||||||
|
Node::children()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return mChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace xml */
|
||||||
|
} /* namespace erw */
|
30
site_scons/paths.py
Normal file
30
site_scons/paths.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# paths.py
|
||||||
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
def is_executable(path):
|
||||||
|
return os.path.exists(path) and os.access(path, os.X_OK)
|
||||||
|
|
||||||
|
|
||||||
|
def which(program):
|
||||||
|
'''
|
||||||
|
Look for `program` in system path and return the full path to that binary if
|
||||||
|
it is found. Otherwise, return `None`.
|
||||||
|
'''
|
||||||
|
path, name = os.path.split(program)
|
||||||
|
if path:
|
||||||
|
if is_executable(program):
|
||||||
|
return program
|
||||||
|
else:
|
||||||
|
pathext = [''] + os.environ.get('PATHEXT', '').split(os.pathsep)
|
||||||
|
for path in os.environ.get('PATH', '').split(os.pathsep):
|
||||||
|
exe = os.path.join(path, program)
|
||||||
|
for ext in pathext:
|
||||||
|
candidate = exe + ext
|
||||||
|
if is_executable(candidate):
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import SCons.Environment
|
import SCons.Environment
|
||||||
import SCons.Errors
|
import SCons.Errors
|
||||||
|
|
||||||
|
import paths
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(level=logging.DEBUG):
|
def setup_logging(level=logging.DEBUG):
|
||||||
'''Configure global logging for the SCons system.'''
|
'''Configure global logging for the SCons system.'''
|
||||||
|
@ -102,9 +105,6 @@ class Environment(SCons.Environment.Environment):
|
||||||
if colorful and sys.stdout.isatty():
|
if colorful and sys.stdout.isatty():
|
||||||
if 'clang' in self['CC'] or 'clang' in self['CXX']:
|
if 'clang' in self['CC'] or 'clang' in self['CXX']:
|
||||||
self.AppendUnique(CCFLAGS=['-fcolor-diagnostics'])
|
self.AppendUnique(CCFLAGS=['-fcolor-diagnostics'])
|
||||||
elif 'gcc' in self['CC'] or 'g++' in self['CXX']:
|
|
||||||
# TODO: Also set a GCC_COLORS variable in the system environment?
|
|
||||||
self.AppendUnique(CCFLAGS=['-fdiagnostics-color=always'])
|
|
||||||
|
|
||||||
# Pretty printing
|
# Pretty printing
|
||||||
self.SetDefault(ARCOMSTR=Environment._comstr('Archiving', succinct))
|
self.SetDefault(ARCOMSTR=Environment._comstr('Archiving', succinct))
|
||||||
|
@ -135,6 +135,8 @@ class Environment(SCons.Environment.Environment):
|
||||||
|
|
||||||
def process_src(self):
|
def process_src(self):
|
||||||
out_dir = self.build_root.Dir('src')
|
out_dir = self.build_root.Dir('src')
|
||||||
|
# TODO: Do the thing.
|
||||||
|
# do_sconscript(env, env.source_root, src_out_dir)
|
||||||
self.SConscript(self.src_root.File('SConscript'),
|
self.SConscript(self.src_root.File('SConscript'),
|
||||||
variant_dir=out_dir)
|
variant_dir=out_dir)
|
||||||
self.Append(CPPPATH=[self.src_root])
|
self.Append(CPPPATH=[self.src_root])
|
||||||
|
@ -182,7 +184,7 @@ class Environment(SCons.Environment.Environment):
|
||||||
def _append_custom_tools(self, kwargs):
|
def _append_custom_tools(self, kwargs):
|
||||||
'''Add custom tools to the `kwargs`.'''
|
'''Add custom tools to the `kwargs`.'''
|
||||||
tools = kwargs.setdefault('tools', ['default'])
|
tools = kwargs.setdefault('tools', ['default'])
|
||||||
for tool in ['lib', 'test', 'program', 'sconscript', 'swiftc']:
|
for tool in ['lib', 'test', 'program', 'sconscript']:
|
||||||
if tool in tools:
|
if tool in tools:
|
||||||
continue
|
continue
|
||||||
tools.append(tool)
|
tools.append(tool)
|
||||||
|
|
|
@ -9,20 +9,18 @@ import SCons.Script
|
||||||
|
|
||||||
def _do_sconscript(env):
|
def _do_sconscript(env):
|
||||||
original_sconscript = env.SConscript
|
original_sconscript = env.SConscript
|
||||||
|
|
||||||
def sconscript(env, sconscript, clone=False, *args, **kwargs):
|
def sconscript(env, sconscript, clone=False, *args, **kwargs):
|
||||||
exports = {
|
exports = {'Library': env.Library,
|
||||||
'Library': env.Library,
|
'StaticLibrary': env.StaticLibrary,
|
||||||
'Object': env.Object,
|
'SharedLibrary': env.SharedLibrary,
|
||||||
'SharedObject': env.SharedObject,
|
'Program': env.Program}
|
||||||
'StaticLibrary': env.StaticLibrary,
|
|
||||||
'SharedLibrary': env.SharedLibrary,
|
|
||||||
'Program': env.Program,
|
|
||||||
'env': env.Clone() if clone else env,
|
|
||||||
}
|
|
||||||
SCons.Script._SConscript.GlobalDict.update(exports)
|
SCons.Script._SConscript.GlobalDict.update(exports)
|
||||||
env.log('Reading {}'.format(sconscript))
|
env.log('Reading {}'.format(sconscript))
|
||||||
return original_sconscript(sconscript, {}, *args, **kwargs)
|
return original_sconscript(sconscript,
|
||||||
|
{'env': env.Clone() if clone else env},
|
||||||
|
*args,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
return sconscript
|
return sconscript
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
# swiftc.py
|
|
||||||
# vim: set ft=python:
|
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
'''
|
|
||||||
SCons plugin for building Swift files with swiftc.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import SCons.Action
|
|
||||||
import SCons.Tool
|
|
||||||
import SCons.Util
|
|
||||||
|
|
||||||
SwiftSuffix = '.swift'
|
|
||||||
SwiftAction = SCons.Action.Action("$SWIFTCCOM", "$SWIFTCCOMSTR")
|
|
||||||
|
|
||||||
compilers = ['swiftc']
|
|
||||||
|
|
||||||
def generate(env):
|
|
||||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
|
|
||||||
static_obj.add_action(SwiftSuffix, SwiftAction)
|
|
||||||
static_obj.add_emitter(SwiftSuffix, SCons.Defaults.SharedObjectEmitter)
|
|
||||||
shared_obj.add_action(SwiftSuffix, SwiftAction)
|
|
||||||
shared_obj.add_emitter(SwiftSuffix, SCons.Defaults.SharedObjectEmitter)
|
|
||||||
|
|
||||||
if 'SWIFTC' not in env:
|
|
||||||
compiler = env.Detect(compilers)
|
|
||||||
env['SWIFTC'] = compiler if compiler else compilers[0]
|
|
||||||
env['SWIFTFLAGS'] = SCons.Util.CLVar('')
|
|
||||||
env['SWIFTCCOM'] = '$SWIFTC -o $TARGET -c $SWIFTFLAGS $SOURCES'
|
|
||||||
env['SWIFTFILESUFFIX'] = SwiftSuffix
|
|
||||||
|
|
||||||
def exists(env):
|
|
||||||
return env.Detect(compilers)
|
|
Loading…
Add table
Add a link
Reference in a new issue