Demonstration of complete bootstrap, loading INI and resolving GUI module path

This commit is contained in:
Fischlurch 2011-02-04 16:10:59 +01:00
parent a42e6a5a8f
commit babbe33d1d
5 changed files with 86 additions and 45 deletions

View file

@ -348,6 +348,7 @@ def defineBuildTargets(env, artifacts):
artifacts['corelib'] = core artifacts['corelib'] = core
artifacts['support'] = lLib artifacts['support'] = lLib
artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True)
+ env.ConfigData(env.path.srcConf+'setup.ini')
+ env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini')
) )

View file

@ -1,4 +1,4 @@
/* This is an dummy Lumiera config file # This is an dummy Lumiera config file
* #
* Actually Lumiera can't yet load any config, as of 1/2011 # Actually Lumiera can't yet load any extended config, as of 1/2011
*/ #

20
data/config/setup.ini Normal file
View file

@ -0,0 +1,20 @@
###########################################################
# ### Lumiera installation and platform configuration ###
#
# (file located relative to the Lumiera executable)
#
[BuildsystemDemo]
# This is dummy/demonstration executable
# built from the "scons" git tree. It serves to
# document and evolve the SCons buildsystem in isolation
# and for tests of the packaging and build process.
#
gui = gtk_gui.lum
modulepath = $ORIGIN/modules
[Lumiera]
# Lumiera video editor main application
#
# Nothing configurable as of 1/2011
#

View file

@ -5,10 +5,6 @@
#ifndef LUMIERA_PLUGIN_PATH
#error please define the plugin search path as -DLUMIERA_PLUGIN_PATH, e.g. as $INSTALL_PREFIX/lib/lumiera
#endif
#include "common/dummy-func.hpp" #include "common/dummy-func.hpp"
@ -35,19 +31,22 @@ namespace lumiera {
using boost::regex_search; using boost::regex_search;
using boost::sregex_iterator; using boost::sregex_iterator;
typedef smatch::value_type const& SubMatch;
namespace fsys = boost::filesystem; namespace fsys = boost::filesystem;
namespace opt = boost::program_options; namespace opt = boost::program_options;
namespace { // Implementation helpers namespace { // Implementation helpers
const size_t STRING_MAX_RELEVANT = 1000; const size_t STRING_MAX_RELEVANT = 1000;
const char * const BOOTSTRAP_INI = "$ORIGIN/setup.ini"; const char * const BOOTSTRAP_INI = "$ORIGIN/config/setup.ini";
const char * const GUI_MODULE_TO_LOAD = "gtk_gui.lum";
const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe"; const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe";
regex EXTRACT_RELATIVE_PATHSPEC ("\\$?ORIGIN/([^:]+)"); regex EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)");
regex EXTRACT_PATHSPEC ("(\\$?ORIGIN)?([^:]+)");
/** the real application would throw a custom exception... */ /** the real application would throw a custom exception... */
@ -66,7 +65,7 @@ namespace lumiera {
catchMyself () catchMyself ()
{ {
static string buff(STRING_MAX_RELEVANT+1, '\0' ); static string buff(STRING_MAX_RELEVANT+1, '\0' );
if (!buff.size()) if (!buff[0])
{ {
ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT);
@ -79,20 +78,16 @@ namespace lumiera {
} }
/** extract from the PLUGIN_PATH a path specification
* given relative to the location of the executable,
* as denoted by the 'ORIGIN' token
*/
string
getRelativeModuleLocation()
{
smatch match;
if (regex_search (string(LUMIERA_PLUGIN_PATH), match, EXTRACT_RELATIVE_PATHSPEC))
return (match[1]);
else
dieHard ("no valid module loading location relative to executable defined in LUMIERA_PLUGIN_PATH");
}
/**
* Helper: Access a path Specification as a sequence of filesystem Paths.
* This iterator class dissects a ':'-separated path list. The individual
* components may use the symbol \c $ORIGIN to denote the directory holding
* the current executable. After resolving this symbol, a valid absolute or
* relative filesystem path should result, which must not denote an existing
* file (directory is OK).
* @note #fetch picks the current component and advances the iteration.
*/
class SearchPathSplitter class SearchPathSplitter
: boost::noncopyable : boost::noncopyable
{ {
@ -100,25 +95,26 @@ namespace lumiera {
end_; end_;
public: public:
SearchPathSplitter (string searchPath) SearchPathSplitter (string const& searchPath)
: pos_(searchPath.begin(),searchPath.end(), EXTRACT_PATHSPEC) : pos_(searchPath.begin(),searchPath.end(), EXTRACT_PATHSPEC)
, end_() , end_()
{ } { }
bool bool
hasNext() isValid() const
{ {
return pos_ != end_; return pos_ != end_;
} }
string string
next() fetch ()
{ {
++pos_; if (!isValid())
if (!hasNext())
dieHard ("Search path exhausted."); dieHard ("Search path exhausted.");
return resolveRelative(); string currentPathElement = resolveRelative();
++pos_;
return currentPathElement;
} }
private: private:
@ -133,7 +129,7 @@ namespace lumiera {
return getFullPath(); return getFullPath();
} }
smatch::value_type found(int group=0) { return (*pos_)[group]; } SubMatch found(int group=0) { return (*pos_)[group]; }
bool containsORIGINToken() { return found(1).matched; } bool containsORIGINToken() { return found(1).matched; }
string getRelativePath() { return found(2); } string getRelativePath() { return found(2); }
@ -146,13 +142,13 @@ namespace lumiera {
fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); fsys::path modPathName (exePathName.remove_leaf() / getRelativePath());
if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) if (fsys::exists(modPathName) && !fsys::is_directory (modPathName))
dieHard ("Error in search path: component"+modPathName.string()+"is not an existing directory"); dieHard ("Error in search path: component \""+modPathName.string()+"\" is not a directory");
return modPathName.string(); return modPathName.directory_string();
} }
}; };
/** helper to establish the location to search for loadable modules. /** helper to establish the location to search for loadable modules.
* This is a simple demonstration of the basic technique used in the * This is a simple demonstration of the basic technique used in the
* real application source to establish a plugin search path, based * real application source to establish a plugin search path, based
@ -174,22 +170,26 @@ namespace lumiera {
} }
// try / continue search path // try / continue search path
if (searchLocation.hasNext()) if (searchLocation.isValid())
modulePathName = fsys::path() / searchLocation.next() / moduleName; modulePathName = fsys::path() / searchLocation.fetch() / moduleName;
else else
dieHard ("Module \""+moduleName+"\" not found" dieHard ("Module \""+moduleName+"\" not found"
+ (searchPath.empty()? ".":" in search path: "+searchPath)); + (searchPath.empty()? ".":" in search path: "+searchPath));
} } }
}
/**
* Encapsulate an INI-style configuration file.
* The acceptable settings are defined in the ctor.
* Implementation based on boost::program_options
*/
class Config class Config
: boost::noncopyable : boost::noncopyable
{ {
opt::options_description syntax; opt::options_description syntax;
opt::variables_map settings; opt::variables_map settings;
public: public:
Config (string bootstrapIni) Config (string bootstrapIni)
: syntax("Lumiera installation and platform configuration") : syntax("Lumiera installation and platform configuration")
@ -203,8 +203,8 @@ namespace lumiera {
"May us $ORIGIN to refer to the EXE location") "May us $ORIGIN to refer to the EXE location")
; ;
ifstream configIn (resolveModulePath (bootstrapIni).c_str()); ifstream configIn (resolve(bootstrapIni).c_str());
opt::parsed_options parsed = opt::parsed_options parsed =
opt::parse_config_file (configIn, syntax); opt::parse_config_file (configIn, syntax);
@ -218,6 +218,15 @@ namespace lumiera {
{ {
return settings[key].as<string>(); return settings[key].as<string>();
} }
private:
string
resolve (fsys::path iniSpec)
{
string file = iniSpec.leaf();
string searchpath = iniSpec.branch_path().string();
return resolveModulePath (file, searchpath);
}
}; };
}//(End) implementation helpers }//(End) implementation helpers
@ -233,7 +242,7 @@ namespace lumiera {
{ {
Config appConfig(BOOTSTRAP_INI); Config appConfig(BOOTSTRAP_INI);
string guiModule = appConfig["BuildsystemDemo.gui"]; string guiModule = appConfig["BuildsystemDemo.gui"];
string moduleSearch = appConfig["BuildsystemDemo.modulepath"]; string moduleSearch = appConfig["BuildsystemDemo.modulepath"];
string moduleLocation = resolveModulePath (guiModule, moduleSearch); string moduleLocation = resolveModulePath (guiModule, moduleSearch);
void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL); void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL);

View file

@ -20,6 +20,17 @@ namespace lumiera {
* which attempts to load the "pseudo-gui" as shared module * which attempts to load the "pseudo-gui" as shared module
* and invoke the gui-main. The sole purpose of this function * and invoke the gui-main. The sole purpose of this function
* is to demonstrate that the SCons build system is working. * is to demonstrate that the SCons build system is working.
*
* \par requirements
* While this isn't the actual implementation used in Lumiera,
* we try to mimic or demonstrate the techniques used to resolve
* the actual module to be loaded. So there are some requirements
* - \c $ORIGIN/config/setup.ini exists and defines...
* - a section [BuildsystemDemo], which holds
* - a setting gui = gtk_gui.lum
* - a module search path, typically: modulepath = $ORIGIN/modules
* Here the token \c $ORIGIN is automatically resolved to the directory
* holding the current executable, by reading the symlink \c /proc/self/exe
*/ */
void loadDummyGui(); void loadDummyGui();