diff --git a/src/common/appstate.cpp b/src/common/appstate.cpp index 20d2c6e96..391a550f7 100644 --- a/src/common/appstate.cpp +++ b/src/common/appstate.cpp @@ -34,6 +34,7 @@ extern "C" { #include "common/plugin.h" } +#include "lib/symbol.hpp" #include "lib/util.hpp" #include "include/configfacade.hpp" //////////TODO: temp hack to force configfacade.o to be linked in @@ -59,6 +60,7 @@ namespace lumiera { LifecycleHook schedule_ (ON_BASIC_INIT, &createAppStateInstance); + lib::Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); } diff --git a/src/common/bootstrap.cpp b/src/common/bootstrap.cpp index 35f2d20cc..12eb75e01 100644 --- a/src/common/bootstrap.cpp +++ b/src/common/bootstrap.cpp @@ -6,6 +6,7 @@ +#include "lib/error.hpp" #include "common/bootstrap.hpp" extern "C" { diff --git a/src/common/bootstrap.hpp b/src/common/bootstrap.hpp index 43e43726a..aa2a41f0b 100644 --- a/src/common/bootstrap.hpp +++ b/src/common/bootstrap.hpp @@ -7,8 +7,6 @@ #ifndef COMMON_DUMMY_FUNC_H #define COMMON_DUMMY_FUNC_H -#include "include/nobugcfg.h" - #include diff --git a/src/common/option.cpp b/src/common/option.cpp index d01afc88d..2be4a8d46 100644 --- a/src/common/option.cpp +++ b/src/common/option.cpp @@ -33,7 +33,7 @@ typedef boost::program_options::variables_map VarMap; namespace op = boost::program_options; -using util::VectS; +using lib::VectS; using util::cStr; @@ -50,7 +50,7 @@ namespace lumiera { * \endcode * @todo describe the actual options */ - Option::Option (util::Cmdline& cmdline) + Option::Option (lib::Cmdline& cmdline) : syntax("Lumiera, the non linear video editor. Supported parameters"), parameters() { diff --git a/src/common/option.hpp b/src/common/option.hpp index a6ccc4ab9..012844ea3 100644 --- a/src/common/option.hpp +++ b/src/common/option.hpp @@ -37,13 +37,13 @@ namespace lumiera { using std::string; using std::ostream; - using util::VectS; + using lib::VectS; /** - * Support for selecting and configuring testcases - * via commandline arguments. A preconfigured wrapper + * Frontend for handling the Lumiera application + * commandline arguments. A preconfigured wrapper * around boost::program_options, with the ability * to tolerate unknown options. The commandline * to be parsed is taken wrapped into a Cmdline @@ -54,7 +54,7 @@ namespace lumiera { class Option : private boost::noncopyable { public: - Option (util::Cmdline& cmdline); + Option (lib::Cmdline& cmdline); const string getSessName(); const VectS getScripts(); diff --git a/src/gui/guistart.cpp b/src/gui/guistart.cpp index 6eadf445c..d5f072058 100644 --- a/src/gui/guistart.cpp +++ b/src/gui/guistart.cpp @@ -3,8 +3,8 @@ Copyright (C) Lumiera.org 2007-2008, Joel Holdsworth - 2009, Christian Thaeter - Hermann Vosseler + 2009, Hermann Vosseler + Christian Thaeter This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/lib/cmdline.cpp b/src/lib/cmdline.cpp index 8539909ec..bffbd2e8e 100644 --- a/src/lib/cmdline.cpp +++ b/src/lib/cmdline.cpp @@ -40,11 +40,12 @@ using boost::regex; using boost::smatch; using boost::regex_search; +using util::noneg; #include -namespace util { +namespace lib { /** create as a tokenised copy of the current commandline. @@ -88,4 +89,4 @@ namespace util { -} // namespace util +} // namespace lib diff --git a/src/lib/cmdline.hpp b/src/lib/cmdline.hpp index 88d6e2d5a..f65db650b 100644 --- a/src/lib/cmdline.hpp +++ b/src/lib/cmdline.hpp @@ -21,8 +21,8 @@ */ -#ifndef UTIL_CMDLINE_H -#define UTIL_CMDLINE_H +#ifndef LIB_CMDLINE_H +#define LIB_CMDLINE_H #include #include @@ -30,7 +30,7 @@ -namespace util { +namespace lib { using std::string; using std::vector; @@ -66,5 +66,5 @@ namespace util { -} // namespace util +} // namespace lib #endif diff --git a/src/lib/error.c b/src/lib/error.c index d91a27359..322fa2126 100644 --- a/src/lib/error.c +++ b/src/lib/error.c @@ -33,7 +33,7 @@ predefined errors */ LUMIERA_ERROR_DEFINE (ERRNO, "errno"); -LUMIERA_ERROR_DEFINE (EERROR, "could not initialize error system"); +LUMIERA_ERROR_DEFINE (EERROR, "could not initialise error system"); LUMIERA_ERROR_DEFINE (UNKNOWN, "unknown error"); @@ -78,7 +78,7 @@ lumiera_error_get (void) LumieraErrorcontext self = pthread_getspecific (lumiera_error_tls); if (!self) { - /* malloc() and not lumiera_malloc() here because nothing else might be initialized when calling this */ + /* malloc() and not lumiera_malloc() here because nothing else might be initialised when calling this */ self = malloc (sizeof *self); if (!self) LUMIERA_DIE (EERROR); diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp new file mode 100644 index 000000000..02f9f2e0a --- /dev/null +++ b/src/lib/searchpath.cpp @@ -0,0 +1,94 @@ +/* + Searchpath - helpers for searching directory lists and locating modules + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "lib/searchpath.hpp" +#include "lib/symbol.hpp" + + +namespace lib { + + + LUMIERA_ERROR_DEFINE (FILE_NOT_DIRECTORY, "path element points at a file instead of a directory"); + + + + namespace { + + Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); + } + + + + regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); + + + + + /** @internal helper to figure out the installation directory, + * as given by the absolute path of the currently executing program + * @warning this is Linux specific code */ + string + findExePath() + { + static string buff(lib::STRING_MAX_RELEVANT+1, '\0' ); + if (!buff[0]) + { + ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], lib::STRING_MAX_RELEVANT); + + if (0 > chars_read || chars_read == ssize_t(lib::STRING_MAX_RELEVANT)) + throw error::Fatal ("unable to discover path of running executable"); + + buff.resize(chars_read); + } + return buff; + } + + + + + string + resolveModulePath (string moduleName, string searchPath) + { + fsys::path modulePathName (moduleName); + SearchPathSplitter searchLocation(searchPath); + + while (true) + { + if (fsys::exists (modulePathName)) + { + INFO (config, "found module %s", modulePathName.string().c_str()); + return modulePathName.string(); + } + + // try / continue search path + if (searchLocation.isValid()) + modulePathName = fsys::path() / searchLocation.fetch() / moduleName; + else + throw error::Config ("Module \""+moduleName+"\" not found" + + (searchPath.empty()? ".":" in search path: "+searchPath)); + } } + + + +} // namespace lib diff --git a/src/lib/searchpath.hpp b/src/lib/searchpath.hpp new file mode 100644 index 000000000..eff8b2510 --- /dev/null +++ b/src/lib/searchpath.hpp @@ -0,0 +1,147 @@ +/* + SEARCHPATH.hpp - helpers for searching directory lists and locating modules + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef COMMON_SEARCHPATH_H +#define COMMON_SEARCHPATH_H + +#include "lib/error.hpp" +#include "lib/bool-checkable.hpp" + +#include +#include +#include +#include +#include + + +namespace lib { + + using std::string; + using boost::regex; + using boost::smatch; + using boost::regex_search; + using boost::sregex_iterator; + + typedef smatch::value_type const& SubMatch; + + namespace error = lumiera::error; + namespace fsys = boost::filesystem; + + LUMIERA_ERROR_DECLARE (FILE_NOT_DIRECTORY); ///< path element points at a file instead of a directory + using error::LUMIERA_ERROR_ITER_EXHAUST; + + + /** retrieve the location of the executable */ + string findExePath(); + + + /** + * 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 + : public BoolCheckable + { + string pathSpec_; + sregex_iterator pos_, + end_; + + static regex EXTRACT_PATHSPEC; + + public: + SearchPathSplitter (string const& searchPath) + : pathSpec_(searchPath) + , pos_(pathSpec_.begin(),pathSpec_.end(), EXTRACT_PATHSPEC) + , end_() + { } + + bool + isValid() const + { + return pos_ != end_; + } + + string + fetch () + { + if (!isValid()) + throw error::Logic ("Search path exhausted." + ,LUMIERA_ERROR_ITER_EXHAUST); + + string currentPathElement = resolveRelative(); + ++pos_; + return currentPathElement; + } + + private: + /** maybe resolve a path spec given relative to + * the current Executable location ($ORIGIN) */ + string + resolveRelative () + { + if (containsORIGINToken()) + return asAbsolutePath(); + else + return getFullPath(); + } + + SubMatch found(int group=0) { return (*pos_)[group]; } + + bool containsORIGINToken() { return found(1).matched; } + string getRelativePath() { return found(2); } + string getFullPath() { return found(); } + + string + asAbsolutePath() + { + fsys::path exePathName (findExePath()); + fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); + + if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) + throw error::Invalid ("Error in search path: component \""+modPathName.string()+"\" is not a directory" + ,LUMIERA_ERROR_FILE_NOT_DIRECTORY); + + return modPathName.directory_string(); + } + }; + + + + /** helper to establish the location to search for loadable modules. + * This is a simple demonstration of the basic technique used in the + * real application source to establish a plugin search path, based + * on the actual executable position plus compiled in and configured + * relative and absolute path specifications. + */ + string resolveModulePath (string moduleName, string searchPath = ""); + + + +} // namespace lib +#endif diff --git a/src/lib/test/suite.cpp b/src/lib/test/suite.cpp index b109b755d..dd8e35bfe 100644 --- a/src/lib/test/suite.cpp +++ b/src/lib/test/suite.cpp @@ -232,7 +232,7 @@ namespace test { void Suite::describe () { - util::Cmdline noCmdline(""); + lib::Cmdline noCmdline(""); PTestMap tests = testcases.getGroup(groupID_); ASSERT (tests); diff --git a/src/lib/test/testoption.cpp b/src/lib/test/testoption.cpp index 3a84ee4f8..9e0598d2b 100644 --- a/src/lib/test/testoption.cpp +++ b/src/lib/test/testoption.cpp @@ -33,10 +33,9 @@ typedef boost::program_options::variables_map VarMap; namespace op = boost::program_options; -using util::VectS; +using lib::VectS; -namespace test - { +namespace test { /** set up an options parser to use the current commandline. @@ -47,7 +46,7 @@ namespace test * --describe * \endcode */ - TestOption::TestOption (util::Cmdline& cmdline) + TestOption::TestOption (lib::Cmdline& cmdline) : syntax("Run a collection of test cases. Supported parameters"), parameters() { @@ -89,37 +88,37 @@ namespace test */ const string TestOption::getTestgroup () - { - ASSERT (parameters.count ("group")); - return parameters["group"].as(); - } + { + ASSERT (parameters.count ("group")); + return parameters["group"].as(); + } /** @return ID of a single test to run, empty string if not specified */ const string TestOption::getTestID () - { - if (parameters.count ("id") && - parameters["id"].as().size() > 0) - return parameters["id"].as()[0]; - else - return string (); - } + { + if (parameters.count ("id") && + parameters["id"].as().size() > 0) + return parameters["id"].as()[0]; + else + return string (); + } /** @return \c true if --describe switch was given */ bool TestOption::getDescribe () - { - return parameters["describe"].as(); - } + { + return parameters["describe"].as(); + } ostream& operator<< (ostream& os, const TestOption& to) - { - return os << to.syntax; - } + { + return os << to.syntax; + } diff --git a/src/lib/test/testoption.hpp b/src/lib/test/testoption.hpp index efa9ea2cd..4d41de8e6 100644 --- a/src/lib/test/testoption.hpp +++ b/src/lib/test/testoption.hpp @@ -53,7 +53,7 @@ namespace test { class TestOption : private boost::noncopyable { public: - TestOption (util::Cmdline& cmdline); + TestOption (lib::Cmdline& cmdline); const string getTestgroup (); const string getTestID (); bool getDescribe (); diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 4eed4da1d..14d039b7d 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -34,7 +34,7 @@ #include "proc/facade.hpp" #include "gui/guifacade.hpp" -using util::Cmdline; +using lib::Cmdline; using lumiera::Subsys; using lumiera::AppState; using lumiera::ON_GLOBAL_INIT; diff --git a/tests/40components.tests b/tests/40components.tests index b22c6025d..d72a6a8f8 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -590,6 +590,21 @@ return: 0 END +TEST "Search path walking" SearchPathSplitter_test < @@ -27,21 +27,26 @@ #include #include +#include #include +using util::for_each; +using std::string; using std::cout; +using std::endl; -namespace util { -namespace test { +namespace lib { +namespace test{ + using boost::lambda::_1; using boost::lambda::var; - /** @test for util::Cmdline, wrapping various example cmdlines */ + /** @test for lib::Cmdline, wrapping various example cmdlines */ class CmdlineWrapper_test : public Test { void @@ -61,12 +66,12 @@ namespace test { void testLine (const string cmdline) { - cout << "wrapping cmdline:" << cmdline << "..." << "\n"; + cout << "wrapping cmdline:" << cmdline << "..." << endl; int i=0; Cmdline theCmdline (cmdline); for_each(theCmdline, (cout << var(i)++ << "|" << _1 << "|\n")); - cout << "-->" << theCmdline << "\n"; + cout << "-->" << theCmdline << endl; // consistency checks std::ostringstream output; @@ -88,12 +93,11 @@ namespace test { { const char* fakeArg[3] = {"CMD", "one ", "two"}; Cmdline theCmdline(3, fakeArg); - cout << "Standard Cmdlineformat:" << theCmdline << "\n"; + cout << "Standard Cmdlineformat:" << theCmdline << endl; } }; LAUNCHER (CmdlineWrapper_test, "unit common"); -}} // namespace util::test - +}} // namespace lib::test diff --git a/tests/lib/mainsuite.cpp b/tests/lib/mainsuite.cpp index 882ee78d4..4493fc9b6 100644 --- a/tests/lib/mainsuite.cpp +++ b/tests/lib/mainsuite.cpp @@ -39,7 +39,7 @@ using lumiera::ON_GLOBAL_SHUTDOWN; */ int main (int argc, const char* argv[]) { - util::Cmdline args (argc,argv); + lib::Cmdline args (argc,argv); test::TestOption optparser (args); test::Suite suite (optparser.getTestgroup()); LifecycleHook::trigger (ON_GLOBAL_INIT); diff --git a/tests/lib/query/query-utils-test.cpp b/tests/lib/query/query-utils-test.cpp index 70a9f59ec..b0af6b950 100644 --- a/tests/lib/query/query-utils-test.cpp +++ b/tests/lib/query/query-utils-test.cpp @@ -33,7 +33,7 @@ #include using lumiera::Query; -using util::Cmdline; +using lib::Cmdline; using util::isnil; using util::contains; using util::for_each; diff --git a/tests/lib/search-path-splitter-test.cpp b/tests/lib/search-path-splitter-test.cpp new file mode 100644 index 000000000..e4e824090 --- /dev/null +++ b/tests/lib/search-path-splitter-test.cpp @@ -0,0 +1,101 @@ +/* + SearchPathSplitter(Test) - iterating a search path specification + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" + +#include "lib/searchpath.hpp" +#include "common/appstate.hpp" + +#include + +using std::cout; +using std::endl; + + + +namespace lib { +namespace test { + + + /****************************************************** + * @test verify splitting a search path specification + * and retrieving the components by iteration. + * Embedded \c $ORIGIN tokens get resolved + * to the absolute path of this executable. + */ + class SearchPathSplitter_test : public Test + { + void + run (Arg) + { + walkSimplePaths (); + resolveEmbeddedOriginToken (); + } + + + void + walkSimplePaths () + { + walk (""); + walk (":"); + walk ("a:"); + walk (":a"); + walk ("a:b"); + walk (":a:b:c:"); + walk (" d : e f"); + walk ("/usr/bin:/usr/lib"); + + SearchPathSplitter sp(""); + VERIFY_ERROR (ITER_EXHAUST, sp.fetch() ); + } + + void + walk (string spec) + { + SearchPathSplitter path(spec); + while (path) + cout << "➢➢" << path.fetch() << endl; + } + + + + void + resolveEmbeddedOriginToken () + { + fsys::path exePath (findExePath()); + string expected = (exePath.remove_leaf() / "modules").string(); + + SearchPathSplitter sp("xyz:$ORIGIN/modules:abc"); + CHECK ("xyz" == sp.fetch()); + CHECK (sp.fetch() == expected); + CHECK ("abc" == sp.fetch()); + CHECK (!sp.isValid()); + } + }; + + + LAUNCHER (SearchPathSplitter_test, "unit common"); + + +}} // namespace lib::test diff --git a/tests/lib/subsystem-runner-test.cpp b/tests/lib/subsystem-runner-test.cpp index 8eebc7d26..6db7ebf99 100644 --- a/tests/lib/subsystem-runner-test.cpp +++ b/tests/lib/subsystem-runner-test.cpp @@ -69,7 +69,7 @@ namespace test { const uint DELAY_FOR_FLOUNDERING_THRAD_ms = 20; /** dummy options just to be ignored */ - util::Cmdline dummyArgs (""); + lib::Cmdline dummyArgs (""); lumiera::Option dummyOpt (dummyArgs); /** marker for simulated failure exceptions */ diff --git a/tests/lib/test/testoptiontest.cpp b/tests/lib/test/testoptiontest.cpp index 2fd3761d4..8bb517604 100644 --- a/tests/lib/test/testoptiontest.cpp +++ b/tests/lib/test/testoptiontest.cpp @@ -26,12 +26,11 @@ #include "lib/test/testoption.hpp" #include "lib/util.hpp" -using util::Cmdline; +using lib::Cmdline; using util::isnil; using std::endl; -namespace test - { +namespace test { /**************************************************************** * invokes the TestOption parser for various example commandlines @@ -76,10 +75,10 @@ namespace test void groupFilter2() { doIt (" --group TestGroupID SingleTestID "); } void additionalCmd() { doIt (" --group TestGroupID SingleTestID spam eggs"); } void additionalCmd2() { doIt ("\t\tSingleTestID spam --group TestGroupID \t --eggs"); } - + }; - LAUNCHER (TestOption_test, "function common"); - + LAUNCHER (TestOption_test, "function common"); + + } // namespace test -