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