00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <cstdlib>
00031 #include <algorithm>
00032 #include <cstdio>
00033 #include <cstring>
00034 #include <cerrno>
00035 #include <string>
00036 #include <vector>
00037 #include <map>
00038 #include <stack>
00039 #include <queue>
00040 #include <cassert>
00041 #if !defined(WIN32)
00042 #include <unistd.h>
00043 #include <dirent.h>
00044 #include <sys/time.h>
00045 #include <sys/file.h>
00046 #include <stdint.h>
00047 #endif
00048 #include <stdexcept>
00049 #include <time.h>
00050 #include <sstream>
00051 #include <iterator>
00052
00053 #if defined(_MSC_VER) // msvc only
00054 #define F_OK 0x00
00055 #define W_OK 0x02
00056 #define R_OK 0x04
00057 #else // non msvc only
00058 #include <libgen.h>
00059 #endif
00060
00061 #if defined(WIN32) // both msvc and mingw
00062 #include <direct.h>
00063 #include <time.h>
00064 #include <windows.h>
00065 #include <io.h>
00066 #include <fcntl.h>
00067 #define PATH_MAX MAX_PATH
00068 #define snprintf _snprintf
00069 #define getcwd _getcwd
00070 #define fdopen _fdopen
00071 #define access _access
00072 #define mkdir(a,b) _mkdir(a)
00073 #endif
00074
00075 #include "tinyxml-2.5.3/tinyxml.h"
00076 #include "rospack/rosstack.h"
00077 #include "rospack/rospack.h"
00078 using namespace std;
00079
00080
00081 const double DEFAULT_MAX_CACHE_AGE = 60.0;
00082
00083 #include <sys/stat.h>
00084 #ifndef S_ISDIR
00085 #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
00086 #endif
00087
00088 using namespace rosstack;
00089
00090 #ifdef __APPLE__
00091 const string g_ros_os("osx");
00092 #else
00093 #if defined(WIN32)
00094 const string g_ros_os("win32");
00095 #else
00096 const string g_ros_os("linux");
00097 #endif
00098 #endif
00099
00100 #if defined(_MSVC_VER)
00101
00102
00103
00104
00105
00106
00107
00108
00109 const int TiXmlBase::utf8ByteTable[256] =
00110 {
00111
00112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00120 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00121 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00123 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
00124 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
00125 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
00126 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
00127 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
00128 };
00129 bool TiXmlBase::condenseWhiteSpace = true;
00130 #endif
00131
00133
00134
00135 bool g_deps_only;
00136
00137 string g_length;
00138
00139 string g_stack;
00140
00141 unsigned int g_profile_length = 0;
00142
00143 ROSStack *g_rosstack = NULL;
00144
00146
00147 #if defined(WIN32)
00148
00149
00150
00151 const char *rosstack::fs_delim = "\\";
00152 const char *rosstack::path_delim = ";";
00153 #else
00154 const char *rosstack::fs_delim = "/";
00155 const char *rosstack::path_delim = ":";
00156 #endif
00157
00158
00159 Stack::Stack(string _path) : path(_path),
00160 deps_calculated(false), direct_deps_calculated(false),
00161 descendants_calculated(false), manifest_loaded(false)
00162 {
00163 vector<string> path_tokens;
00164 string_split(path, path_tokens, fs_delim);
00165 name = path_tokens.back();
00166
00167
00168
00169
00170 }
00171 bool Stack::is_stack(const string &path)
00172 {
00173 return file_exists(path + string(fs_delim) + "stack.xml");
00174 }
00175 bool Stack::is_package(const string &path)
00176 {
00177 return file_exists(path + string(fs_delim) + "manifest.xml");
00178 }
00179 bool Stack::is_no_subdirs(const string &path)
00180 {
00181 return file_exists(path + string(fs_delim) + "rosstack_nosubdirs");
00182 }
00183 const VecStack &Stack::deps1()
00184 {
00185 return direct_deps();
00186 }
00187 const VecStack &Stack::deps(traversal_order_t order, int depth)
00188 {
00189 if (depth > 1000)
00190 {
00191 fprintf(stderr,"[rosstack] woah! expanding the dependency tree made it blow "
00192 "up.\n There must be a circular dependency somewhere.\n");
00193 throw runtime_error(string("circular dependency"));
00194 }
00195 if (deps_calculated)
00196 return _deps;
00197
00198 VecStack my_dd = direct_deps();
00199 for (VecStack::iterator i = my_dd.begin(); i != my_dd.end(); ++i)
00200 {
00201 VecStack d = (*i)->deps(order, depth+1);
00202 if (order == PREORDER)
00203 _deps.push_back(*i);
00204 for (VecStack::iterator j = d.begin(); j != d.end(); ++j)
00205 {
00206
00207
00208 bool have = false;
00209 VecStack::iterator prior_loc;
00210 for (VecStack::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
00211 if ((*k) == (*j))
00212 {
00213 prior_loc = k;
00214 have = true;
00215 }
00216 if (have && order == PREORDER)
00217 {
00218 _deps.erase(prior_loc);
00219 _deps.push_back(*j);
00220 }
00221 else if (!have)
00222 _deps.push_back(*j);
00223 }
00224 if (order == POSTORDER)
00225 {
00226
00227 bool have = false;
00228 for (VecStack::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
00229 if ((*k) == (*i))
00230 have = true;
00231 if (!have)
00232 _deps.push_back(*i);
00233 }
00234 }
00235 deps_calculated = true;
00236 return _deps;
00237 }
00238 string Stack::manifest_path()
00239 {
00240 return path + string(fs_delim) + "stack.xml";
00241 }
00242
00243 VecStack Stack::descendants1()
00244 {
00245 VecStack children;
00246 for (VecStack::iterator p = stacks.begin(); p != stacks.end(); ++p)
00247 {
00248
00249
00250 try
00251 {
00252 if ((*p)->has_parent(name))
00253 children.push_back(*p);
00254 }
00255 catch (runtime_error &e)
00256 {
00257 }
00258 }
00259 return children;
00260 }
00261
00262 const VecStack &Stack::descendants(int depth)
00263 {
00264 if (depth > 100)
00265 {
00266 fprintf(stderr, "[rosstack] woah! circular dependency! aaaaaa!\n");
00267 throw runtime_error(string("circular dependency"));
00268 }
00269 if (descendants_calculated)
00270 return _descendants;
00271 VecStack desc_with_dups;
00272 for (VecStack::iterator p = stacks.begin(); p != stacks.end(); ++p)
00273 {
00274
00275
00276 try
00277 {
00278 if ((*p)->has_parent(name))
00279 {
00280 desc_with_dups.push_back(*p);
00281 const VecStack &p_desc = (*p)->descendants(depth+1);
00282 for (VecStack::const_iterator q = p_desc.begin();
00283 q != p_desc.end(); ++q)
00284 desc_with_dups.push_back(*q);
00285 }
00286 }
00287 catch (runtime_error &e)
00288 {
00289 }
00290 }
00291 assert(_descendants.size() == 0);
00292 for (VecStack::iterator p = desc_with_dups.begin();
00293 p != desc_with_dups.end(); ++p)
00294 {
00295 bool found = false;
00296 for (VecStack::iterator q = _descendants.begin();
00297 q != _descendants.end() && !found; ++q)
00298 if ((*q)->name == (*p)->name)
00299 found = true;
00300 if (!found)
00301 _descendants.push_back(*p);
00302 }
00303 descendants_calculated = true;
00304 return _descendants;
00305 }
00306
00307
00308 bool Stack::has_parent(string pkg)
00309 {
00310 VecStack parents = direct_deps(true);
00311 for (VecStack::iterator i = parents.begin(); i != parents.end(); ++i)
00312 if ((*i)->name == pkg)
00313 return true;
00314 return false;
00315 }
00316
00317 const VecStack &Stack::direct_deps(bool missing_stack_as_warning)
00318 {
00319 if (direct_deps_calculated)
00320 return _direct_deps;
00321 #ifdef VERBOSE_DEBUG
00322 printf("calculating direct deps for package [%s]\n", name.c_str());
00323 #endif
00324 TiXmlElement *mroot = manifest_root();
00325 TiXmlNode *dep_node = 0;
00326 while ((dep_node = mroot->IterateChildren(string("depend"), dep_node)))
00327 {
00328 TiXmlElement *dep_ele = dep_node->ToElement();
00329 assert(dep_ele);
00330 const char *dep_stackname = dep_ele->Attribute("stack");
00331 if (!dep_stackname)
00332 {
00333 fprintf(stderr,"[rosstack] bad depend syntax (no 'stack' attribute) in "
00334 "[%s]\n", manifest_path().c_str());
00335 throw runtime_error(string("invalid manifest"));
00336 }
00337
00338
00339 string dep_stackname_copy = string(dep_stackname);
00340 string name_copy = name;
00341 #ifdef VERBOSE_DEBUG
00342 printf("direct_deps: stk %s has dep %s\n",
00343 name.c_str(), dep_stackname_copy.c_str());
00344 #endif
00345 try
00346 {
00347 _direct_deps.push_back(g_get_stack(dep_stackname_copy));
00348 }
00349 catch (runtime_error &e)
00350 {
00351 if (missing_stack_as_warning)
00352 fprintf(stderr, "[rosstack] warning: couldn't find dependency "
00353 "[%s] of [%s]\n",
00354 dep_stackname_copy.c_str(), name_copy.c_str());
00355 else
00356 {
00357 fprintf(stderr, "[rosstack] couldn't find dependency [%s] of [%s]\n",
00358 dep_stackname_copy.c_str(), name_copy.c_str());
00359 throw runtime_error(string("missing dependency"));
00360 }
00361 }
00362 }
00363 direct_deps_calculated = true;
00364 return _direct_deps;
00365 }
00366
00367 void Stack::load_manifest()
00368 {
00369 if (manifest_loaded)
00370 return;
00371 if (!manifest.LoadFile(manifest_path()))
00372 {
00373 string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
00374 fprintf(stderr, "[rosstack] warning: error parsing manifest file at [%s]. Blowing away the cache...\n",
00375 manifest_path().c_str());
00376 g_rosstack->deleteCache();
00377
00378 manifest_loaded = true;
00379 throw runtime_error(errmsg);
00380 }
00381 TiXmlElement *mroot = manifest.RootElement();
00382 }
00383
00384 TiXmlElement *Stack::manifest_root()
00385 {
00386 load_manifest();
00387 TiXmlElement *ele = manifest.RootElement();
00388 if (!ele)
00389 {
00390 string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
00391 throw runtime_error(errmsg);
00392 }
00393 return ele;
00394 }
00395
00396 VecStack Stack::stacks;
00397
00399
00400
00401 ROSStack::ROSStack() : ros_root(NULL), crawled(false)
00402 {
00403 g_rosstack = this;
00404 Stack::stacks.reserve(500);
00405 ros_root = getenv("ROS_ROOT");
00406 if (!ros_root)
00407 {
00408 fprintf(stderr,"[rosstack] ROS_ROOT is not defined in the environment.\n");
00409 throw runtime_error(string("no ROS_ROOT"));
00410 }
00411 if (!file_exists(ros_root))
00412 {
00413 fprintf(stderr,"[rosstack] the path specified as ROS_ROOT is not "
00414 "accessible. Please ensure that this environment variable "
00415 "is set and is writeable by your user account.\n");
00416 throw runtime_error(string("no ROS_ROOT"));
00417 }
00418
00419 createROSHomeDirectory();
00420
00421 crawl_for_stacks();
00422 }
00423
00424 ROSStack::~ROSStack()
00425 {
00426 for (VecStack::iterator p = Stack::stacks.begin();
00427 p != Stack::stacks.end(); ++p)
00428 delete (*p);
00429 Stack::stacks.clear();
00430 }
00431
00432 const char* ROSStack::usage()
00433 {
00434 return "USAGE: rosstack [options] <command> [stack]\n"
00435 " Allowed commands:\n"
00436 " help\n"
00437 " find [stack]\n"
00438 " contents [stack]\n"
00439 " list\n"
00440 " list-names\n"
00441 " depends [stack] (alias: deps)\n"
00442 " depends-manifests [stack] (alias: deps-manifests)\n"
00443 " depends1 [stack] (alias: deps1)\n"
00444 " depends-indent [stack] (alias: deps-indent)\n"
00445 " depends-on [stack]\n"
00446 " depends-on1 [stack]\n"
00447 " contains [package]\n"
00448 " contains-path [package]\n"
00449 " profile [--length=<length>] \n\n"
00450 " If [stack] is omitted, the current working directory\n"
00451 " is used (if it contains a stack.xml).\n\n";
00452 }
00453
00454 Stack *ROSStack::get_stack(const string &stack_name)
00455 {
00456 #ifdef VERBOSE_DEBUG
00457 printf("searching for stack %s\n", stack_name.c_str());
00458 #endif
00459 for (VecStack::iterator p = Stack::stacks.begin();
00460 p != Stack::stacks.end(); ++p)
00461 {
00462 if ((*p)->name == stack_name)
00463 {
00464 if(!crawled)
00465 {
00466
00467
00468 std::string manifest_path = (*p)->path + fs_delim + "stack.xml";
00469 struct stat s;
00470 int ret;
00471 while((ret = stat(manifest_path.c_str(), &s)) != 0 &&
00472 errno == EINTR);
00473 if(ret == 0)
00474 {
00475
00476 return (*p);
00477 }
00478 else
00479 {
00480
00481 fprintf(stderr, "[rosstack] warning: invalid cached location %s for package %s; forcing recrawl\n",
00482 (*p)->path.c_str(),
00483 (*p)->name.c_str());
00484 break;
00485 }
00486 }
00487 else
00488 {
00489
00490 return (*p);
00491 }
00492 }
00493 }
00494 if (!crawled)
00495 {
00496 crawl_for_stacks(true);
00497 return get_stack(stack_name);
00498 }
00499 throw runtime_error(string("couldn't find stack ") + stack_name);
00500 return NULL;
00501 }
00502
00503 int ROSStack::cmd_depends_on(bool include_indirect)
00504 {
00505
00506
00507
00508
00509
00510 if(g_stack.size() == 0)
00511 {
00512 string errmsg = string("no stack name given, and current directory is not a stack root");
00513 throw runtime_error(errmsg);
00514 }
00515
00516
00517
00518
00519
00520 crawl_for_stacks(true);
00521
00522 Stack* s;
00523 try
00524 {
00525 s = get_stack(g_stack);
00526 }
00527 catch(runtime_error)
00528 {
00529 fprintf(stderr, "[rosstack] warning: stack %s doesn't exist\n",
00530 g_stack.c_str());
00531
00532
00533 s = add_stack(g_stack);
00534 }
00535 assert(s);
00536 const VecStack descendants = include_indirect ? s->descendants()
00537 : s->descendants1();
00538 for (VecStack::const_iterator sit = descendants.begin();
00539 sit != descendants.end(); ++sit)
00540 printf("%s\n", (*sit)->name.c_str());
00541 return 0;
00542 }
00543
00544 int ROSStack::cmd_find()
00545 {
00546
00547 Stack *p = get_stack(g_stack);
00548 printf("%s\n", p->path.c_str());
00549 return 0;
00550 }
00551
00552 string ROSStack::lookup_owner(string pkg_name, bool just_owner_name)
00553 {
00554
00555 rospack::Package *pkg = rp.get_pkg(pkg_name);
00556 #ifdef VERBOSE_DEBUG
00557 printf("package path: [%s]\n", pkg->path.c_str());
00558 #endif
00559 map<string, string> bases;
00560 for (VecStack::iterator p = Stack::stacks.begin();
00561 p != Stack::stacks.end(); ++p)
00562 bases[(*p)->path] = (*p)->name;
00563
00564
00565
00566
00567
00568
00569
00570 char *rpp = getenv("ROS_PACKAGE_PATH");
00571 if (rpp)
00572 {
00573 vector<string> rppvec;
00574 string_split(rpp, rppvec, path_delim);
00575 sanitize_rppvec(rppvec);
00576 for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i)
00577 bases[*i] = string("");
00578 }
00579 #ifdef VERBOSE_DEBUG
00580 printf("bases:\n");
00581 for (map<string, string>::iterator i = bases.begin(); i != bases.end(); ++i)
00582 printf("%s -> %s\n", i->first.c_str(), i->second.c_str());
00583 #endif
00584
00585 string pkg_path_fragment = pkg->path;
00586 while (pkg_path_fragment.length() > 1)
00587 {
00588
00589 size_t last_slash_pos = pkg_path_fragment.find_last_of('/');
00590 if (last_slash_pos == string::npos)
00591 break;
00592 pkg_path_fragment = pkg_path_fragment.substr(0, last_slash_pos);
00593 #ifdef VERBOSE_DEBUG
00594 printf("frag = %s\n", pkg_path_fragment.c_str());
00595 #endif
00596 map<string, string>::iterator i = bases.find(pkg_path_fragment);
00597 if (i != bases.end())
00598 {
00599 if (just_owner_name)
00600 return bases[pkg_path_fragment];
00601 else
00602 return pkg_path_fragment;
00603 break;
00604 }
00605 }
00606 return string("");
00607 }
00608
00609 int ROSStack::cmd_contains()
00610 {
00611 printf("%s\n", lookup_owner(g_stack, true).c_str());
00612 return 0;
00613 }
00614
00615 int ROSStack::cmd_contains_path()
00616 {
00617 printf("%s\n", lookup_owner(g_stack, false).c_str());
00618 return 0;
00619 }
00620
00621 int ROSStack::cmd_deps()
00622 {
00623 VecStack d = get_stack(g_stack)->deps(Stack::POSTORDER);
00624 for (VecStack::iterator i = d.begin(); i != d.end(); ++i)
00625 printf("%s\n", (*i)->name.c_str());
00626 return 0;
00627 }
00628
00629 int ROSStack::cmd_deps_manifests()
00630 {
00631 VecStack d = get_stack(g_stack)->deps(Stack::POSTORDER);
00632 for (VecStack::iterator i = d.begin(); i != d.end(); ++i)
00633 printf("%s/stack.xml ", (*i)->path.c_str());
00634 puts("");
00635 return 0;
00636 }
00637
00638 int ROSStack::cmd_deps1()
00639 {
00640 VecStack d = get_stack(g_stack)->deps1();
00641 for (VecStack::iterator i = d.begin(); i != d.end(); ++i)
00642 printf("%s\n", (*i)->name.c_str());
00643 return 0;
00644 }
00645
00646 int ROSStack::cmd_depsindent(Stack *stack, int indent)
00647 {
00648 VecStack d = stack->deps1();
00649 for (VecStack::iterator i = d.begin(); i != d.end(); ++i)
00650 {
00651 for(int s=0; s<indent; s++)
00652 printf(" ");
00653 printf("%s\n", (*i)->name.c_str());
00654 cmd_depsindent(*i, indent+2);
00655 }
00656 return 0;
00657 }
00658
00659 static bool space(char c) { return isspace(c); }
00660 static bool not_space(char c) { return !isspace(c); }
00661 static vector<string> split_space(const string& str)
00662 {
00663 typedef string::const_iterator iter;
00664 vector<string> ret;
00665 iter i = str.begin();
00666 while (i != str.end())
00667 {
00668 i = find_if(i, str.end(), not_space);
00669 iter j = find_if(i, str.end(), space);
00670 if (i != str.end())
00671 ret.push_back(string(i, j));
00672 i = j;
00673 }
00674 return ret;
00675 }
00676
00677 int ROSStack::run(int argc, char **argv)
00678 {
00679 assert(argc >= 2);
00680 int i;
00681 const char* opt_length = "--length=";
00682
00683 string errmsg = string(usage());
00684
00685 i=1;
00686 const char* cmd = argv[i++];
00687
00688 for(;i<argc;i++)
00689 {
00690 if(!strncmp(argv[i], opt_length, strlen(opt_length)))
00691 {
00692 if(strlen(argv[i]) > strlen(opt_length))
00693 g_length = string(argv[i]+strlen(opt_length));
00694 else
00695 throw runtime_error(errmsg);
00696 }
00697 else
00698 break;
00699 }
00700
00701 if(strcmp(cmd, "profile") && g_length.size())
00702 throw runtime_error(errmsg);
00703
00704 if(i < argc)
00705 {
00706 if(!strcmp(cmd, "help") ||
00707 !strcmp(cmd, "list") ||
00708 !strcmp(cmd, "list-names") ||
00709 !strcmp(cmd, "profile"))
00710 throw runtime_error(errmsg);
00711 g_stack = string(argv[i++]);
00712 }
00713
00714 else if(Stack::is_stack("."))
00715 {
00716 char buf[1024];
00717 if(!getcwd(buf,sizeof(buf)))
00718 throw runtime_error(errmsg);
00719 #if defined(_MSC_VER)
00720
00721 char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
00722 _splitpath_s(buf, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
00723 ext, _MAX_EXT);
00724 char filename[_MAX_FNAME + _MAX_EXT];
00725 if (ext[0] != '\0')
00726 {
00727 _makepath_s(filename, _MAX_FNAME + _MAX_EXT, NULL, NULL, fname, ext);
00728 g_stack = string(filename);
00729 }
00730 else
00731 g_stack = string(fname);
00732 #else
00733 g_stack = string(basename(buf));
00734 #endif
00735 }
00736
00737 if (i != argc)
00738 throw runtime_error(errmsg);
00739
00740 if (!strcmp(cmd, "profile"))
00741 {
00742 if (g_length.size())
00743 g_profile_length = atoi(g_length.c_str());
00744 else
00745 g_profile_length = 20;
00746 #ifdef VERBOSE_DEBUG
00747 printf("profile_length = %d\n", g_profile_length);
00748 #endif
00749
00750 crawl_for_stacks(true);
00751 return 0;
00752 }
00753 else if (!strcmp(cmd, "find"))
00754 return cmd_find();
00755 else if (!strcmp(cmd, "contains"))
00756 return cmd_contains();
00757 else if (!strcmp(cmd, "contains-path"))
00758 return cmd_contains_path();
00759 else if (!strcmp(cmd, "list"))
00760 return cmd_print_stack_list(true);
00761 else if (!strcmp(cmd, "list-names"))
00762 return cmd_print_stack_list(false);
00763 else if (!strcmp(cmd, "contents"))
00764 return cmd_print_packages();
00765 else if (!strcmp(cmd, "depends") || !strcmp(cmd, "deps"))
00766 return cmd_deps();
00767 else if (!strcmp(cmd, "depends-manifests") || !strcmp(cmd, "deps-manifests"))
00768 return cmd_deps_manifests();
00769 else if (!strcmp(cmd, "depends1") || !strcmp(cmd, "deps1"))
00770 return cmd_deps1();
00771 else if (!strcmp(cmd, "depends-indent") || !strcmp(cmd, "deps-indent"))
00772 return cmd_depsindent(get_stack(g_stack), 0);
00773 else if (!strcmp(cmd, "depends-on"))
00774 return cmd_depends_on(true);
00775 else if (!strcmp(cmd, "depends-on1"))
00776 return cmd_depends_on(false);
00777 else if (!strcmp(cmd, "help"))
00778 fputs(usage(), stderr);
00779 else
00780 throw runtime_error(errmsg);
00781 return 0;
00782 }
00783
00784 int ROSStack::cmd_print_stack_list(bool print_path)
00785 {
00786 for (VecStack::iterator i = Stack::stacks.begin();
00787 i != Stack::stacks.end(); ++i)
00788 if (print_path)
00789 printf("%s %s\n", (*i)->name.c_str(), (*i)->path.c_str());
00790 else
00791 printf("%s\n", (*i)->name.c_str());
00792 return 0;
00793 }
00794
00795 int ROSStack::cmd_print_packages()
00796 {
00797 rospack::ROSPack rp;
00798 string path = get_stack(g_stack)->path;
00799
00800 rospack::VecPkg pkgs = rp.partial_crawl(path);
00801
00802 for (rospack::VecPkg::iterator i = pkgs.begin(); i != pkgs.end(); ++i)
00803 {
00804 printf("%s\n", (*i)->name.c_str());
00805 delete *i;
00806 }
00807 return 0;
00808 }
00809
00810 void ROSStack::createROSHomeDirectory()
00811 {
00812 char *homedir = getenv("HOME");
00813 if (!homedir) {
00814
00815 }
00816 else
00817 {
00818 string path = string(homedir) + "/.ros";
00819 if (access(path.c_str(), R_OK) && !mkdir(path.c_str(), 0700))
00820 fprintf(stderr,"[rosstack] WARNING: cannot create ~/.ros directory.\n");
00821 }
00822 }
00823
00824 string ROSStack::getCachePath()
00825 {
00826 string path;
00827 path = string(ros_root) + fs_delim + ".rosstack_cache";
00828 if (access(ros_root, W_OK) == 0)
00829 return path;
00830
00831
00832 createROSHomeDirectory();
00833 path = string(getenv("HOME")) + fs_delim + ".ros" + fs_delim + "rosstack_cache";
00834 return path;
00835 }
00836
00837 void ROSStack::deleteCache()
00838 {
00839 string cache_path = g_rosstack->getCachePath();
00840 if (file_exists(cache_path))
00841 remove(cache_path.c_str());
00842 }
00843
00844 bool ROSStack::cache_is_good()
00845 {
00846 string cache_path = getCachePath();
00847
00848 double cache_max_age = DEFAULT_MAX_CACHE_AGE;
00849 const char *user_cache_time_str = getenv("ROS_CACHE_TIMEOUT");
00850 if(user_cache_time_str)
00851 cache_max_age = atof(user_cache_time_str);
00852 if(cache_max_age == 0.0)
00853 return false;
00854 struct stat s;
00855 if (stat(cache_path.c_str(), &s) == 0)
00856 {
00857 double dt = difftime(time(NULL), s.st_mtime);
00858 #ifdef VERBOSE_DEBUG
00859 printf("cache age: %f\n", dt);
00860 #endif
00861
00862
00863 if ((cache_max_age > 0.0) && (dt > cache_max_age))
00864 return false;
00865 }
00866
00867 FILE *cache = fopen(cache_path.c_str(), "r");
00868 if (!cache)
00869 return false;
00870
00871
00872 char linebuf[30000];
00873 bool ros_root_ok = false, ros_package_path_ok = false;
00874 const char *ros_package_path = getenv("ROS_PACKAGE_PATH");
00875 while (!feof(cache))
00876 {
00877 linebuf[0] = 0;
00878 if (!fgets(linebuf, sizeof(linebuf), cache))
00879 break;
00880 if (!linebuf[0])
00881 continue;
00882 linebuf[strlen(linebuf)-1] = 0;
00883 if (linebuf[0] == '#')
00884 {
00885 if (!strncmp("#ROS_ROOT=", linebuf, 10))
00886 {
00887 if (!strcmp(linebuf+10, ros_root))
00888 ros_root_ok = true;
00889 }
00890 else if (!strncmp("#ROS_PACKAGE_PATH=", linebuf, 18))
00891 {
00892 if (!ros_package_path)
00893 {
00894 if (!strlen(linebuf+18))
00895 ros_package_path_ok = true;
00896 }
00897 else if (!strcmp(linebuf+18, getenv("ROS_PACKAGE_PATH")))
00898 ros_package_path_ok = true;
00899 }
00900 }
00901 else
00902 break;
00903 }
00904 fclose(cache);
00905 return ros_root_ok && ros_package_path_ok;
00906 }
00907
00908 class CrawlQueueEntry
00909 {
00910 public:
00911 string path;
00912 double start_time, elapsed_time;
00913 CrawlQueueEntry(string _path)
00914 : path(_path), start_time(0), elapsed_time(0) { }
00915 bool operator>(const CrawlQueueEntry &rhs) const
00916 {
00917 return elapsed_time > rhs.elapsed_time;
00918 }
00919 };
00920
00921 double ROSStack::time_since_epoch()
00922 {
00923 #if defined(WIN32)
00924 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
00925 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
00926 #else
00927 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
00928 #endif
00929 FILETIME ft;
00930 unsigned __int64 tmpres = 0;
00931
00932 GetSystemTimeAsFileTime(&ft);
00933 tmpres |= ft.dwHighDateTime;
00934 tmpres <<= 32;
00935 tmpres |= ft.dwLowDateTime;
00936 tmpres /= 10;
00937 tmpres -= DELTA_EPOCH_IN_MICROSECS;
00938 return static_cast<double>(tmpres) / 1e6;
00939 #else
00940 struct timeval tod;
00941 gettimeofday(&tod, NULL);
00942 return tod.tv_sec + 1e-6 * tod.tv_usec;
00943 #endif
00944 }
00945
00946
00947 Stack* ROSStack::add_stack(string path)
00948 {
00949
00950 Stack* newp = new Stack(path);
00951 Stack* return_p = newp;
00952
00953 bool dup = false;
00954 for(std::vector<Stack *>::const_iterator it = Stack::stacks.begin();
00955 it != Stack::stacks.end();
00956 it++)
00957 {
00958 if((*it)->name == newp->name)
00959 {
00960 dup=true;
00961 return_p = *it;
00962 break;
00963 }
00964 }
00965 if(dup)
00966 delete newp;
00967 else
00968 Stack::stacks.push_back(newp);
00969
00970 return return_p;
00971 }
00972
00973 void ROSStack::crawl_for_stacks(bool force_crawl)
00974 {
00975 for (VecStack::iterator p = Stack::stacks.begin();
00976 p != Stack::stacks.end(); ++p)
00977 delete *p;
00978 Stack::stacks.clear();
00979
00980 if(!force_crawl && cache_is_good())
00981 {
00982 string cache_path = getCachePath();
00983 FILE *cache = fopen(cache_path.c_str(), "r");
00984 if (cache)
00985 {
00986 #ifdef VERBOSE_DEBUG
00987 printf("trying to use cache...\n");
00988 #endif
00989 char linebuf[30000];
00990 while (!feof(cache))
00991 {
00992 linebuf[0] = 0;
00993 if (!fgets(linebuf, sizeof(linebuf), cache))
00994 break;
00995 if (!linebuf[0] || linebuf[0] == '#')
00996 continue;
00997 char *newline_pos = strchr(linebuf, '\n');
00998 if (newline_pos)
00999 *newline_pos = 0;
01000
01001 add_stack(linebuf);
01002 }
01003 fclose(cache);
01004 return;
01005 }
01006 }
01007
01008
01009 #ifdef VERBOSE_DEBUG
01010 printf("building cache\n");
01011 #endif
01012 deque<CrawlQueueEntry> q;
01013 q.push_back(CrawlQueueEntry(ros_root));
01014 vector<string> rspvec;
01015
01016 char *rr = getenv("ROS_ROOT");
01017 if (!rr)
01018 {
01019 fprintf(stderr, "[rosstack] ERROR: ROS_ROOT not set.\n");
01020 exit(1);
01021 }
01022
01023
01024 add_stack(string(rr));
01025 string rsp;
01026 char *rpp = getenv("ROS_PACKAGE_PATH");
01027 if (rpp)
01028 rsp = string(rpp);
01029 string_split(rsp, rspvec, path_delim);
01030 sanitize_rppvec(rspvec);
01031 #ifdef VERBOSE_DEBUG
01032 printf("seeding crawler with [%s], which has %lu entries\n", rsp.c_str(), rspvec.size());
01033 #endif
01034
01035 for (vector<string>::iterator i = rspvec.begin(); i != rspvec.end(); ++i)
01036 {
01037 if (Stack::is_no_subdirs(*i))
01038 fprintf(stderr, "[rosstack] WARNING: non-stack directory in "
01039 "ROS_PACKAGE_PATH marked "
01040 "rosstack_nosubdirs:\n\t%s\n",
01041 i->c_str());
01042 else
01043 q.push_back(CrawlQueueEntry(*i));
01044 }
01045 const double crawl_start_time = time_since_epoch();
01046 priority_queue<CrawlQueueEntry, vector<CrawlQueueEntry>,
01047 greater<CrawlQueueEntry> > profile;
01048 while (!q.empty())
01049 {
01050 CrawlQueueEntry cqe = q.front();
01051 q.pop_front();
01052
01053
01054 if (Stack::is_stack(cqe.path))
01055 {
01056
01057 add_stack(cqe.path);
01058 continue;
01059 }
01060 else if (Stack::is_package(cqe.path))
01061 continue;
01062
01063
01064 if (g_profile_length > 0)
01065 {
01066 if (cqe.start_time != 0)
01067 {
01068
01069
01070 cqe.elapsed_time = time_since_epoch() - cqe.start_time;
01071 profile.push(cqe);
01072 if (profile.size() > g_profile_length)
01073 profile.pop();
01074 continue;
01075 }
01076 cqe.start_time = time_since_epoch();
01077 q.push_front(cqe);
01078 }
01079 #if defined(WIN32)
01080
01081 WIN32_FIND_DATA find_file_data;
01082 HANDLE hfind = INVALID_HANDLE_VALUE;
01083
01084 if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
01085 &find_file_data)) == INVALID_HANDLE_VALUE)
01086 {
01087 fprintf(stderr, "[rosstack] FindFirstFile error %u while crawling %s\n",
01088 GetLastError(), cqe.path.c_str());
01089 continue;
01090 }
01091
01092 do
01093 {
01094 if (!S_ISDIR(find_file_data.dwFileAttributes))
01095 continue;
01096 if (find_file_data.cFileName[0] == '.')
01097 continue;
01098 string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
01099 if (Stack::is_stack(child_path))
01100 continue;
01101 if (Stack::is_stack(child_path))
01102 {
01103
01104 Stack *newp = new Stack(child_path);
01105
01106
01107 bool dup = false;
01108 for(std::vector<Stack *>::const_iterator it = Stack::stacks.begin();
01109 it != Stack::stacks.end();
01110 it++)
01111 {
01112 if((*it)->name == newp->name)
01113 {
01114 dup=true;
01115 break;
01116 }
01117 }
01118 if(dup)
01119 delete newp;
01120 else
01121 Stack::stacks.push_back(newp);
01122 }
01123
01124 else if (!Stack::is_no_subdirs(child_path))
01125 q.push_front(CrawlQueueEntry(child_path));
01126 }
01127 while (FindNextFile(hfind, &find_file_data) != 0);
01128 DWORD last_error = GetLastError();
01129 FindClose(hfind);
01130 if (last_error != ERROR_NO_MORE_FILES)
01131 {
01132 fprintf(stderr, "[rosstack] FindNextFile error %u while crawling %s\n",
01133 GetLastError(), cqe.path.c_str());
01134 continue;
01135 }
01136 #else
01137 DIR *d = opendir(cqe.path.c_str());
01138 if (!d)
01139 {
01140 fprintf(stderr, "[rosstack] opendir error [%s] while crawling %s\n",
01141 strerror(errno), cqe.path.c_str());
01142 continue;
01143 }
01144 struct dirent *ent;
01145 while ((ent = readdir(d)) != NULL)
01146 {
01147 struct stat s;
01148 string child_path = cqe.path + fs_delim + string(ent->d_name);
01149 if (stat(child_path.c_str(), &s) != 0)
01150 continue;
01151 if (!S_ISDIR(s.st_mode))
01152 continue;
01153 if (ent->d_name[0] == '.')
01154 continue;
01155 else if (Stack::is_package(child_path))
01156 continue;
01157 if (Stack::is_stack(child_path))
01158 {
01159 add_stack(child_path);
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181 }
01182
01183 else if (!Stack::is_no_subdirs(child_path))
01184 q.push_front(CrawlQueueEntry(child_path));
01185 }
01186 closedir(d);
01187 #endif
01188 }
01189 crawled = true;
01190 const double crawl_elapsed_time = time_since_epoch() - crawl_start_time;
01191
01192 string cache_path = getCachePath();
01193 char tmp_cache_dir[PATH_MAX];
01194 char tmp_cache_path[PATH_MAX];
01195 strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
01196 #if defined(_MSC_VER)
01197
01198 char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
01199 _splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
01200 ext, _MAX_EXT);
01201 char full_dir[_MAX_DRIVE + _MAX_DIR];
01202 _makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
01203 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rosstack_cache.XXXXXX", full_dir);
01204 #else
01205 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rosstack_cache.XXXXXX", dirname(tmp_cache_dir));
01206 #endif
01207 #if defined(__MINGW32__)
01208
01209
01210
01211 FILE *cache = tmpfile();
01212 if ( cache == NULL ) {
01213 fprintf(stderr,
01214 "[rospack] Unable to generate temporary cache file name: %u",
01215 errno);
01216 }
01217 #elif defined(WIN32)
01218
01219
01220
01221 if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
01222 {
01223 fprintf(stderr,
01224 "[rosstack] Unable to generate temporary cache file name: %u",
01225 GetLastError());
01226 throw runtime_error(string("Failed to create tmp cache file name"));
01227 }
01228 FILE *cache = fopen(tmp_cache_path, "w");
01229 #else
01230 int fd = mkstemp(tmp_cache_path);
01231 if (fd < 0)
01232 {
01233 fprintf(stderr, "Unable to create temporary cache file: %s\n", tmp_cache_path);
01234 throw runtime_error(string("failed to create tmp cache file"));
01235 }
01236
01237 FILE *cache = fdopen(fd, "w");
01238 #endif
01239 if (!cache)
01240 {
01241 fprintf(stderr, "woah! couldn't create the cache file. Please check "
01242 "ROS_ROOT to make sure it's a writeable directory.\n");
01243 throw runtime_error(string("failed to create tmp cache file"));
01244 }
01245
01246 fprintf(cache, "#ROS_ROOT=%s\n#ROS_PACKAGE_PATH=%s\n", ros_root, rsp.c_str());
01247 for (VecStack::iterator s = Stack::stacks.begin();
01248 s != Stack::stacks.end(); ++s)
01249 fprintf(cache, "%s\n", (*s)->path.c_str());
01250 fclose(cache);
01251
01252 if(file_exists(cache_path.c_str()))
01253 remove(cache_path.c_str());
01254 if(rename(tmp_cache_path, cache_path.c_str()) < 0)
01255 {
01256 fprintf(stderr,
01257 "[rospack] Error: failed rename cache file %s to %s\n",
01258 tmp_cache_path, cache_path.c_str());
01259 perror("rename");
01260 throw runtime_error(string("failed to rename cache file"));
01261 }
01262
01263 if (g_profile_length)
01264 {
01265
01266 stack<CrawlQueueEntry> reverse_profile;
01267 while (!profile.empty())
01268 {
01269 reverse_profile.push(profile.top());
01270 profile.pop();
01271 }
01272 printf("\nFull tree crawl took %.6f seconds.\n", crawl_elapsed_time);
01273 printf("-------------------------------------------------------------\n");
01274 while (!reverse_profile.empty())
01275 {
01276 CrawlQueueEntry cqe = reverse_profile.top();
01277 reverse_profile.pop();
01278 printf("%.6f %s\n", cqe.elapsed_time, cqe.path.c_str());
01279 }
01280 printf("\n");
01281 }
01282 }
01283
01285
01286 void rosstack::string_split(const string &s, vector<string> &t, const string &d)
01287 {
01288 t.clear();
01289 size_t start = 0, end;
01290 while ((end = s.find_first_of(d, start)) != string::npos)
01291 {
01292 t.push_back(s.substr(start, end-start));
01293 start = end + 1;
01294 }
01295 if (start != s.length())
01296 t.push_back(s.substr(start));
01297 }
01298
01299 bool rosstack::file_exists(const string &fname)
01300 {
01301 return (access(fname.c_str(), F_OK) == 0);
01302 }
01303
01304 Stack *rosstack::g_get_stack(const string &name)
01305 {
01306 return g_rosstack->get_stack(name);
01307 }
01308
01309 void ROSStack::sanitize_rppvec(std::vector<std::string> &rppvec)
01310 {
01311
01312 for (size_t i = 0; i < rppvec.size(); i++)
01313 {
01314 size_t last_slash_pos = rppvec[i].find_last_of("/");
01315 if (last_slash_pos != string::npos &&
01316 last_slash_pos == rppvec[i].length()-1)
01317 {
01318 fprintf(stderr, "[rosstack] warning: trailing slash found in "
01319 "ROS_PACKAGE_PATH\n");
01320 rppvec[i].erase(last_slash_pos);
01321 }
01322 }
01323 }
01324