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 <stack>
00038 #include <queue>
00039 #include <cassert>
00040 #if !defined(WIN32)
00041 #include <unistd.h>
00042 #include <dirent.h>
00043 #include <sys/time.h>
00044 #include <sys/file.h>
00045 #endif
00046 #include <stdexcept>
00047 #include <time.h>
00048 #include <sstream>
00049 #include <iterator>
00050
00051
00052 #if defined(_MSC_VER) // msvc only
00053 #define F_OK 0x00
00054 #else // non msvc only
00055 #include <libgen.h>
00056 #endif
00057
00058 #if defined(WIN32) //
00059 #include <direct.h>
00060 #include <winsock2.h>
00061 #include <time.h>
00062 #include <windows.h>
00063 #include <io.h>
00064 #include <fcntl.h>
00065 #define PATH_MAX MAX_PATH
00066 #define snprintf _snprintf
00067 #define popen _popen
00068 #define pclose _pclose
00069 #define getcwd _getcwd
00070 #define mkdir(a,b) _mkdir(a)
00071 #define fdopen _fdopen
00072 #define access _access
00073 #endif
00074
00075
00076 #include "tinyxml-2.5.3/tinyxml.h"
00077 #include "rospack/rospack.h"
00078 using namespace std;
00079
00080
00081 const double DEFAULT_MAX_CACHE_AGE = 60.0;
00082 const int MAX_DEPENDENCY_TREE_DEPTH = 1000;
00083 const int MAX_DIRECTORY_DEPTH = 1000;
00084
00085
00086 #include <sys/stat.h>
00087 #ifndef S_ISDIR
00088 #if defined(WIN32)
00089 #define S_ISDIR(x) (((x) & FILE_ATTRIBUTE_DIRECTORY) != 0)
00090 #else
00091 #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
00092 #endif
00093 #endif
00094
00095 namespace rospack
00096 {
00097
00098 ROSPack *g_rospack = NULL;
00099
00100 #ifdef __APPLE__
00101 const string g_ros_os("osx");
00102 #else
00103 #if defined(WIN32)
00104 const string g_ros_os("win32");
00105 #else
00106 const string g_ros_os("linux");
00107 #endif
00108 #endif
00109
00110 #if defined(WIN32)
00111
00112
00113
00114 const char *fs_delim = "\\";
00115 const char *path_delim = ";";
00116 #else
00117 const char *fs_delim = "/";
00118 const char *path_delim = ":";
00119 #endif
00120
00121 inline string ToUnixPathDelim(string path)
00122 {
00123 #if defined(WIN32)
00124
00125
00126
00127 string token("\\");
00128 for (string::size_type ii = path.find(token); ii != string::npos;
00129 ii = path.find(token, ii))
00130 {
00131 path.replace(ii, token.length(), string("/"));
00132 }
00133 #endif
00134 return path;
00135 }
00136
00137 Package::Package(string _path) : path(_path),
00138 deps_calculated(false), direct_deps_calculated(false),
00139 descendants_calculated(false), manifest_loaded(false)
00140 {
00141 vector<string> name_tokens;
00142 string_split(path, name_tokens, fs_delim);
00143 name = name_tokens.back();
00144 }
00145 bool Package::is_package(string path)
00146 {
00147 return file_exists(path + string(fs_delim) + "manifest.xml");
00148 }
00149 bool Package::is_no_subdirs(string path)
00150 {
00151 return file_exists(path + string(fs_delim) + "rospack_nosubdirs");
00152 }
00153 const VecPkg &Package::deps1()
00154 {
00155 return direct_deps();
00156 }
00157 const VecPkg &Package::deps(traversal_order_t order, int depth)
00158 {
00159 if (depth > MAX_DEPENDENCY_TREE_DEPTH)
00160 {
00161 fprintf(stderr,"[rospack] woah! expanding the dependency tree made it blow "
00162 "up.\n There must be a circular dependency somewhere.\n");
00163 throw runtime_error(string("circular dependency"));
00164 }
00165 if (deps_calculated)
00166 return _deps;
00167
00168 VecPkg my_dd = direct_deps();
00169 for (VecPkg::iterator i = my_dd.begin(); i != my_dd.end(); ++i)
00170 {
00171 VecPkg d = (*i)->deps(order, depth+1);
00172 if (order == PREORDER)
00173 _deps.push_back(*i);
00174 for (VecPkg::iterator j = d.begin(); j != d.end(); ++j)
00175 {
00176
00177
00178 bool have = false;
00179 VecPkg::iterator prior_loc;
00180 for (VecPkg::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
00181 if ((*k) == (*j))
00182 {
00183 prior_loc = k;
00184 have = true;
00185 }
00186 if (have && order == PREORDER)
00187 {
00188 _deps.erase(prior_loc);
00189 _deps.push_back(*j);
00190 }
00191 else if (!have)
00192 _deps.push_back(*j);
00193 }
00194 if (order == POSTORDER)
00195 {
00196
00197 bool have = false;
00198 for (VecPkg::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
00199 if ((*k) == (*i))
00200 have = true;
00201 if (!have)
00202 _deps.push_back(*i);
00203 }
00204 }
00205 deps_calculated = true;
00206 return _deps;
00207 }
00208 string Package::manifest_path()
00209 {
00210 return path + string(fs_delim) + "manifest.xml";
00211 }
00212 string Package::flags(string lang, string attrib)
00213 {
00214 VecPkg d = deps(PREORDER);
00215 string s;
00216
00217
00218 if(!g_rospack->opt_deps_only)
00219 s += this->direct_flags(lang, attrib) + string(" ");
00220 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00221 {
00222 string f = (*i)->direct_flags(lang, attrib);
00223 if (f.length())
00224 s += f + string(" ");
00225 }
00226 return s;
00227 }
00228
00229 string Package::rosdep()
00230 {
00231 string sd;
00232 TiXmlElement *mroot = manifest_root();
00233 for(TiXmlElement *sd_ele = mroot->FirstChildElement("rosdep");
00234 sd_ele;
00235 sd_ele = sd_ele->NextSiblingElement("rosdep"))
00236 {
00237 const char *att_str;
00238 if((att_str = sd_ele->Attribute("name")))
00239 sd += string("name: ") + string(att_str);
00240 sd += string("\n");
00241 }
00242
00243 return sd;
00244 }
00245
00246 string Package::versioncontrol()
00247 {
00248 string sd;
00249 TiXmlElement *mroot = manifest_root();
00250 for(TiXmlElement *sd_ele = mroot->FirstChildElement("versioncontrol");
00251 sd_ele;
00252 sd_ele = sd_ele->NextSiblingElement("versioncontrol"))
00253 {
00254 const char *att_str;
00255 if((att_str = sd_ele->Attribute("type")))
00256 sd += string("type: ") + string(att_str);
00257 if((att_str = sd_ele->Attribute("url")))
00258 sd += string("\turl: ") + string(att_str);
00259 sd += string("\n");
00260 }
00261
00262 return sd;
00263 }
00264
00265 vector<pair<string, string> > Package::plugins()
00266 {
00267 vector<pair<string, string> > plugins;
00268
00269 VecPkg deplist;
00270
00271
00272 if(g_rospack->opt_top.size())
00273 {
00274 Package* gtp = g_get_pkg(g_rospack->opt_top);
00275 deplist = gtp->deps(Package::POSTORDER);
00276 deplist.push_back(gtp);
00277 }
00278
00279 VecPkg desc1 = descendants1();
00280 desc1.push_back(this);
00281 VecPkg::iterator it = desc1.begin();
00282 VecPkg::iterator end = desc1.end();
00283 for (; it != end; ++it)
00284 {
00285
00286
00287 if(deplist.size())
00288 {
00289 bool found = false;
00290 for(VecPkg::const_iterator dit = deplist.begin();
00291 dit != deplist.end();
00292 dit++)
00293 {
00294 if((*dit)->name == (*it)->name)
00295 {
00296 found = true;
00297 break;
00298 }
00299 }
00300 if(!found)
00301 continue;
00302 }
00303 std::string flags = (*it)->direct_flags(name, g_rospack->opt_attrib);
00304 if (!flags.empty())
00305 {
00306 plugins.push_back(make_pair((*it)->name, flags));
00307 }
00308 }
00309
00310 return plugins;
00311 }
00312
00313
00314 VecPkg Package::descendants1()
00315 {
00316 VecPkg children;
00317
00318
00319
00320
00321 VecPkg pkgs_copy(pkgs);
00322 for (VecPkg::iterator p = pkgs_copy.begin(); p != pkgs_copy.end(); ++p)
00323 {
00324
00325
00326 try
00327 {
00328 if ((*p)->has_parent(name))
00329 children.push_back(*p);
00330 }
00331 catch (runtime_error &e)
00332 {
00333 }
00334 }
00335 return children;
00336 }
00337
00338 const vector<Package *> &Package::descendants(int depth)
00339 {
00340 if (depth > MAX_DEPENDENCY_TREE_DEPTH)
00341 {
00342 fprintf(stderr, "[rospack] woah! circular dependency in the ros tree! aaaaaa!\n");
00343 throw runtime_error(string("circular dependency"));
00344 }
00345 if (descendants_calculated)
00346 return _descendants;
00347 VecPkg desc_with_dups;
00348 for (VecPkg::iterator p = pkgs.begin(); p != pkgs.end(); ++p)
00349 {
00350
00351
00352 try
00353 {
00354 if ((*p)->has_parent(name))
00355 {
00356 desc_with_dups.push_back(*p);
00357 const VecPkg &p_desc = (*p)->descendants(depth+1);
00358 for (VecPkg::const_iterator q = p_desc.begin();
00359 q != p_desc.end(); ++q)
00360 desc_with_dups.push_back(*q);
00361 }
00362 }
00363 catch (runtime_error &e)
00364 {
00365 }
00366 }
00367 _descendants.clear();
00368 for (VecPkg::iterator p = desc_with_dups.begin();
00369 p != desc_with_dups.end(); ++p)
00370 {
00371 bool found = false;
00372 for (VecPkg::iterator q = _descendants.begin();
00373 q != _descendants.end() && !found; ++q)
00374 if ((*q)->name == (*p)->name)
00375 found = true;
00376 if (!found)
00377 _descendants.push_back(*p);
00378 }
00379 descendants_calculated = true;
00380 return _descendants;
00381 }
00382
00383
00384 bool Package::has_parent(string pkg)
00385 {
00386 vector<Package *> parents = direct_deps(true);
00387 for (VecPkg::iterator i = parents.begin(); i != parents.end(); ++i)
00388 if ((*i)->name == pkg)
00389 return true;
00390 return false;
00391 }
00392
00393 const vector<Package *> &Package::direct_deps(bool missing_package_as_warning)
00394 {
00395 if (direct_deps_calculated)
00396 return _direct_deps;
00397 #ifdef VERBOSE_DEBUG
00398 fprintf(stderr, "calculating direct deps for package [%s]\n", name.c_str());
00399 #endif
00400 TiXmlElement *mroot = manifest_root();
00401 TiXmlNode *dep_node = 0;
00402 while ((dep_node = mroot->IterateChildren(string("depend"), dep_node)))
00403 {
00404 TiXmlElement *dep_ele = dep_node->ToElement();
00405 assert(dep_ele);
00406 const char *dep_pkgname = dep_ele->Attribute("package");
00407 if (!dep_pkgname)
00408 {
00409 fprintf(stderr,"[rospack] bad depend syntax (no 'package' attribute) in [%s]\n",
00410 manifest_path().c_str());
00411 throw runtime_error(string("invalid manifest"));
00412 }
00413 else if(dep_pkgname == name)
00414 {
00415 fprintf(stderr,"[rospack] package [%s] depends on itself (%s).\n",
00416 name.c_str(), manifest_path().c_str());
00417 throw runtime_error(string("self-dependency"));
00418 }
00419
00420
00421 char* dep_pkgname_copy = strdup(dep_pkgname);
00422 #ifdef VERBOSE_DEBUG
00423 fprintf(stderr, "direct_deps: pkg %s has dep %s\n", name.c_str(), dep_pkgname_copy);
00424 #endif
00425 try
00426 {
00427 _direct_deps.push_back(g_get_pkg(dep_pkgname_copy));
00428 }
00429 catch (runtime_error &e)
00430 {
00431 if (missing_package_as_warning)
00432 {
00433
00434 if(g_rospack->opt_warn_on_missing_deps)
00435 {
00436 fprintf(stderr, "[rospack] warning: couldn't find dependency [%s] of [%s]\n",
00437 dep_pkgname_copy, name.c_str());
00438 }
00439 }
00440 else
00441 {
00442 fprintf(stderr, "[rospack] couldn't find dependency [%s] of [%s]\n",
00443 dep_pkgname_copy, name.c_str());
00444 free(dep_pkgname_copy);
00445 throw runtime_error(string("missing dependency"));
00446 }
00447 }
00448 free(dep_pkgname_copy);
00449 }
00450 direct_deps_calculated = true;
00451 return _direct_deps;
00452 }
00453
00454 string Package::cpp_message_flags(bool cflags, bool lflags)
00455 {
00456 bool msg_exists = file_exists((path + "/msg_gen/generated").c_str());
00457 bool srv_exists = file_exists((path + "/srv_gen/generated").c_str());
00458
00459 string flags;
00460
00461 if (cflags)
00462 {
00463 if (msg_exists)
00464 {
00465 flags += string(" -I") + path + "/msg_gen/cpp/include";
00466 }
00467
00468 if (srv_exists)
00469 {
00470 flags += string(" -I") + path + "/srv_gen/cpp/include";
00471 }
00472 }
00473
00474
00475
00476 #if 0
00477 if (lflags)
00478 {
00479 if (msg_exists)
00480 {
00481 flags += string(" -L") + path + "/lib";
00482 flags += string(" -Wl,-rpath,") + path + "/lib";
00483 flags += " -l" + package->name + "msgs";
00484 }
00485
00486 if (srv_exists)
00487 {
00488
00489 if (!msg_exists)
00490 {
00491 flags += string(" -L") + path + "/lib";
00492 flags += string(" -Wl,-rpath,") + path + "/lib";
00493 }
00494
00495 flags += " -l" + package->name + "srvs";
00496 }
00497 }
00498 #endif
00499
00500 flags += " ";
00501 return flags;
00502 }
00503
00504 string Package::direct_flags(string lang, string attrib)
00505 {
00506 TiXmlElement *mroot = manifest_root();
00507 TiXmlElement *export_ele = mroot->FirstChildElement("export");
00508 string str;
00509 if (export_ele)
00510 {
00511 bool os_match = false;
00512 TiXmlElement *best_usage = NULL;
00513 for (TiXmlElement *lang_ele = export_ele->FirstChildElement(lang);
00514 lang_ele; lang_ele = lang_ele->NextSiblingElement(lang))
00515 {
00516 const char *os_str;
00517 if ((os_str = lang_ele->Attribute("os")))
00518 {
00519 if(g_ros_os == string(os_str))
00520 {
00521 if(os_match)
00522 {
00523 fprintf(stderr, "[rospack] warning: ignoring duplicate \"%s\" tag with os=\"%s\" in export block\n",
00524 lang.c_str(), os_str);
00525 }
00526 else
00527 {
00528 best_usage = lang_ele;
00529 os_match = true;
00530 }
00531 }
00532 }
00533 if(!os_match)
00534 {
00535 if (!best_usage)
00536 best_usage = lang_ele;
00537 else if(!os_str)
00538 {
00539 fprintf(stderr, "[rospack] warning: ignoring duplicate \"%s\" tag in export block\n",
00540 lang.c_str());
00541 }
00542 }
00543 }
00544
00545
00546
00547
00548 if (best_usage)
00549 {
00550 const char *cstr = best_usage->Attribute(attrib.c_str());
00551 if (cstr)
00552 {
00553 str = cstr;
00554 while (1)
00555 {
00556 int i = str.find(string("${prefix}"));
00557 if (i < 0)
00558 break;
00559 str.replace(i, string("${prefix}").length(), path);
00560 }
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572 string cmd = string("ret=\"") + str + string("\" && echo $ret");
00573
00574
00575 string token("\n");
00576 for (string::size_type s = cmd.find(token); s != string::npos;
00577 s = cmd.find(token, s))
00578 {
00579 cmd.replace(s,token.length(),string(" "));
00580 }
00581
00582 FILE* p;
00583 if(!(p = popen(cmd.c_str(), "r")))
00584 {
00585 fprintf(stderr, "[rospack] warning: failed to execute backquote "
00586 "expression \"%s\" in [%s]\n",
00587 cmd.c_str(), manifest_path().c_str());
00588 string errmsg = string("error in backquote expansion for ") + g_rospack->opt_package;
00589 throw runtime_error(errmsg);
00590 }
00591 else
00592 {
00593 char buf[8192];
00594 memset(buf,0,sizeof(buf));
00595
00596 do
00597 {
00598 clearerr(p);
00599 while(fgets(buf + strlen(buf),sizeof(buf)-strlen(buf)-1,p));
00600 } while(ferror(p) && errno == EINTR);
00601
00602 if(pclose(p) != 0)
00603 {
00604 fprintf(stderr, "[rospack] warning: got non-zero exit status from executing backquote expression \"%s\" in [%s]\n",
00605 cmd.c_str(), manifest_path().c_str());
00606 string errmsg = string("error in backquote expansion for ") + g_rospack->opt_package;
00607 throw runtime_error(errmsg);
00608 }
00609 else
00610 {
00611
00612 buf[strlen(buf)-1] = '\0';
00613
00614 str = string(buf);
00615 }
00616 }
00617 }
00618 }
00619 }
00620
00621 if (lang == "cpp")
00622 {
00623 if (attrib == "cflags")
00624 {
00625
00626 str += cpp_message_flags(true, false);
00627 }
00628 else if (attrib == "lflags")
00629 {
00630
00631 str += cpp_message_flags(false, true);
00632 }
00633 }
00634
00635 return str;
00636 }
00637
00638 void Package::load_manifest()
00639 {
00640 if (manifest_loaded)
00641 return;
00642 if (!manifest.LoadFile(manifest_path()))
00643 {
00644 string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
00645 fprintf(stderr, "[rospack] warning: error parsing manifest file at [%s]\n",
00646 manifest_path().c_str());
00647
00648 manifest_loaded = true;
00649 throw runtime_error(errmsg);
00650 }
00651 }
00652
00653 TiXmlElement *Package::manifest_root()
00654 {
00655 load_manifest();
00656 TiXmlElement *ele = manifest.RootElement();
00657 if (!ele)
00658 {
00659 string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
00660 throw runtime_error(errmsg);
00661 }
00662 return ele;
00663 }
00664
00665
00666 void Package::accumulate_deps(AccList& acc_list, Package* to)
00667 {
00668 VecPkg dd = direct_deps();
00669 for(VecPkg::iterator it = dd.begin();
00670 it != dd.end();
00671 ++it)
00672 {
00673 if((*it)->name == to->name)
00674 {
00675 Acc acc;
00676 acc.push_back(this);
00677 acc.push_back(to);
00678 acc_list.push_back(acc);
00679 }
00680 else
00681 {
00682 AccList l;
00683 (*it)->accumulate_deps(l, to);
00684 for(AccList::iterator lit = l.begin();
00685 lit != l.end();
00686 ++lit)
00687 {
00688 lit->push_front(this);
00689 acc_list.push_back(*lit);
00690 }
00691 }
00692 }
00693 }
00694
00695 VecPkg Package::pkgs;
00696 VecPkg Package::deleted_pkgs;
00697
00699
00700
00701 ROSPack::ROSPack() :
00702 ros_root(NULL),
00703 opt_deps_only(false),
00704 opt_profile_length(0),
00705 opt_profile_zombie_only(false),
00706 opt_warn_on_missing_deps(true),
00707 opt_display_duplicate_pkgs(false),
00708 opt_quiet(false),
00709 cache_lock_failed(false),
00710 crawled(false),
00711 my_argc(0),
00712 my_argv(NULL),
00713 total_num_pkgs(0),
00714 duplicate_packages_found(false)
00715 {
00716 g_rospack = this;
00717 Package::pkgs.reserve(500);
00718 ros_root = getenv("ROS_ROOT");
00719 if (!ros_root)
00720 {
00721 fprintf(stderr,"[rospack] ROS_ROOT is not defined in the environment.\n");
00722 throw runtime_error(string("no ROS_ROOT"));
00723 }
00724 if (!file_exists(ros_root))
00725 {
00726 fprintf(stderr,"[rospack] the path specified as ROS_ROOT is not "
00727 "accessible. Please ensure that this environment variable "
00728 "is set and is writeable by your user account.\n");
00729 throw runtime_error(string("no ROS_ROOT"));
00730 }
00731
00732 crawl_for_packages();
00733 }
00734
00735 ROSPack::~ROSPack()
00736 {
00737 for (VecPkg::iterator p = Package::pkgs.begin();
00738 p != Package::pkgs.end(); ++p)
00739 delete (*p);
00740 Package::pkgs.clear();
00741 for (VecPkg::iterator p = Package::deleted_pkgs.begin();
00742 p != Package::deleted_pkgs.end(); ++p)
00743 delete (*p);
00744 Package::deleted_pkgs.clear();
00745 freeArgv();
00746 }
00747
00748 const char* ROSPack::usage()
00749 {
00750 return "USAGE: rospack <command> [options] [package]\n"
00751 " Allowed commands:\n"
00752 " help\n"
00753 " find [package]\n"
00754 " list\n"
00755 " list-names\n"
00756 " list-duplicates\n"
00757 " langs\n"
00758 " depends [package] (alias: deps)\n"
00759 " depends-manifests [package] (alias: deps-manifests)\n"
00760 " depends-msgsrv [package] (alias: deps-msgsrv)\n"
00761 " depends1 [package] (alias: deps1)\n"
00762 " depends-indent [package] (alias: deps-indent)\n"
00763 " depends-why --target=<target> [package] (alias: deps-why)\n"
00764 " rosdep [package] (alias: rosdeps)\n"
00765 " rosdep0 [package] (alias: rosdeps0)\n"
00766 " vcs [package]\n"
00767 " vcs0 [package]\n"
00768 " depends-on [package]\n"
00769 " depends-on1 [package]\n"
00770 " export [--deps-only] --lang=<lang> --attrib=<attrib> [package]\n"
00771 " plugins --attrib=<attrib> [--top=<toppkg>] [package]\n"
00772 " cflags-only-I [--deps-only] [package]\n"
00773 " cflags-only-other [--deps-only] [package]\n"
00774 " libs-only-L [--deps-only] [package]\n"
00775 " libs-only-l [--deps-only] [package]\n"
00776 " libs-only-other [--deps-only] [package]\n"
00777 " profile [--length=<length>] [--zombie-only]\n"
00778 " Extra options:\n"
00779 " -q Quiets error reports.\n\n"
00780 " If [package] is omitted, the current working directory\n"
00781 " is used (if it contains a manifest.xml).\n\n";
00782 }
00783
00784 Package *ROSPack::get_pkg(string pkgname)
00785 {
00786 for (VecPkg::iterator p = Package::pkgs.begin();
00787 p != Package::pkgs.end(); ++p)
00788 {
00789 if ((*p)->name == pkgname)
00790 {
00791 if(!crawled)
00792 {
00793
00794
00795 std::string manifest_path = (*p)->path + fs_delim + "manifest.xml";
00796 struct stat s;
00797 int ret;
00798 while((ret = stat(manifest_path.c_str(), &s)) != 0 &&
00799 errno == EINTR);
00800 if(ret == 0)
00801 {
00802
00803 return (*p);
00804 }
00805 else
00806 {
00807
00808 fprintf(stderr, "[rospack] warning: invalid cached location %s for package %s; forcing recrawl\n",
00809 (*p)->path.c_str(),
00810 (*p)->name.c_str());
00811 break;
00812 }
00813 }
00814 else
00815 {
00816
00817 return (*p);
00818 }
00819 }
00820 }
00821 if (!crawled)
00822 {
00823 crawl_for_packages(true);
00824 return get_pkg(pkgname);
00825 }
00826 string errmsg = string("couldn't find package [") + pkgname + string("]");
00827 throw runtime_error(errmsg);
00828 return NULL;
00829 }
00830
00831 int ROSPack::cmd_depends_on(bool include_indirect)
00832 {
00833
00834
00835
00836
00837
00838 if(opt_package.size() == 0)
00839 {
00840 string errmsg = string("no package name given, and current directory is not a package root");
00841 throw runtime_error(errmsg);
00842 }
00843
00844
00845 opt_warn_on_missing_deps = false;
00846
00847
00848
00849
00850
00851 crawl_for_packages(true);
00852
00853 Package* p;
00854 try
00855 {
00856 p = get_pkg(opt_package);
00857 }
00858 catch(runtime_error)
00859 {
00860 fprintf(stderr, "[rospack] warning: package %s doesn't exist\n",
00861 opt_package.c_str());
00862
00863
00864 p = add_package(opt_package);
00865 }
00866 assert(p);
00867 const VecPkg descendants = include_indirect ? p->descendants()
00868 : p->descendants1();
00869 for (VecPkg::const_iterator p = descendants.begin();
00870 p != descendants.end(); ++p)
00871 {
00872
00873 output_acc += (*p)->name + "\n";
00874 }
00875 return 0;
00876 }
00877
00878
00879 int ROSPack::cmd_depends_why()
00880 {
00881 AccList acc_list;
00882 Package* from = get_pkg(opt_package);
00883 Package* to = get_pkg(opt_target);
00884 from->accumulate_deps(acc_list, to);
00885 printf("Dependency chains from %s to %s:\n",
00886 from->name.c_str(), to->name.c_str());
00887 for(AccList::iterator lit = acc_list.begin();
00888 lit != acc_list.end();
00889 ++lit)
00890 {
00891 printf("* ");
00892 for(Acc::iterator ait = lit->begin();
00893 ait != lit->end();
00894 ++ait)
00895 {
00896 if(ait != lit->begin())
00897 printf("-> ");
00898 printf("%s ", (*ait)->name.c_str());
00899 }
00900 printf("\n");
00901 }
00902 return 0;
00903 }
00904
00905 int ROSPack::cmd_find()
00906 {
00907
00908 Package *p = get_pkg(opt_package);
00909
00910 output_acc += ToUnixPathDelim(p->path) + "\n";
00911 return 0;
00912 }
00913
00914 int ROSPack::cmd_deps()
00915 {
00916 VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
00917 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00918 {
00919
00920 output_acc += ToUnixPathDelim((*i)->name) + "\n";
00921 }
00922 return 0;
00923 }
00924
00925 int ROSPack::cmd_deps_manifests()
00926 {
00927 VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
00928 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00929 {
00930
00931 output_acc += ToUnixPathDelim((*i)->path + "/manifest.xml ");
00932 }
00933
00934 output_acc += "\n";
00935 return 0;
00936 }
00937
00938 int ROSPack::cmd_deps_msgsrv()
00939 {
00940 VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
00941 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00942 {
00943 Package* p = *i;
00944 bool msg_exists = file_exists(ToUnixPathDelim(p->path + "/msg_gen/generated").c_str());
00945 bool srv_exists = file_exists(ToUnixPathDelim(p->path + "/srv_gen/generated").c_str());
00946
00947 if (msg_exists)
00948 {
00949 output_acc += ToUnixPathDelim(p->path + "/msg_gen/generated ");
00950 }
00951
00952 if (srv_exists)
00953 {
00954 output_acc += ToUnixPathDelim(p->path + "/srv_gen/generated ");
00955 }
00956 }
00957 output_acc += "\n";
00958 return 0;
00959 }
00960
00961 int ROSPack::cmd_deps1()
00962 {
00963 VecPkg d = get_pkg(opt_package)->deps1();
00964 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00965 {
00966
00967 output_acc += ToUnixPathDelim((*i)->name) + "\n";
00968 }
00969 return 0;
00970 }
00971
00972 int ROSPack::cmd_depsindent(Package* pkg, int indent)
00973 {
00974 VecPkg d = pkg->deps1();
00975
00976 for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
00977 {
00978 for(int s=0; s<indent; s++)
00979 {
00980
00981 output_acc += " ";
00982 }
00983
00984 output_acc += ToUnixPathDelim((*i)->name) + "\n";
00985 cmd_depsindent(*i, indent+2);
00986 }
00987 return 0;
00988 }
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005 static bool space(char c) { return isspace(c); }
01006 static bool not_space(char c) { return !isspace(c); }
01007 static vector<string> split_space(const string& str)
01008 {
01009 typedef string::const_iterator iter;
01010 vector<string> ret;
01011 iter i = str.begin();
01012 while (i != str.end())
01013 {
01014 i = find_if(i, str.end(), not_space);
01015 iter j = find_if(i, str.end(), space);
01016 if (i != str.end())
01017 while (j != str.end() && *(j-1) == '\\')
01018 j = find_if(j+1, str.end(), space);
01019 ret.push_back(string(i, j));
01020 i = j;
01021 }
01022 return ret;
01023 }
01024
01025 string ROSPack::snarf_libs(string flags, bool invert)
01026 {
01027 vector<string> tokens = split_space(flags);
01028 string snarfed;
01029 for (size_t i = 0; i < tokens.size(); ++i)
01030 {
01031
01032 if (invert)
01033 {
01034 if ((tokens[i].substr(0, 2) != "-l") &&
01035 (tokens[i].length() < 2 || tokens[i][0] != '/' || tokens[i].substr(tokens[i].length()-2) != ".a"))
01036 snarfed += (snarfed.length() ? " " : "" ) + tokens[i];
01037 }
01038 else
01039 {
01040 if (tokens[i].substr(0, 2) == "-l")
01041 snarfed += (snarfed.length() ? " " : "" ) + tokens[i].substr(2);
01042 else if (tokens[i].length() > 2 && tokens[i][0] == '/' && tokens[i].substr(tokens[i].length()-2) == ".a")
01043 snarfed += (snarfed.length() ? " " : "" ) + tokens[i];
01044 }
01045 }
01046 return snarfed;
01047 }
01048
01049 string ROSPack::snarf_flags(string flags, string prefix, bool invert)
01050 {
01051 vector<string> tokens = split_space(flags);
01052 string snarfed;
01053 for (size_t i = 0; i < tokens.size(); ++i)
01054 {
01055 if ((tokens[i].substr(0, prefix.length()) == prefix) ^ invert)
01056 {
01057 snarfed += (snarfed.length() ? " " : "" ) + tokens[i].substr(invert ? 0 : prefix.length());
01058 }
01059 }
01060 return snarfed;
01061 }
01062
01063 int ROSPack::cmd_libs_only(string token)
01064 {
01065 string lflags = get_pkg(opt_package)->flags("cpp", "lflags");;
01066 if(!token.compare("-other"))
01067 {
01068 lflags = snarf_libs(lflags, true);
01069 lflags = snarf_flags(lflags, "-L", true);
01070 }
01071 else if(!token.compare("-l"))
01072 {
01073 lflags = snarf_libs(lflags);
01074 }
01075 else
01076 {
01077 lflags = snarf_flags(lflags, token);
01078 lflags = deduplicate_tokens(lflags);
01079 }
01080
01081 output_acc += ToUnixPathDelim(lflags) + "\n";
01082 return 0;
01083 }
01084
01085 int ROSPack::cmd_cflags_only(string token)
01086 {
01087 string cflags = get_pkg(opt_package)->flags("cpp", "cflags");
01088 if(!token.compare("-other"))
01089 cflags = snarf_flags(cflags, "-I", true);
01090 else
01091 {
01092 cflags = snarf_flags(cflags, token);
01093 cflags = deduplicate_tokens(cflags);
01094 }
01095
01096 output_acc += ToUnixPathDelim(cflags) + "\n";
01097 return 0;
01098 }
01099
01100 void ROSPack::export_flags(string pkg, string lang, string attrib)
01101 {
01102 string flags = get_pkg(pkg)->flags(lang, attrib);
01103
01104 output_acc += ToUnixPathDelim(flags) + "\n";
01105 }
01106
01107 int ROSPack::cmd_versioncontrol(int depth)
01108 {
01109 string sds;
01110
01111 sds += get_pkg(opt_package)->versioncontrol();
01112
01113 if(depth < 0)
01114 {
01115 VecPkg descs = get_pkg(opt_package)->deps(Package::POSTORDER);
01116 for(VecPkg::iterator dit = descs.begin();
01117 dit != descs.end();
01118 dit++)
01119 {
01120 sds += (*dit)->versioncontrol();
01121 }
01122 }
01123
01124
01125 output_acc += sds;
01126 return 0;
01127 }
01128
01129 int ROSPack::cmd_rosdep(int depth)
01130 {
01131 string sds;
01132 sds += get_pkg(opt_package)->rosdep();
01133
01134 if(depth < 0)
01135 {
01136 VecPkg descs = get_pkg(opt_package)->deps(Package::POSTORDER);
01137 for(VecPkg::iterator dit = descs.begin();
01138 dit != descs.end();
01139 dit++)
01140 {
01141 sds += (*dit)->rosdep();
01142 }
01143 }
01144
01145
01146 output_acc += sds;
01147 return 0;
01148 }
01149
01150 int ROSPack::cmd_export()
01151 {
01152 export_flags(opt_package, opt_lang, opt_attrib);
01153 return 0;
01154 }
01155
01156 int ROSPack::cmd_plugins()
01157 {
01158
01159 opt_warn_on_missing_deps = false;
01160
01161 Package* p = get_pkg(opt_package);
01162
01163 vector<pair<string, string> > plugins = p->plugins();
01164 vector<pair<string, string> >::iterator it = plugins.begin();
01165 vector<pair<string, string> >::iterator end = plugins.end();
01166 for (; it != end; ++it)
01167 {
01168
01169 output_acc += it->first + " " + it->second + "\n";
01170 }
01171
01172 return 0;
01173 }
01174
01175 void ROSPack::freeArgv()
01176 {
01177 if(my_argc)
01178 {
01179 for(int i=0;i<my_argc;i++)
01180 free(my_argv[i]);
01181 free(my_argv);
01182 }
01183 my_argc = 0;
01184 my_argv = NULL;
01185 }
01186
01187 int ROSPack::run(const std::string& cmd)
01188 {
01189 std::vector<std::string> cmd_list;
01190
01191
01192
01193
01194 string_split(cmd, cmd_list, " ");
01195
01196
01197 freeArgv();
01198
01199 my_argc = (int)cmd_list.size() + 1;
01200 my_argv = (char**)malloc(sizeof(char*) * my_argc);
01201 my_argv[0] = strdup("rospack");
01202 for(int i=1;i<my_argc;i++)
01203 my_argv[i] = strdup(cmd_list[i-1].c_str());
01204
01205 return run(my_argc, my_argv);
01206 }
01207
01208 int ROSPack::run(int argc, char **argv)
01209 {
01210 assert(argc >= 2);
01211 int i;
01212 const char* opt_deps_name = "--deps-only";
01213 const char* opt_zombie_name = "--zombie-only";
01214 const char* opt_lang_name = "--lang=";
01215 const char* opt_attrib_name = "--attrib=";
01216 const char* opt_length_name = "--length=";
01217 const char* opt_top_name = "--top=";
01218 const char* opt_target_name = "--target=";
01219 const char* opt_quiet_name = "-q";
01220
01221
01222 opt_deps_only = false;
01223
01224 opt_lang = string("");
01225
01226 opt_attrib = string("");
01227
01228 opt_length = string("");
01229
01230 opt_top = string("");
01231
01232 opt_target = string("");
01233
01234 opt_package = string("");
01235
01236 opt_profile_length = 0;
01237
01238 opt_profile_zombie_only = false;
01239
01240 opt_warn_on_missing_deps = true;
01241
01242 opt_display_duplicate_pkgs = false;
01243
01244 output_acc = string("");
01245
01246 string errmsg = string(usage());
01247
01248 i=1;
01249 const char* cmd = argv[i++];
01250
01251 for(;i<argc;i++)
01252 {
01253 if(!strcmp(argv[i], opt_deps_name))
01254 opt_deps_only=true;
01255 else if(!strcmp(argv[i], opt_zombie_name))
01256 opt_profile_zombie_only=true;
01257 else if(!strcmp(argv[i], opt_quiet_name))
01258 opt_quiet=true;
01259 else if(!strncmp(argv[i], opt_target_name, strlen(opt_target_name)))
01260 {
01261 if(opt_target.size())
01262 throw runtime_error(errmsg);
01263 else if(strlen(argv[i]) > strlen(opt_target_name))
01264 opt_target = string(argv[i]+strlen(opt_target_name));
01265 else
01266 throw runtime_error(errmsg);
01267 }
01268 else if(!strncmp(argv[i], opt_lang_name, strlen(opt_lang_name)))
01269 {
01270 if(opt_lang.size())
01271 throw runtime_error(errmsg);
01272 else if(strlen(argv[i]) > strlen(opt_lang_name))
01273 opt_lang = string(argv[i]+strlen(opt_lang_name));
01274 else
01275 throw runtime_error(errmsg);
01276 }
01277 else if(!strncmp(argv[i], opt_attrib_name, strlen(opt_attrib_name)))
01278 {
01279 if(opt_attrib.size())
01280 throw runtime_error(errmsg);
01281 else if(strlen(argv[i]) > strlen(opt_attrib_name))
01282 opt_attrib = string(argv[i]+strlen(opt_attrib_name));
01283 else
01284 throw runtime_error(errmsg);
01285 }
01286 else if(!strncmp(argv[i], opt_length_name, strlen(opt_length_name)))
01287 {
01288 if(strlen(argv[i]) > strlen(opt_length_name))
01289 opt_length = string(argv[i]+strlen(opt_length_name));
01290 else
01291 throw runtime_error(errmsg);
01292 }
01293 else if(!strncmp(argv[i], opt_top_name, strlen(opt_top_name)))
01294 {
01295 if(strlen(argv[i]) > strlen(opt_top_name))
01296 opt_top = string(argv[i]+strlen(opt_top_name));
01297 else
01298 throw runtime_error(errmsg);
01299 }
01300 else
01301 break;
01302 }
01303
01304 if((strcmp(cmd, "depends-why") && strcmp(cmd, "deps-why")) && opt_target.size())
01305 throw runtime_error(errmsg);
01306
01307 if((!strcmp(cmd, "depends-why") || !strcmp(cmd, "deps-why")) && !opt_target.size())
01308 throw runtime_error(errmsg);
01309
01310 if(strcmp(cmd, "profile") && (opt_length.size() || opt_profile_zombie_only))
01311 throw runtime_error(errmsg);
01312
01313
01314 if(strcmp(cmd, "plugins") && opt_top.size())
01315 throw runtime_error(errmsg);
01316
01317
01318 if((strcmp(cmd, "export") && strcmp(cmd, "plugins")) &&
01319 opt_attrib.size())
01320 throw runtime_error(errmsg);
01321
01322
01323 if((strcmp(cmd, "export") && opt_lang.size()))
01324 throw runtime_error(errmsg);
01325
01326
01327 if(!strcmp(cmd, "export") && (!opt_lang.size() || !opt_attrib.size()))
01328 throw runtime_error(errmsg);
01329
01330
01331 if(!strcmp(cmd, "plugins") && !opt_attrib.size())
01332 throw runtime_error(errmsg);
01333
01334 if(opt_deps_only &&
01335 strcmp(cmd, "export") &&
01336 strcmp(cmd, "cflags-only-I") &&
01337 strcmp(cmd, "cflags-only-other") &&
01338 strcmp(cmd, "libs-only-L") &&
01339 strcmp(cmd, "libs-only-l") &&
01340 strcmp(cmd, "libs-only-other"))
01341 throw runtime_error(errmsg);
01342
01343 if(i < argc)
01344 {
01345 if(!strcmp(cmd, "help") ||
01346 !strcmp(cmd, "list") ||
01347 !strcmp(cmd, "list-names") ||
01348 !strcmp(cmd, "list-duplicates") ||
01349 !strcmp(cmd, "langs") ||
01350 !strcmp(cmd, "profile"))
01351 throw runtime_error(errmsg);
01352
01353 opt_package = string(argv[i++]);
01354 }
01355
01356 else
01357 {
01358 char buf[1024];
01359 if(getcwd(buf,sizeof(buf)))
01360 {
01361 if(Package::is_package("."))
01362 {
01363 #if defined(_MSC_VER)
01364
01365 char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
01366 _splitpath_s(buf, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
01367 ext, _MAX_EXT);
01368 char filename[_MAX_FNAME + _MAX_EXT];
01369 if (ext[0] != '\0')
01370 {
01371 _makepath_s(filename, _MAX_FNAME + _MAX_EXT, NULL, NULL, fname, ext);
01372 opt_package = string(filename);
01373 }
01374 else
01375 opt_package = string(fname);
01376 #else
01377 opt_package = string(basename(buf));
01378 #endif
01379 }
01380 }
01381 }
01382
01383 if (i != argc)
01384 throw runtime_error(errmsg);
01385
01386 if (!strcmp(cmd, "profile"))
01387 {
01388 if (opt_length.size())
01389 opt_profile_length = atoi(opt_length.c_str());
01390 else
01391 {
01392 if(opt_profile_zombie_only)
01393 opt_profile_length = -1;
01394 else
01395 opt_profile_length = 20;
01396 }
01397 #ifdef VERBOSE_DEBUG
01398 fprintf(stderr, "profile_length = %d\n", opt_profile_length);
01399 #endif
01400
01401 crawl_for_packages(true);
01402 return 0;
01403 }
01404 else if (!strcmp(cmd, "find"))
01405 return cmd_find();
01406 else if (!strcmp(cmd, "list"))
01407 return cmd_print_package_list(true);
01408 else if (!strcmp(cmd, "list-names"))
01409 return cmd_print_package_list(false);
01410 else if (!strcmp(cmd, "list-duplicates"))
01411 return cmd_list_duplicates();
01412 else if (!strcmp(cmd, "langs"))
01413 return cmd_print_langs_list();
01414 else if (!strcmp(cmd, "depends") || !strcmp(cmd, "deps"))
01415 return cmd_deps();
01416 else if (!strcmp(cmd, "depends-manifests") || !strcmp(cmd, "deps-manifests"))
01417 return cmd_deps_manifests();
01418 else if (!strcmp(cmd, "depends-msgsrv") || !strcmp(cmd, "deps-msgsrv"))
01419 return cmd_deps_msgsrv();
01420 else if (!strcmp(cmd, "depends1") || !strcmp(cmd, "deps1"))
01421 return cmd_deps1();
01422 else if (!strcmp(cmd, "depends-indent") || !strcmp(cmd, "deps-indent"))
01423 return cmd_depsindent(get_pkg(opt_package), 0);
01424 else if (!strcmp(cmd, "depends-on"))
01425 return cmd_depends_on(true);
01426 else if (!strcmp(cmd, "depends-why") || !strcmp(cmd, "deps-why"))
01427 return cmd_depends_why();
01428 else if (!strcmp(cmd, "depends-on1"))
01429 return cmd_depends_on(false);
01430
01431
01432
01433
01434 else if (!strcmp(cmd, "export"))
01435 return cmd_export();
01436 else if (!strcmp(cmd, "plugins"))
01437 return cmd_plugins();
01438 else if (!strcmp(cmd, "rosdep") || !strcmp(cmd, "rosdeps"))
01439 return cmd_rosdep(-1);
01440 else if (!strcmp(cmd, "rosdep0") || !strcmp(cmd, "rosdeps0"))
01441 return cmd_rosdep(0);
01442 else if (!strcmp(cmd, "vcs"))
01443 return cmd_versioncontrol(-1);
01444 else if (!strcmp(cmd, "vcs0"))
01445 return cmd_versioncontrol(0);
01446 else if (!strcmp(cmd, "libs-only-l"))
01447 return cmd_libs_only("-l");
01448 else if (!strcmp(cmd, "libs-only-L"))
01449 return cmd_libs_only("-L");
01450 else if (!strcmp(cmd, "libs-only-other"))
01451 return cmd_libs_only("-other");
01452 else if (!strcmp(cmd, "cflags-only-I"))
01453 return cmd_cflags_only("-I");
01454 else if (!strcmp(cmd, "cflags-only-other"))
01455 return cmd_cflags_only("-other");
01456 else if (!strcmp(cmd, "help"))
01457 fputs(usage(), stderr);
01458 else
01459 {
01460 throw runtime_error(errmsg);
01461 }
01462 return 0;
01463 }
01464
01465 int ROSPack::cmd_print_package_list(bool print_path)
01466 {
01467 for (VecPkg::iterator i = Package::pkgs.begin();
01468 i != Package::pkgs.end(); ++i)
01469 if (print_path)
01470 {
01471
01472 output_acc += (*i)->name + " " + (*i)->path + "\n";
01473 }
01474 else
01475 {
01476
01477 output_acc += (*i)->name + "\n";
01478 }
01479 return 0;
01480 }
01481
01482 int ROSPack::cmd_list_duplicates()
01483 {
01484
01485 opt_display_duplicate_pkgs = true;
01486 crawl_for_packages(true);
01487
01488
01489 if(duplicate_packages_found)
01490 return 1;
01491 else
01492 return 0;
01493 }
01494
01495 int ROSPack::cmd_print_langs_list()
01496 {
01497
01498 opt_warn_on_missing_deps = false;
01499
01500
01501 VecPkg lang_pkgs;
01502 Package* roslang;
01503
01504 roslang = get_pkg("roslang");
01505 assert(roslang);
01506
01507 lang_pkgs = roslang->descendants1();
01508
01509
01510 char *disable = getenv("ROS_LANG_DISABLE");
01511 vector<string> disable_list;
01512 if(disable)
01513 string_split(disable, disable_list, ":");
01514
01515 for(VecPkg::const_iterator i = lang_pkgs.begin();
01516 i != lang_pkgs.end();
01517 ++i)
01518 {
01519 vector<string>::const_iterator j;
01520 for(j = disable_list.begin();
01521 j != disable_list.end();
01522 ++j)
01523 {
01524 if((*j) == (*i)->name)
01525 break;
01526 }
01527 if(j == disable_list.end())
01528 {
01529
01530 output_acc += (*i)->name + " ";
01531 }
01532 }
01533
01534 output_acc += "\n";
01535 return 0;
01536 }
01537
01538 string ROSPack::getCachePath()
01539 {
01540 string cache_file_name;
01541 char* ros_home = getenv("ROS_HOME");
01542
01543 if (ros_home)
01544 {
01545
01546
01547
01548
01549 #if defined(WIN32)
01550 std::string ros_home_slash = ros_home + std::string("\\");
01551 #else
01552 std::string ros_home_slash = ros_home + std::string("/");
01553 #endif
01554 struct stat s;
01555 if(stat(ros_home_slash.c_str(), &s))
01556 {
01557 if(mkdir(ros_home_slash.c_str(), 0700) != 0)
01558 {
01559 perror("[rospack] WARNING: cannot create rospack cache directory");
01560 }
01561 }
01562 cache_file_name = ros_home_slash + std::string("rospack_cache");
01563 }
01564 else
01565 {
01566
01567
01568 ros_home = getenv("HOME");
01569 if (ros_home)
01570 {
01571
01572
01573 std::string dotros = ros_home + std::string("/.ros/");
01574 struct stat s;
01575 if(stat(dotros.c_str(), &s))
01576 {
01577 if(mkdir(dotros.c_str(), 0700) != 0)
01578 perror("[rospack] WARNING: cannot create rospack cache directory");
01579 }
01580 cache_file_name = dotros + "rospack_cache";
01581 }
01582 }
01583 return cache_file_name;
01584 }
01585
01586 bool ROSPack::cache_is_good()
01587 {
01588 string cache_path = getCachePath();
01589
01590 double cache_max_age = DEFAULT_MAX_CACHE_AGE;
01591 const char *user_cache_time_str = getenv("ROS_CACHE_TIMEOUT");
01592 if(user_cache_time_str)
01593 cache_max_age = atof(user_cache_time_str);
01594 if(cache_max_age == 0.0)
01595 return false;
01596 struct stat s;
01597 if (stat(cache_path.c_str(), &s) == 0)
01598 {
01599 double dt = difftime(time(NULL), s.st_mtime);
01600 #ifdef VERBOSE_DEBUG
01601 fprintf(stderr, "cache age: %f\n", dt);
01602 #endif
01603
01604
01605 if ((cache_max_age > 0.0) && (dt > cache_max_age))
01606 return false;
01607 }
01608
01609 FILE *cache = fopen(cache_path.c_str(), "r");
01610 if (!cache)
01611 return false;
01612
01613
01614 char linebuf[30000];
01615 bool ros_root_ok = false, ros_package_path_ok = false;
01616 const char *ros_package_path = getenv("ROS_PACKAGE_PATH");
01617 for(;;)
01618 {
01619 if (!fgets(linebuf, sizeof(linebuf), cache))
01620 break;
01621 linebuf[strlen(linebuf)-1] = 0;
01622 if (linebuf[0] == '#')
01623 {
01624 if (!strncmp("#ROS_ROOT=", linebuf, 10))
01625 {
01626 if (!strcmp(linebuf+10, ros_root))
01627 ros_root_ok = true;
01628 }
01629 else if (!strncmp("#ROS_PACKAGE_PATH=", linebuf, 18))
01630 {
01631 if (!ros_package_path)
01632 {
01633 if (!strlen(linebuf+18))
01634 ros_package_path_ok = true;
01635 }
01636 else if (!strcmp(linebuf+18, getenv("ROS_PACKAGE_PATH")))
01637 ros_package_path_ok = true;
01638 }
01639 }
01640 else
01641 break;
01642 }
01643 fclose(cache);
01644 return ros_root_ok && ros_package_path_ok;
01645 }
01646
01647 class CrawlQueueEntry
01648 {
01649 public:
01650 string path;
01651 double start_time, elapsed_time;
01652 size_t start_num_pkgs;
01653 bool has_manifest;
01654 CrawlQueueEntry(string _path)
01655 : path(_path), start_time(0), elapsed_time(0),
01656 start_num_pkgs(0), has_manifest(false){ }
01657 bool operator>(const CrawlQueueEntry &rhs) const
01658 {
01659 return elapsed_time > rhs.elapsed_time;
01660 }
01661 };
01662
01663 double ROSPack::time_since_epoch()
01664 {
01665 #if defined(WIN32)
01666 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
01667 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
01668 #else
01669 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
01670 #endif
01671 FILETIME ft;
01672 unsigned __int64 tmpres = 0;
01673
01674 GetSystemTimeAsFileTime(&ft);
01675 tmpres |= ft.dwHighDateTime;
01676 tmpres <<= 32;
01677 tmpres |= ft.dwLowDateTime;
01678 tmpres /= 10;
01679 tmpres -= DELTA_EPOCH_IN_MICROSECS;
01680 return static_cast<double>(tmpres) / 1e6;
01681 #else
01682 struct timeval tod;
01683 gettimeofday(&tod, NULL);
01684 return tod.tv_sec + 1e-6 * tod.tv_usec;
01685 #endif
01686 }
01687
01688
01689 Package* ROSPack::add_package(string path)
01690 {
01691
01692 Package* newp = new Package(path);
01693 Package* return_p = newp;
01694
01695 bool dup = false;
01696 for(std::vector<Package *>::const_iterator it = Package::pkgs.begin();
01697 it != Package::pkgs.end();
01698 it++)
01699 {
01700 if((*it)->name == newp->name)
01701 {
01702 dup=true;
01703 return_p = *it;
01704
01705 if(opt_display_duplicate_pkgs)
01706 output_acc += (*it)->path + " " + newp->path + "\n";
01707
01708 duplicate_packages_found = true;
01709 break;
01710 }
01711 }
01712 if(dup)
01713 delete newp;
01714 else
01715 Package::pkgs.push_back(newp);
01716
01717 return return_p;
01718 }
01719
01720 void ROSPack::crawl_for_packages(bool force_crawl)
01721 {
01722 for (VecPkg::iterator p = Package::pkgs.begin();
01723 p != Package::pkgs.end(); ++p)
01724 Package::deleted_pkgs.push_back(*p);
01725 Package::pkgs.clear();
01726
01727 if(!force_crawl && cache_is_good())
01728 {
01729 string cache_path = getCachePath();
01730 FILE *cache = fopen(cache_path.c_str(), "r");
01731 if (cache)
01732 {
01733 #ifdef VERBOSE_DEBUG
01734 fprintf(stderr, "trying to use cache...\n");
01735 #endif
01736 char linebuf[30000];
01737 for(;;)
01738 {
01739 if (!fgets(linebuf, sizeof(linebuf), cache))
01740 break;
01741 if (linebuf[0] == '#')
01742 continue;
01743 char *newline_pos = strchr(linebuf, '\n');
01744 if (newline_pos)
01745 *newline_pos = 0;
01746
01747 add_package(linebuf);
01748 }
01749 fclose(cache);
01750 return;
01751 }
01752 }
01753
01754
01755 #ifdef VERBOSE_DEBUG
01756 fprintf(stderr, "building cache\n");
01757 #endif
01758 deque<CrawlQueueEntry> q;
01759 q.push_back(CrawlQueueEntry(ros_root));
01760 if (char *rpp = getenv("ROS_PACKAGE_PATH"))
01761 {
01762 vector<string> rppvec;
01763 string_split(rpp, rppvec, path_delim);
01764 sanitize_rppvec(rppvec);
01765 for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i)
01766 {
01767 if(!i->size())
01768 continue;
01769 else if (!Package::is_package(*i) && Package::is_no_subdirs(*i))
01770 fprintf(stderr, "[rospack] WARNING: non-package directory in "
01771 "ROS_PACKAGE_PATH marked rospack_nosubdirs:\n\t%s\n",
01772 i->c_str());
01773 else
01774 q.push_back(CrawlQueueEntry(*i));
01775 }
01776 }
01777 const double crawl_start_time = time_since_epoch();
01778 priority_queue<CrawlQueueEntry, vector<CrawlQueueEntry>,
01779 greater<CrawlQueueEntry> > profile;
01780 while (!q.empty())
01781 {
01782 CrawlQueueEntry cqe = q.front();
01783 q.pop_front();
01784
01785
01786
01787 path_components.reserve(MAX_DIRECTORY_DEPTH+1);
01788
01789 string_split(cqe.path, path_components, "/");
01790 if(path_components.size() > MAX_DIRECTORY_DEPTH)
01791 {
01792 fprintf(stderr,"[rospack] Exceeded maximum directory depth of %d at %s. There must be a self-referencing symlink somewhere.\n", MAX_DIRECTORY_DEPTH, cqe.path.c_str());
01793 throw runtime_error(string("circular directory structure"));
01794 }
01795
01796
01797 if (Package::is_package(cqe.path))
01798 {
01799
01800 add_package(cqe.path);
01801 continue;
01802 }
01803
01804
01805 if (opt_profile_length != 0)
01806 {
01807 if (cqe.start_time != 0)
01808 {
01809
01810
01811
01812
01813 cqe.elapsed_time = time_since_epoch() - cqe.start_time;
01814
01815
01816
01817
01818
01819 if(cqe.start_num_pkgs < total_num_pkgs)
01820 cqe.has_manifest = true;
01821 if(!opt_profile_zombie_only || !cqe.has_manifest)
01822 {
01823 profile.push(cqe);
01824 if ((opt_profile_length > 0) && (profile.size() > opt_profile_length))
01825 profile.pop();
01826 }
01827 continue;
01828 }
01829 cqe.start_time = time_since_epoch();
01830 cqe.start_num_pkgs = total_num_pkgs;
01831 q.push_front(cqe);
01832 }
01833 #if defined(WIN32)
01834 WIN32_FIND_DATA find_file_data;
01835 HANDLE hfind = INVALID_HANDLE_VALUE;
01836 if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
01837 &find_file_data)) == INVALID_HANDLE_VALUE)
01838 {
01839 fprintf(stderr, "[rospack] FindFirstFile error %u while crawling %s\n",
01840 GetLastError(), cqe.path.c_str());
01841 continue;
01842 }
01843
01844 do
01845 {
01846 if (!S_ISDIR(find_file_data.dwFileAttributes))
01847 continue;
01848 if (find_file_data.cFileName[0] == '.')
01849 continue;
01850 string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
01851 if (Package::is_package(child_path))
01852 {
01853 total_num_pkgs++;
01854
01855 Package* newp = new Package(child_path);
01856
01857 bool dup = false;
01858 for(std::vector<Package *>::const_iterator it = Package::pkgs.begin();
01859 it != Package::pkgs.end();
01860 it++)
01861 {
01862 if((*it)->name == newp->name)
01863 {
01864 dup=true;
01865 break;
01866 }
01867 }
01868 if(dup)
01869 delete newp;
01870 else
01871 {
01872 Package::pkgs.push_back(newp);
01873 }
01874 }
01875
01876 else if (!Package::is_no_subdirs(child_path))
01877 q.push_front(CrawlQueueEntry(child_path));
01878 }
01879 while (FindNextFile(hfind, &find_file_data) != 0);
01880 DWORD last_error = GetLastError();
01881 FindClose(hfind);
01882 if (last_error != ERROR_NO_MORE_FILES)
01883 {
01884 fprintf(stderr, "[rospack] FindNextFile error %u while crawling %s\n",
01885 GetLastError(), cqe.path.c_str());
01886 continue;
01887 }
01888 #else
01889 DIR *d = opendir(cqe.path.c_str());
01890 if (!d)
01891 {
01892 fprintf(stderr, "[rospack] opendir error [%s] while crawling %s\n",
01893 strerror(errno), cqe.path.c_str());
01894 continue;
01895 }
01896 struct dirent *ent;
01897 while ((ent = readdir(d)) != NULL)
01898 {
01899 struct stat s;
01900 string child_path = cqe.path + fs_delim + string(ent->d_name);
01901 int ret;
01902 while ((ret = stat(child_path.c_str(), &s)) != 0 &&
01903 errno == EINTR);
01904 if (ret != 0)
01905 continue;
01906 if (!S_ISDIR(s.st_mode))
01907 continue;
01908 if (ent->d_name[0] == '.')
01909 continue;
01910 if (Package::is_package(child_path))
01911 {
01912 total_num_pkgs++;
01913 add_package(child_path);
01914 }
01915
01916 else if (!Package::is_no_subdirs(child_path))
01917 q.push_front(CrawlQueueEntry(child_path));
01918 }
01919 closedir(d);
01920 #endif
01921 }
01922 crawled = true;
01923 const double crawl_elapsed_time = time_since_epoch() - crawl_start_time;
01924
01925
01926 string cache_path(getCachePath());
01927 if(!cache_path.size())
01928 {
01929 fprintf(stderr, "[rospack] No location available to write cache file. Try setting ROS_HOME or HOME.\n");
01930 }
01931 else
01932 {
01933 char tmp_cache_dir[PATH_MAX];
01934 char tmp_cache_path[PATH_MAX];
01935 strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
01936 #if defined(_MSC_VER)
01937
01938 char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
01939 _splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
01940 ext, _MAX_EXT);
01941 char full_dir[_MAX_DRIVE + _MAX_DIR];
01942 _makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
01943 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rospack_cache.XXXXXX", full_dir);
01944 #else
01945 snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rospack_cache.XXXXXX", dirname(tmp_cache_dir));
01946 #endif
01947 #if defined(__MINGW32__)
01948
01949
01950
01951 FILE *cache = tmpfile();
01952 if ( cache == NULL ) {
01953 fprintf(stderr,
01954 "[rospack] Unable to generate temporary cache file name: %u",
01955 errno);
01956 }
01957 else
01958 {
01959 #elif defined(WIN32)
01960
01961
01962
01963 if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
01964 {
01965 fprintf(stderr,
01966 "[rospack] Unable to generate temporary cache file name: %u",
01967 GetLastError());
01968 }
01969 else
01970 {
01971 FILE *cache = fopen(tmp_cache_path, "w");
01972 #else
01973 int fd = mkstemp(tmp_cache_path);
01974 if (fd < 0)
01975 {
01976 fprintf(stderr, "[rospack] Unable to create temporary cache file %s: %s\n",
01977 tmp_cache_path, strerror(errno));
01978 }
01979 else
01980 {
01981 FILE *cache = fdopen(fd, "w");
01982 #endif
01983 if (!cache)
01984 {
01985 fprintf(stderr, "[rospack] Unable open cache file %s: %s\n",
01986 tmp_cache_path, strerror(errno));
01987 }
01988 else
01989 {
01990 char *rpp = getenv("ROS_PACKAGE_PATH");
01991 fprintf(cache, "#ROS_ROOT=%s\n#ROS_PACKAGE_PATH=%s\n", ros_root,
01992 (rpp ? rpp : ""));
01993 for (VecPkg::iterator pkg = Package::pkgs.begin();
01994 pkg != Package::pkgs.end(); ++pkg)
01995 fprintf(cache, "%s\n", (*pkg)->path.c_str());
01996 fclose(cache);
01997 if(file_exists(cache_path.c_str()))
01998 remove(cache_path.c_str());
01999 if(rename(tmp_cache_path, cache_path.c_str()) < 0)
02000 {
02001 fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n",
02002 tmp_cache_path, cache_path.c_str(), strerror(errno));
02003 }
02004 }
02005 }
02006 }
02007
02008 if (opt_profile_length)
02009 {
02010
02011 stack<CrawlQueueEntry> reverse_profile;
02012
02013
02014 vector<string> zombie_dirs;
02015 zombie_dirs.reserve(profile.size());
02016 while (!profile.empty())
02017 {
02018 reverse_profile.push(profile.top());
02019 zombie_dirs.push_back(profile.top().path);
02020 profile.pop();
02021 }
02022 if(!opt_profile_zombie_only)
02023 {
02024
02025
02026
02027
02028 char buf[16];
02029 snprintf(buf, sizeof(buf), "%.6f", crawl_elapsed_time);
02030 output_acc += "\nFull tree crawl took " + string(buf) + " seconds.\n";
02031 output_acc += "Directories marked with (*) contain no manifest. You may\n";
02032 output_acc += "want to delete these directories.\n";
02033 output_acc += "To get just of list of directories without manifests,\n";
02034 output_acc += "re-run the profile with --zombie-only\n.";
02035 output_acc += "-------------------------------------------------------------\n";
02036 }
02037 while (!reverse_profile.empty())
02038 {
02039 CrawlQueueEntry cqe = reverse_profile.top();
02040 reverse_profile.pop();
02041
02042 if(!opt_profile_zombie_only)
02043 {
02044
02045
02046
02047
02048 char buf[16];
02049 snprintf(buf, sizeof(buf), "%.6f", cqe.elapsed_time);
02050 output_acc += string(buf) + " ";
02051 if(cqe.has_manifest)
02052 output_acc += " ";
02053 else
02054 output_acc += "* ";
02055 output_acc += cqe.path;
02056 output_acc += "\n";
02057 }
02058 else
02059 {
02060 bool dup = false;
02061
02062
02063
02064 if(file_exists(cqe.path + fs_delim + "stack.xml") ||
02065 file_exists(cqe.path + fs_delim + "app.xml"))
02066 {
02067 continue;
02068 }
02069
02070
02071 for(vector<string>::const_iterator it = zombie_dirs.begin();
02072 it != zombie_dirs.end();
02073 ++it)
02074 {
02075 if((cqe.path.size() > it->size()) &&
02076 (cqe.path.substr(0,it->size()) == (*it)))
02077 {
02078 dup = true;
02079 break;
02080 }
02081 }
02082 if(dup)
02083 continue;
02084
02085
02086 output_acc += cqe.path + "\n";
02087 }
02088 }
02089 if(!opt_profile_zombie_only)
02090 {
02091
02092 output_acc += "\n";
02093 }
02094 }
02095 }
02096
02097 VecPkg ROSPack::partial_crawl(const string &path)
02098 {
02099 deque<CrawlQueueEntry> q;
02100 q.push_back(CrawlQueueEntry(path));
02101 VecPkg partial_pkgs;
02102 while (!q.empty())
02103 {
02104 CrawlQueueEntry cqe = q.front();
02105
02106 q.pop_front();
02107 #if defined(WIN32)
02108 WIN32_FIND_DATA find_file_data;
02109 HANDLE hfind = INVALID_HANDLE_VALUE;
02110 if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
02111 &find_file_data)) == INVALID_HANDLE_VALUE)
02112 {
02113 fprintf(stderr, "[rospack] FindFirstFile error %u while crawling %s\n",
02114 GetLastError(), cqe.path.c_str());
02115 continue;
02116 }
02117
02118 do
02119 {
02120 if (!S_ISDIR(find_file_data.dwFileAttributes))
02121 continue;
02122 if (find_file_data.cFileName[0] == '.')
02123 continue;
02124 string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
02125 if (Package::is_package(child_path))
02126 {
02127
02128 Package* newp = new Package(child_path);
02129
02130 bool dup = false;
02131 for(std::vector<Package *>::const_iterator it = partial_pkgs.begin();
02132 it != partial_pkgs.end();
02133 it++)
02134 {
02135 if((*it)->name == newp->name)
02136 {
02137 dup=true;
02138 break;
02139 }
02140 }
02141 if(dup)
02142 delete newp;
02143 else
02144 partial_pkgs.push_back(newp);
02145 }
02146
02147 else if (!Package::is_no_subdirs(child_path))
02148 q.push_front(CrawlQueueEntry(child_path));
02149 }
02150 while (FindNextFile(hfind, &find_file_data) != 0);
02151 DWORD last_error = GetLastError();
02152 FindClose(hfind);
02153 if (last_error != ERROR_NO_MORE_FILES)
02154 {
02155 fprintf(stderr, "[rospack] FindNextFile error %u while crawling %s\n",
02156 GetLastError(), cqe.path.c_str());
02157 continue;
02158 }
02159 #else
02160 DIR *d = opendir(cqe.path.c_str());
02161 if (!d)
02162 {
02163 fprintf(stderr, "[rospack] opendir error [%s] while crawling %s\n",
02164 strerror(errno), cqe.path.c_str());
02165 continue;
02166 }
02167 struct dirent *ent;
02168 while ((ent = readdir(d)) != NULL)
02169 {
02170 struct stat s;
02171 string child_path = cqe.path + fs_delim + string(ent->d_name);
02172 int ret;
02173 while ((ret = stat(child_path.c_str(), &s)) != 0 &&
02174 errno == EINTR);
02175 if (ret != 0)
02176 continue;
02177 if (!S_ISDIR(s.st_mode))
02178 continue;
02179 if (ent->d_name[0] == '.')
02180 continue;
02181 if (Package::is_package(child_path))
02182 {
02183
02184 Package* newp = new Package(child_path);
02185
02186 bool dup = false;
02187 for(std::vector<Package *>::const_iterator it = partial_pkgs.begin();
02188 it != partial_pkgs.end();
02189 it++)
02190 {
02191 if((*it)->name == newp->name)
02192 {
02193 dup=true;
02194 break;
02195 }
02196 }
02197 if(dup)
02198 delete newp;
02199 else
02200 partial_pkgs.push_back(newp);
02201 }
02202
02203 else if (!Package::is_no_subdirs(child_path))
02204 q.push_front(CrawlQueueEntry(child_path));
02205 }
02206 closedir(d);
02207 #endif
02208 }
02209 return partial_pkgs;
02210 }
02211
02213
02214 void string_split(const string &s, vector<string> &t, const string &d)
02215 {
02216 t.clear();
02217 size_t start = 0, end;
02218 while ((end = s.find_first_of(d, start)) != string::npos)
02219 {
02220 if((end-start) > 0)
02221 t.push_back(s.substr(start, end-start));
02222 start = end + 1;
02223 }
02224 if(start < s.size())
02225 t.push_back(s.substr(start));
02226 }
02227
02228
02229
02230
02231 string ROSPack::deduplicate_tokens(const string& s)
02232 {
02233 vector<string> in;
02234 vector<string> out;
02235 string_split(s, in, " ");
02236 for(int i=0; i<in.size(); i++)
02237 {
02238 bool dup = false;
02239 for(int j=0; j<out.size(); j++)
02240 {
02241 if(!out[j].compare(in[i]))
02242 {
02243 dup = true;
02244 break;
02245 }
02246 }
02247 if(!dup)
02248 out.push_back(in[i]);
02249 }
02250
02251 string res;
02252 for(int j=0; j<out.size(); j++)
02253 {
02254 if(!j)
02255 res += out[j];
02256 else
02257 res += string(" ") + out[j];
02258 }
02259
02260 return res;
02261 }
02262
02263 bool file_exists(const string &fname)
02264 {
02265
02266
02267 return (access(fname.c_str(), F_OK) == 0);
02268 }
02269
02270 Package *g_get_pkg(const string &name)
02271 {
02272
02273 return g_rospack->get_pkg(name);
02274 }
02275
02276 void ROSPack::sanitize_rppvec(std::vector<std::string> &rppvec)
02277 {
02278
02279 for (size_t i = 0; i < rppvec.size(); i++)
02280 {
02281 size_t last_slash_pos;
02282 while((last_slash_pos = rppvec[i].find_last_of("/")) == rppvec[i].length()-1)
02283 {
02284 fprintf(stderr, "[rospack] warning: trailing slash found in "
02285 "ROS_PACKAGE_PATH\n");
02286 rppvec[i].erase(last_slash_pos);
02287 }
02288 }
02289 }
02290
02291 }