Urbi SDK Remote for C++  2.7.5
liburbi/urbi-root.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009-2011, Gostai S.A.S.
00003  *
00004  * This software is provided "as is" without warranty of any kind,
00005  * either expressed or implied, including but not limited to the
00006  * implied warranties of fitness for a particular purpose.
00007  *
00008  * See the LICENSE file for more information.
00009  */
00010 
00011 #include <libport/sys/stat.h>
00012 #include <libport/unistd.h>
00013 
00014 #include <libport/cassert>
00015 #include <libport/cstring>
00016 #include <libport/cstdlib>
00017 #include <libport/cstdio>
00018 #include <iostream>
00019 
00020 #include <libport/config.h>
00021 #include <libport/foreach.hh>
00022 #include <libport/sysexits.hh>
00023 
00024 #include <urbi/urbi-root.hh>
00025 #ifdef STATIC_BUILD
00026 # include <urbi/umain.hh>
00027 #endif
00028 
00029 #if defined WIN32
00030 # define APPLE_LINUX_WINDOWS(Apple, Linux, Windows) Windows
00031 #elif defined __APPLE__
00032 # define APPLE_LINUX_WINDOWS(Apple, Linux, Windows) Apple
00033 #else
00034 # define APPLE_LINUX_WINDOWS(Apple, Linux, Windows) Linux
00035 #endif
00036 
00037 static std::string
00038 mygetenv(const std::string& var, const std::string& val = "");
00039 
00040 /*------------.
00041 | Reporting.  |
00042 `------------*/
00043 
00044 static bool
00045 debug()
00046 {
00047   static bool res = mygetenv("GD_LEVEL") == "DUMP";
00048   return res;
00049 }
00050 
00051 # define URBI_ROOT_ECHO(S)                      \
00052   std::cerr << S << std::endl                   \
00053 
00054 # define URBI_ROOT_DEBUG(Self, S)               \
00055   do {                                          \
00056     if (debug())                                \
00057       URBI_ROOT_ECHO(Self << ": " << S);        \
00058   } while (0)
00059 
00060 # define URBI_ROOT_FATAL(Self, N, S)            \
00061   do {                                          \
00062     URBI_ROOT_ECHO(Self << ": " << S);          \
00063     exit(N);                                    \
00064   } while (0)
00065 
00066 
00067 
00068 /*----------.
00069 | Helpers.  |
00070 `----------*/
00071 
00072 static std::string
00073 mygetenv(const std::string& var, const std::string& val)
00074 {
00075   const char* res = getenv(var.c_str());
00076   return res ? std::string(res) : val;
00077 }
00078 
00079 static std::string
00080 urbi_getenv(const std::string& logname,
00081             std::string var,
00082             const std::string& val = "")
00083 {
00084   var = "URBI_" + var;
00085   const char* res = getenv(var.c_str());
00086   if (res)
00087   {
00088     URBI_ROOT_DEBUG(logname, "obeying to " << var << " = " << res);
00089     return res;
00090   }
00091   else
00092     return val;
00093 }
00094 
00095 /*-----------------.
00096 | File constants.  |
00097 `-----------------*/
00098 
00099 static const std::string libext =
00100                            APPLE_LINUX_WINDOWS(".dylib", ".so", ".dll");
00101 static const std::string separator =
00102                            APPLE_LINUX_WINDOWS("/", "/", "\\");
00103 static const std::string libdir =
00104                            APPLE_LINUX_WINDOWS("lib", "lib", "bin");
00105 
00107 static
00108 std::string
00109 operator/(const std::string& lhs, const std::string& rhs)
00110 {
00111   return (lhs.empty() ? rhs
00112           : rhs.empty() ? lhs
00113           : lhs + separator + rhs);
00114 }
00115 
00116 /*-------------------------------------.
00117 | Crapy dynamic portability routines.  |
00118 `-------------------------------------*/
00119 
00120 #ifdef WIN32
00121 # define RTLD_LAZY 0
00122 # define RTLD_NOW 0
00123 # define RTLD_GLOBAL 0
00124 
00125 static RTLD_HANDLE
00126 dlopen(const char* name, int)
00127 {
00128   RTLD_HANDLE res = LoadLibrary(name);
00129   if (res)
00130   {
00131     char buf[BUFSIZ];
00132     GetModuleFileName(res, buf, sizeof buf - 1);
00133     buf[sizeof buf - 1] = 0;
00134   }
00135   return res;
00136 }
00137 
00138 static void*
00139 dlsym(RTLD_HANDLE module, const char* name)
00140 {
00141   return static_cast<void*>(GetProcAddress(module, name));
00142 }
00143 
00144 static const char*
00145 dlerror(DWORD err = GetLastError())
00146 {
00147   static char buf[1024];
00148 
00149   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
00150                 0, err, 0,
00151                 (LPTSTR)buf, sizeof buf,
00152                 0);
00153 
00154   return buf;
00155 }
00156 
00157 #else
00158 # include <dlfcn.h>
00159 #endif
00160 
00161 typedef std::vector<std::string> strings_type;
00162 static strings_type
00163 split(std::string lib)
00164 {
00165   strings_type res;
00166   size_t pos = lib.find(':');
00167 
00168   if ((pos = lib.find(':')) == lib.npos)
00169   {
00170     res.push_back(lib);
00171     return res;
00172   }
00173 
00174   do
00175   {
00176     std::string s = lib.substr(0, pos);
00177     if (pos != lib.npos)
00178     {
00179       lib = lib.substr(pos + 1, lib.npos);
00180       pos = lib.find(':');
00181     }
00182     else
00183       lib.clear();
00184 
00185 #ifdef WIN32
00186     // Colon could be used as a path separator under cygwin and as a volume
00187     // separator under windows.
00188     if (s[0] == '\\' && !res.empty())
00189     {
00190       std::string& back = res.back();
00191       size_t back_len = back.length();
00192 
00193       // In case we split "c:\foo" into "c" and "\foo", glue them
00194       // together again.
00195       if (back_len == 1)
00196         back += ':' + s;
00197 
00198       // In case we split "bar;c:\foo" into "bar;c" and "\foo", split them
00199       // into "bar" and "c:\foo"
00200       else if (back_len >= 2 && back[back_len - 2] == ';')
00201       {
00202         s = std::string("") + back[back_len - 1] + ':' + s;
00203         back = back.substr(0, back_len - 2);
00204         res.push_back(s);
00205       }
00206     }
00207     else
00208       res.push_back(s);
00209 #else
00210     res.push_back(s);
00211 #endif
00212 
00213   } while (!lib.empty());
00214 
00215   return res;
00216 }
00217 
00219 static
00220 RTLD_HANDLE
00221 xdlopen(const std::string& program,
00222         const std::string& msg,
00223         std::string path,
00224         sysexit status = EX_FAIL,
00225         int flags = RTLD_LAZY | RTLD_GLOBAL)
00226 {
00227   path += libext;
00228   URBI_ROOT_DEBUG(program, "loading library: " << path << " (" << msg << ")");
00229   if (RTLD_HANDLE res = dlopen(path.c_str(), flags))
00230     return res;
00231   else
00232     URBI_ROOT_FATAL(program, status,
00233                     "cannot open library: " << path << ": " << dlerror());
00234 }
00235 
00236 template <typename Res>
00237 static
00238 Res
00239 xdlsym(const std::string& program,
00240        const char* modname, RTLD_HANDLE module,
00241        const char* name)
00242 {
00243   URBI_ROOT_DEBUG(program, "loading symbol " << name << " from " << modname);
00244   // Reinterpret-cast fails with gcc3 arm.
00245   if (Res res = (Res)(dlsym(module, name)))
00246     return res;
00247   else
00248     URBI_ROOT_FATAL(program, 2,
00249                     "cannot locate " << name << " symbol: " << dlerror());
00250 }
00251 
00252 
00253 static
00254 std::string
00255 resolve_symlinks(const std::string& logname, const std::string& s)
00256 {
00257 #if defined WIN32
00258   return s;
00259 #else
00260   char path[BUFSIZ];
00261   strncpy(path, s.c_str(), sizeof path - 1);
00262   path[sizeof path - 1] = 0;
00263   while (readlink(path, path, sizeof path) != -1)
00264     URBI_ROOT_DEBUG(logname, "unrolling symbolic link: " << path);
00265   return path;
00266 #endif
00267 }
00268 
00274 static
00275 std::string
00276 find_program(const std::string& logname,
00277              std::string prog)
00278 {
00279 #ifdef WIN32
00280   size_t pos = prog.find_last_of("/\\");
00281   {
00282     size_t dot = prog.find_last_of(".");
00283     if (dot == prog.npos || prog.substr(dot + 1) != "exe")
00284       prog += ".exe";
00285   }
00286 #else
00287   size_t pos = prog.rfind('/');
00288 #endif
00289   if (pos == std::string::npos)
00290   {
00291     struct stat stats;
00292     std::string dir, file;
00293     bool found = false;
00294 
00295 #ifdef WIN32
00296     URBI_ROOT_DEBUG(logname,
00297                     "check if invoked from the current directory");
00298 
00299     {
00300       char *dir_buf = getcwd(0, 0);
00301       std::string dir(dir_buf);
00302       free(dir_buf);
00303       file = dir / prog;
00304     }
00305 
00306     found = stat(file.c_str(), &stats) == 0;
00307     if (!found)
00308     {
00309       URBI_ROOT_DEBUG(logname, "not found: " << file);
00310 #endif
00311       URBI_ROOT_DEBUG(logname,
00312                       "check if invoked from the path");
00313       strings_type path = split(mygetenv("PATH"));
00314       foreach (const std::string& dir_, path)
00315       {
00316         file = dir_ / prog;
00317         found = stat(file.c_str(), &stats) == 0;
00318         if (found)
00319         {
00320           dir = dir_;
00321           break;
00322         }
00323         URBI_ROOT_DEBUG(logname, "not found: " << file);
00324       }
00325 #ifdef WIN32
00326     }
00327 #endif
00328 
00329     if (found)
00330     {
00331       URBI_ROOT_DEBUG(logname, "found: " << file);
00332       std::string res = dir / "..";
00333       URBI_ROOT_DEBUG(logname, "root directory is: " << res);
00334       return res;
00335     }
00336   }
00337   else
00338   {
00339     std::string res = prog.substr(0, pos) / "..";
00340     URBI_ROOT_DEBUG(logname,
00341                     "invoked with a path, setting root to parent directory: "
00342                     << res);
00343     return res;
00344   }
00345   return "";
00346 }
00347 
00348 /*-----------.
00349 | UrbiRoot.  |
00350 `-----------*/
00351 
00352 UrbiRoot::UrbiRoot(const std::string& program, bool static_build)
00353   : program_(program)
00354   , root_()
00355   , handle_libjpeg_(0)
00356   , handle_libport_(0)
00357   , handle_libsched_(0)
00358   , handle_liburbi_(0)
00359   , handle_libuobject_(0)
00360 {
00361   // Find our directory.
00362   std::string uroot = urbi_getenv(program, "ROOT");
00363   if (uroot.empty())
00364   {
00365     URBI_ROOT_DEBUG(program_, "guessing Urbi root: invoked as: " << program_);
00366     // Handle the chained symlinks case.
00367     std::string argv0 = resolve_symlinks(program_, program);
00368     root_ = find_program(program_, argv0);
00369   }
00370   else
00371   {
00372     root_ = uroot;
00373     URBI_ROOT_DEBUG(program_,
00374                     "URBI_ROOT is set, forcing root directory: " << root_);
00375   }
00376 
00377   if (root_.empty())
00378     URBI_ROOT_FATAL(program_, 3,
00379                     "Unable to find Urbi SDK installation location. "
00380                     "Please set URBI_ROOT.");
00381 
00382   if (!static_build)
00383   {
00384     handle_libjpeg_      = library_load("jpeg4urbi");
00385     handle_libport_      = library_load("port");
00386     handle_libsched_     = library_load("sched");
00387 #ifdef LIBPORT_ENABLE_SERIALIZATION
00388     handle_libserialize_ = library_load("serialize");
00389 #endif
00390     handle_liburbi_      = library_load("urbi");
00391   }
00392 }
00393 
00394 RTLD_HANDLE
00395 UrbiRoot::library_load(const std::string& base, const std::string& env_suffix)
00396 {
00397   std::string envvar;
00398   if (env_suffix.empty())
00399     envvar = "ROOT_LIB" + base;
00400   else
00401     envvar = "ROOT_LIB" + env_suffix;
00402   foreach (char& s, envvar)
00403     s = toupper(s);
00404 
00405   return
00406     xdlopen(program_,
00407             base,
00408             urbi_getenv(program_, envvar,
00409                         root() / libdir / "lib" + base + library_suffix()));
00410 }
00411 
00412 const std::string&
00413 UrbiRoot::root() const
00414 {
00415   return root_;
00416 }
00417 
00418 std::string
00419 UrbiRoot::core_path() const
00420 {
00421   return root() / LIBPORT_LIBDIRNAME / "gostai";
00422 }
00423 
00424 std::string
00425 UrbiRoot::doc_dir() const
00426 {
00427   return urbi_getenv(program_, "DOC", root() / "share" / "doc" / "urbi-sdk");
00428 }
00429 
00430 std::string
00431 UrbiRoot::share_dir() const
00432 {
00433   return urbi_getenv(program_, "SHARE", root() / "share" / "gostai");
00434 }
00435 
00436 std::vector<std::string>
00437 UrbiRoot::uobjects_path() const
00438 {
00439   std::vector<std::string> res;
00440   if (!library_suffix().empty())
00441     res.push_back(core_path() / "uobjects" + library_suffix());
00442   res.push_back(core_path() / "uobjects");
00443   return res;
00444 }
00445 
00446 std::string
00447 UrbiRoot::library_suffix() const
00448 {
00449   return LIBPORT_LIBSFX;
00450 }
00451 
00452 void
00453 UrbiRoot::load_plugin()
00454 {
00455   handle_libuobject_ =
00456     xdlopen(program_,
00457             "plugin UObject implementation",
00458             urbi_getenv(program_, "ROOT_LIBPLUGIN",
00459                         core_path() / "engine" / "libuobject"+library_suffix()),
00460             // This exit status is understood by the test suite.  It
00461             // helps it skipping SDK Remote tests that cannot run
00462             // without Urbi SDK.
00463             EX_OSFILE);
00464 }
00465 
00467 void
00468 UrbiRoot::load_remote()
00469 {
00470   handle_libuobject_ =
00471     xdlopen(program_,
00472             "remote UObject implementation",
00473             urbi_getenv(program_, "ROOT_LIBREMOTE",
00474                         core_path() / "remote" / "libuobject"+library_suffix()),
00475             EX_OSFILE);
00476 }
00477 
00478 void
00479 UrbiRoot::load_custom(const std::string& path_)
00480 {
00481   handle_libuobject_ =
00482     xdlopen(program_,
00483             "custom UObject implementation",
00484             path_ / "libuobject",
00485             EX_OSFILE);
00486 }
00487 
00488 int
00489 UrbiRoot::urbi_launch(int argc, const char** argv)
00490 {
00491   urbi_launch_type f =
00492     xdlsym<urbi_launch_type>(program_,
00493                              "liburbi-launch", handle_liburbi_,
00494                              "urbi_launch");
00495   return f(argc, argv, *this);
00496 }
00497 
00498 int
00499 UrbiRoot::urbi_launch(int argc, char** argv)
00500 {
00501   return urbi_launch(argc, const_cast<const char**>(argv));
00502 }
00503 
00504 typedef int(*urbi_main_type)(const std::vector<std::string>& args,
00505                              UrbiRoot& root,
00506                              bool block, bool errors);
00507 int
00508 UrbiRoot::urbi_main(const std::vector<std::string>& args,
00509                     bool block, bool errors)
00510 {
00511 #ifdef STATIC_BUILD
00512   return ::urbi_main_args(args, *this, block, errors);
00513 #else
00514   urbi_main_type f =
00515     xdlsym<urbi_main_type>(program_,
00516                            "libuobject", handle_libuobject_,
00517                            "urbi_main_args");
00518   URBI_ROOT_DEBUG(program_, "command line: ");
00519   foreach (const std::string& arg, args)
00520     URBI_ROOT_DEBUG(program_, "  " << arg);
00521 
00522   return f(args, *this, block, errors);
00523 #endif
00524 }