kinetic-devel/tools/rospack/rospack.cpp

1987 lines
56 KiB
C++

/*
* Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Author: Morgan Quigley, Brian Gerkey */
#include <cstdlib>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <cassert>
#include <unistd.h>
#include <dirent.h>
#include <stdexcept>
#include <sys/time.h>
#include <sys/file.h>
#include <time.h>
#include <sstream>
#include <iterator>
#include <libgen.h>
#include "tinyxml-2.5.3/tinyxml.h"
#include "rospack/rospack.h"
using namespace std;
//#define VERBOSE_DEBUG
const double DEFAULT_MAX_CACHE_AGE = 60.0; // rebuild cache every minute
#include <sys/stat.h>
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
namespace rospack
{
ROSPack *g_rospack = NULL; // singleton
#ifdef __APPLE__
const string g_ros_os("osx");
#else
const string g_ros_os("linux");
#endif
const char *fs_delim = "/"; // ifdef this for windows
Package::Package(string _path) : path(_path),
deps_calculated(false), direct_deps_calculated(false),
descendants_calculated(false), manifest_loaded(false)
{
vector<string> name_tokens;
string_split(path, name_tokens, fs_delim);
name = name_tokens.back();
}
bool Package::is_package(string path)
{
return file_exists(path + string(fs_delim) + "manifest.xml");
}
bool Package::is_no_subdirs(string path)
{
return file_exists(path + string(fs_delim) + "rospack_nosubdirs");
}
const VecPkg &Package::deps1()
{
return direct_deps();
}
const VecPkg &Package::deps(traversal_order_t order, int depth)
{
if (depth > 1000)
{
fprintf(stderr,"[rospack] woah! expanding the dependency tree made it blow "
"up.\n There must be a circular dependency somewhere.\n");
throw runtime_error(string("circular dependency"));
}
if (deps_calculated)
return _deps;
// postorder traversal of the dependency tree
VecPkg my_dd = direct_deps();
for (VecPkg::iterator i = my_dd.begin(); i != my_dd.end(); ++i)
{
VecPkg d = (*i)->deps(order, depth+1); // recurse on direct dependencies
if (order == PREORDER)
_deps.push_back(*i);
for (VecPkg::iterator j = d.begin(); j != d.end(); ++j)
{
// don't add things twice, but if you have something already
// and we're doing a quasi-preorder traversal, bump it to the back
bool have = false;
VecPkg::iterator prior_loc;
for (VecPkg::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
if ((*k) == (*j))
{
prior_loc = k;
have = true;
}
if (have && order == PREORDER)
{
_deps.erase(prior_loc);
_deps.push_back(*j);
}
else if (!have)
_deps.push_back(*j);
}
if (order == POSTORDER)
{
// only stuff it to the end if it isn't there already
bool have = false;
for (VecPkg::iterator k = _deps.begin(); k != _deps.end() && !have; ++k)
if ((*k) == (*i))
have = true;
if (!have)
_deps.push_back(*i);
}
}
deps_calculated = true;
return _deps;
}
string Package::manifest_path()
{
return path + string(fs_delim) + "manifest.xml";
}
string Package::flags(string lang, string attrib)
{
VecPkg d = deps(PREORDER);
string s;
// Conditionally include this package's exported flags, depending on
// whether --deps-only was given
if(!g_rospack->opt_deps_only)
s += this->direct_flags(lang, attrib) + string(" ");
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
string f = (*i)->direct_flags(lang, attrib);
if (f.length())
s += f + string(" ");
}
return s;
}
string Package::rosdep()
{
string sd;
TiXmlElement *mroot = manifest_root();
for(TiXmlElement *sd_ele = mroot->FirstChildElement("rosdep");
sd_ele;
sd_ele = sd_ele->NextSiblingElement("rosdep"))
{
const char *att_str;
if((att_str = sd_ele->Attribute("name")))
sd += string("name: ") + string(att_str);
sd += string("\n");
}
return sd;
}
string Package::versioncontrol()
{
string sd;
TiXmlElement *mroot = manifest_root();
for(TiXmlElement *sd_ele = mroot->FirstChildElement("versioncontrol");
sd_ele;
sd_ele = sd_ele->NextSiblingElement("versioncontrol"))
{
const char *att_str;
if((att_str = sd_ele->Attribute("type")))
sd += string("type: ") + string(att_str);
if((att_str = sd_ele->Attribute("url")))
sd += string("\turl: ") + string(att_str);
sd += string("\n");
}
return sd;
}
vector<pair<string, string> > Package::plugins()
{
vector<pair<string, string> > plugins;
VecPkg deplist;
// If --top=foo was given, then restrict the search to packages that are
// dependencies of foo, plus foo itself
if(g_rospack->opt_top.size())
{
Package* gtp = g_get_pkg(g_rospack->opt_top);
deplist = gtp->deps(Package::POSTORDER);
deplist.push_back(gtp);
}
VecPkg desc1 = descendants1();
desc1.push_back(this);
VecPkg::iterator it = desc1.begin();
VecPkg::iterator end = desc1.end();
for (; it != end; ++it)
{
// If we're restricting the search, make sure this package is in the
// deplist. This could be made more efficient.
if(deplist.size())
{
bool found = false;
for(VecPkg::const_iterator dit = deplist.begin();
dit != deplist.end();
dit++)
{
if((*dit)->name == (*it)->name)
{
found = true;
break;
}
}
if(!found)
continue;
}
std::string flags = (*it)->direct_flags(name, g_rospack->opt_attrib);
if (!flags.empty())
{
plugins.push_back(make_pair((*it)->name, flags));
}
}
return plugins;
}
VecPkg Package::descendants1()
{
VecPkg children;
// Make a copy of the pkgs vector, because a crawl can be caused in
// has_parent()->direct_deps()->g_get_pkg()->get_pkg(). That crawl
// will rebuild pkgs, invalidating our iterator, causing esoteric crashes
// e.g., #2056.
VecPkg pkgs_copy(pkgs);
for (VecPkg::iterator p = pkgs_copy.begin(); p != pkgs_copy.end(); ++p)
{
// We catch exceptions here, because we don't care if some
// unrelated packages in the system have invalid manifests
try
{
if ((*p)->has_parent(name))
children.push_back(*p);
}
catch (runtime_error &e)
{
}
}
return children;
}
const vector<Package *> &Package::descendants(int depth)
{
if (depth > 100)
{
fprintf(stderr, "[rospack] woah! circular dependency in the ros tree! aaaaaa!\n");
throw runtime_error(string("circular dependency"));
}
if (descendants_calculated)
return _descendants;
VecPkg desc_with_dups;
for (VecPkg::iterator p = pkgs.begin(); p != pkgs.end(); ++p)
{
// We catch exceptions here, because we don't care if some
// unrelated packages in the system have invalid manifests
try
{
if ((*p)->has_parent(name))
{
desc_with_dups.push_back(*p);
const VecPkg &p_desc = (*p)->descendants(depth+1);
for (VecPkg::const_iterator q = p_desc.begin();
q != p_desc.end(); ++q)
desc_with_dups.push_back(*q);
}
}
catch (runtime_error &e)
{
}
}
assert(_descendants.size() == 0);
for (VecPkg::iterator p = desc_with_dups.begin();
p != desc_with_dups.end(); ++p)
{
bool found = false;
for (VecPkg::iterator q = _descendants.begin();
q != _descendants.end() && !found; ++q)
if ((*q)->name == (*p)->name)
found = true;
if (!found)
_descendants.push_back(*p);
}
descendants_calculated = true;
return _descendants;
}
bool Package::has_parent(string pkg)
{
vector<Package *> parents = direct_deps(true);
for (VecPkg::iterator i = parents.begin(); i != parents.end(); ++i)
if ((*i)->name == pkg)
return true;
return false;
}
const vector<Package *> &Package::direct_deps(bool missing_package_as_warning)
{
if (direct_deps_calculated)
return _direct_deps;
#ifdef VERBOSE_DEBUG
printf("calculating direct deps for package [%s]\n", name.c_str());
#endif
TiXmlElement *mroot = manifest_root();
TiXmlNode *dep_node = 0;
while ((dep_node = mroot->IterateChildren(string("depend"), dep_node)))
{
TiXmlElement *dep_ele = dep_node->ToElement();
assert(dep_ele);
const char *dep_pkgname = dep_ele->Attribute("package");
if (!dep_pkgname)
{
fprintf(stderr,"[rospack] bad depend syntax (no 'package' attribute) in [%s]\n",
manifest_path().c_str());
throw runtime_error(string("invalid manifest"));
}
// Must make a copy here, because the call to g_get_pkg() below might
// cause a recrawl, which blows aways the accumulated data structure.
char* dep_pkgname_copy = strdup(dep_pkgname);
#ifdef VERBOSE_DEBUG
printf("direct_deps: pkg %s has dep %s\n", name.c_str(), dep_pkgname_copy);
#endif
try
{
_direct_deps.push_back(g_get_pkg(dep_pkgname_copy));
}
catch (runtime_error &e)
{
if (missing_package_as_warning)
{
// Don't warn if we're in certain modes, #2197
if(g_rospack->opt_warn_on_missing_deps)
{
fprintf(stderr, "[rospack] warning: couldn't find dependency [%s] of [%s]\n",
dep_pkgname_copy, name.c_str());
}
}
else
{
fprintf(stderr, "[rospack] couldn't find dependency [%s] of [%s]\n",
dep_pkgname_copy, name.c_str());
free(dep_pkgname_copy);
throw runtime_error(string("missing dependency"));
}
}
free(dep_pkgname_copy);
}
direct_deps_calculated = true;
return _direct_deps;
}
string Package::cpp_message_flags(bool cflags, bool lflags)
{
bool msg_exists = file_exists((path + "/msg_gen/generated").c_str());
bool srv_exists = file_exists((path + "/srv_gen/generated").c_str());
string flags;
if (cflags)
{
if (msg_exists)
{
flags += string(" -I") + path + "/msg_gen/cpp/include";
}
if (srv_exists)
{
flags += string(" -I") + path + "/srv_gen/cpp/include";
}
}
// lflags not needed until we have a cpp file, but this implementation works, adding -l<package_name>msgs and -l<package_name>srvs
// we'll probably need to figure out a better way of testing for msg/srvs than just checking if <package_path>/msg|srv exists though
#if 0
if (lflags)
{
if (msg_exists)
{
flags += string(" -L") + path + "/lib";
flags += string(" -Wl,-rpath,") + path + "/lib";
flags += " -l" + package->name + "msgs";
}
if (srv_exists)
{
// if msgs already exist, we'll already have added this to the flags
if (!msg_exists)
{
flags += string(" -L") + path + "/lib";
flags += string(" -Wl,-rpath,") + path + "/lib";
}
flags += " -l" + package->name + "srvs";
}
}
#endif
flags += " ";
return flags;
}
string Package::direct_flags(string lang, string attrib)
{
TiXmlElement *mroot = manifest_root();
TiXmlElement *export_ele = mroot->FirstChildElement("export");
string str;
if (export_ele)
{
bool os_match = false;
TiXmlElement *best_usage = NULL;
for (TiXmlElement *lang_ele = export_ele->FirstChildElement(lang);
lang_ele; lang_ele = lang_ele->NextSiblingElement(lang))
{
const char *os_str;
if ((os_str = lang_ele->Attribute("os")))
{
if(g_ros_os == string(os_str))
{
if(os_match)
{
fprintf(stderr, "[rospack] warning: ignoring duplicate \"%s\" tag with os=\"%s\" in export block\n",
lang.c_str(), os_str);
}
else
{
best_usage = lang_ele;
os_match = true;
}
}
}
if(!os_match)
{
if (!best_usage)
best_usage = lang_ele;
else if(!os_str)
{
fprintf(stderr, "[rospack] warning: ignoring duplicate \"%s\" tag in export block\n",
lang.c_str());
}
}
}
if (!best_usage)
return string();
const char *cstr = best_usage->Attribute(attrib.c_str());
if (!cstr)
return string();
str = cstr;
while (1) // every decent C program has a while(1) in it
{
int i = str.find(string("${prefix}"));
if (i < 0)
break; // no more occurrences
str.replace(i, string("${prefix}").length(), path);
}
// Do backquote substitution. E.g., if we find this string:
// `pkg-config --cflags gdk-pixbuf-2.0`
// We replace it with the result of executing the command
// contained within the backquotes (reading from its stdout), which
// might be something like:
// -I/usr/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
// Construct and execute the string
// We do the assignment first to ensure that if backquote expansion (or
// anything else) fails, we'll get a non-zero exit status from pclose().
string cmd = string("ret=\"") + str + string("\" && echo $ret");
// Remove embedded newlines
string token("\n");
for (string::size_type s = cmd.find(token); s != string::npos;
s = cmd.find(token, s))
{
cmd.replace(s,token.length(),string(" "));
}
FILE* p;
if(!(p = popen(cmd.c_str(), "r")))
{
fprintf(stderr, "[rospack] warning: failed to execute backquote "
"expression \"%s\" in [%s]\n",
cmd.c_str(), manifest_path().c_str());
string errmsg = string("error in backquote expansion for ") + g_rospack->opt_package;
throw runtime_error(errmsg);
}
else
{
char buf[8192];
memset(buf,0,sizeof(buf));
// Read the command's output
do
{
clearerr(p);
while(fgets(buf + strlen(buf),sizeof(buf)-strlen(buf)-1,p));
} while(ferror(p) && errno == EINTR);
// Close the subprocess, checking exit status
if(pclose(p) != 0)
{
fprintf(stderr, "[rospack] warning: got non-zero exit status from executing backquote expression \"%s\" in [%s]\n",
cmd.c_str(), manifest_path().c_str());
string errmsg = string("error in backquote expansion for ") + g_rospack->opt_package;
throw runtime_error(errmsg);
}
else
{
// Strip trailing newline, which was added by our call to echo
buf[strlen(buf)-1] = '\0';
// Replace the backquote expression with the new text
str = string(buf);
}
}
}
if (lang == "cpp")
{
if (attrib == "cflags")
{
// Message flags go last so it's possible to override them
str += cpp_message_flags(true, false);
}
else if (attrib == "lflags")
{
// Message flags go last so it's possible to override them
str += cpp_message_flags(false, true);
}
}
return str;
}
void Package::load_manifest()
{
if (manifest_loaded)
return;
if (!manifest.LoadFile(manifest_path()))
{
string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
fprintf(stderr, "[rospack] warning: error parsing manifest file at [%s]\n",
manifest_path().c_str());
// Only want this warning printed once.
manifest_loaded = true;
throw runtime_error(errmsg);
}
}
TiXmlElement *Package::manifest_root()
{
load_manifest();
TiXmlElement *ele = manifest.RootElement();
if (!ele)
{
string errmsg = string("error parsing manifest file at [") + manifest_path().c_str() + string("]");
throw runtime_error(errmsg);
}
return ele;
}
// Naive recursion. Dynamic programming would be more efficient.
void Package::accumulate_deps(AccList& acc_list, Package* to)
{
VecPkg dd = direct_deps();
for(VecPkg::iterator it = dd.begin();
it != dd.end();
++it)
{
if((*it)->name == to->name)
{
Acc acc;
acc.push_back(this);
acc.push_back(to);
acc_list.push_back(acc);
}
else
{
AccList l;
(*it)->accumulate_deps(l, to);
for(AccList::iterator lit = l.begin();
lit != l.end();
++lit)
{
lit->push_front(this);
acc_list.push_back(*lit);
}
}
}
}
VecPkg Package::pkgs;
VecPkg Package::deleted_pkgs;
//////////////////////////////////////////////////////////////////////////////
ROSPack::ROSPack() : ros_root(NULL), cache_lock_failed(false), crawled(false),
my_argc(0), my_argv(NULL), opt_profile_length(0), total_num_pkgs(0)
{
g_rospack = this;
Package::pkgs.reserve(500); // get some space to avoid early recopying...
ros_root = getenv("ROS_ROOT");
if (!ros_root)
{
fprintf(stderr,"[rospack] ROS_ROOT is not defined in the environment.\n");
throw runtime_error(string("no ROS_ROOT"));
}
if (!file_exists(ros_root))
{
fprintf(stderr,"[rospack] the path specified as ROS_ROOT is not "
"accessible. Please ensure that this environment variable "
"is set and is writeable by your user account.\n");
throw runtime_error(string("no ROS_ROOT"));
}
crawl_for_packages();
}
ROSPack::~ROSPack()
{
for (VecPkg::iterator p = Package::pkgs.begin();
p != Package::pkgs.end(); ++p)
delete (*p);
Package::pkgs.clear();
for (VecPkg::iterator p = Package::deleted_pkgs.begin();
p != Package::deleted_pkgs.end(); ++p)
delete (*p);
Package::deleted_pkgs.clear();
freeArgv();
}
const char* ROSPack::usage()
{
return "USAGE: rospack <command> [options] [package]\n"
" Allowed commands:\n"
" help\n"
" find [package]\n"
" list\n"
" list-names\n"
" langs\n"
" depends [package] (alias: deps)\n"
" depends-manifests [package] (alias: deps-manifests)\n"
" depends-msgsrv [package] (alias: deps-msgsrv)\n"
" depends1 [package] (alias: deps1)\n"
" depends-indent [package] (alias: deps-indent)\n"
" depends-why --target=<target> [package] (alias: deps-why)\n"
" rosdep [package] (alias: rosdeps)\n"
" rosdep0 [package] (alias: rosdeps0)\n"
" vcs [package]\n"
" vcs0 [package]\n"
" depends-on [package]\n"
" depends-on1 [package]\n"
" export [--deps-only] --lang=<lang> --attrib=<attrib> [package]\n"
" plugins --attrib=<attrib> [--top=<toppkg>] [package]\n"
" cflags-only-I [--deps-only] [package]\n"
" cflags-only-other [--deps-only] [package]\n"
" libs-only-L [--deps-only] [package]\n"
" libs-only-l [--deps-only] [package]\n"
" libs-only-other [--deps-only] [package]\n"
" profile [--length=<length>] [--zombie-only]\n\n"
" If [package] is omitted, the current working directory\n"
" is used (if it contains a manifest.xml).\n\n";
}
Package *ROSPack::get_pkg(string pkgname)
{
for (VecPkg::iterator p = Package::pkgs.begin();
p != Package::pkgs.end(); ++p)
{
if ((*p)->name == pkgname)
{
if(!crawled)
{
// Answer come from the cache; check that the path is valid, and
// contains a manifest (#1115).
std::string manifest_path = (*p)->path + fs_delim + "manifest.xml";
struct stat s;
int ret;
while((ret = stat(manifest_path.c_str(), &s)) != 0 &&
errno == EINTR);
if(ret == 0)
{
// Answer looks good
return (*p);
}
else
{
// Bad cache. Warn and fall through to the recrawl below.
fprintf(stderr, "[rospack] warning: invalid cached location %s for package %s; forcing recrawl\n",
(*p)->path.c_str(),
(*p)->name.c_str());
break;
}
}
else
{
// Answer came from a fresh crawl; no further checking needed.
return (*p);
}
}
}
if (!crawled) // maybe it's a brand-new package. force a crawl.
{
crawl_for_packages(true); // will set the crawled flag; recursion is safe
return get_pkg(pkgname);
}
string errmsg = string("couldn't find package [") + pkgname + string("]");
throw runtime_error(errmsg);
return NULL; // or not
}
int ROSPack::cmd_depends_on(bool include_indirect)
{
// We can't proceed if the argument-parsing logic wasn't able to provide
// any package name. Note that we need to check for an empty opt_package
// here, but not in other places (e.g., cmd_deps()), because here we're
// catching the exception that get_pkg() throws when it can't find the
// package. Elsewhere, we let that exception propagate up.
if(opt_package.size() == 0)
{
string errmsg = string("no package name given, and current directory is not a package root");
throw runtime_error(errmsg);
}
// Don't warn about missing deps
opt_warn_on_missing_deps = false;
// Explicitly crawl for packages, to ensure that we get newly added
// dependent packages. We also avoid the possibility of a recrawl
// happening within the loop below, which could invalidate the pkgs
// vector as we loop over it.
crawl_for_packages(true);
Package* p;
try
{
p = get_pkg(opt_package);
}
catch(runtime_error)
{
fprintf(stderr, "[rospack] warning: package %s doesn't exist\n",
opt_package.c_str());
p = new Package(opt_package);
Package::pkgs.push_back(p);
}
assert(p);
const VecPkg descendants = include_indirect ? p->descendants()
: p->descendants1();
for (VecPkg::const_iterator p = descendants.begin();
p != descendants.end(); ++p)
{
//printf("%s\n", (*p)->name.c_str());
output_acc += (*p)->name + "\n";
}
return 0;
}
// Naive recursion. Dynamic programming would be more efficient.
int ROSPack::cmd_depends_why()
{
AccList acc_list;
Package* from = get_pkg(opt_package);
Package* to = get_pkg(opt_target);
from->accumulate_deps(acc_list, to);
printf("Dependency chains from %s to %s:\n",
from->name.c_str(), to->name.c_str());
for(AccList::iterator lit = acc_list.begin();
lit != acc_list.end();
++lit)
{
printf("* ");
for(Acc::iterator ait = lit->begin();
ait != lit->end();
++ait)
{
if(ait != lit->begin())
printf("-> ");
printf("%s ", (*ait)->name.c_str());
}
printf("\n");
}
return 0;
}
int ROSPack::cmd_find()
{
// todo: obey the search order
Package *p = get_pkg(opt_package);
//printf("%s\n", p->path.c_str());
output_acc += p->path + "\n";
return 0;
}
int ROSPack::cmd_deps()
{
VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
//printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n";
}
return 0;
}
int ROSPack::cmd_deps_manifests()
{
VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
//printf("%s/manifest.xml ", (*i)->path.c_str());
output_acc += (*i)->path + "/manifest.xml ";
}
//puts("");
output_acc += "\n";
return 0;
}
int ROSPack::cmd_deps_msgsrv()
{
VecPkg d = get_pkg(opt_package)->deps(Package::POSTORDER);
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
Package* p = *i;
bool msg_exists = file_exists((p->path + "/msg_gen/generated").c_str());
bool srv_exists = file_exists((p->path + "/srv_gen/generated").c_str());
if (msg_exists)
{
output_acc += p->path + "/msg_gen/generated ";
}
if (srv_exists)
{
output_acc += p->path + "/srv_gen/generated ";
}
}
output_acc += "\n";
return 0;
}
int ROSPack::cmd_deps1()
{
VecPkg d = get_pkg(opt_package)->deps1();
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
//printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n";
}
return 0;
}
int ROSPack::cmd_depsindent(Package* pkg, int indent)
{
VecPkg d = pkg->deps1();
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{
for(int s=0; s<indent; s++)
{
//printf(" ");
output_acc += " ";
}
//printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n";
cmd_depsindent(*i, indent+2);
}
return 0;
}
/*
int ROSPack::cmd_predeps(char **args, int args_len)
{
if (args_len != 1)
fprintf(stderr,"[rospack] usage: rospack predeps PACKAGE\n");
else
{
VecPkg d = get_pkg(args[0])->deps(Package::PREORDER);
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
printf("%s\n", (*i)->name.c_str());
}
return 0;
}
*/
static bool space(char c) { return isspace(c); }
static bool not_space(char c) { return !isspace(c); }
static vector<string> split_space(const string& str)
{
typedef string::const_iterator iter;
vector<string> ret;
iter i = str.begin();
while (i != str.end())
{
i = find_if(i, str.end(), not_space);
iter j = find_if(i, str.end(), space);
if (i != str.end())
while (j != str.end() && *(j-1) == '\\')
j = find_if(j+1, str.end(), space);
ret.push_back(string(i, j));
i = j;
}
return ret;
}
string ROSPack::snarf_libs(string flags, bool invert)
{
vector<string> tokens = split_space(flags);
string snarfed;
for (size_t i = 0; i < tokens.size(); ++i)
{
//fprintf(stderr, "token = %s, len=%d, f=%c last=%s\n", tokens[i].c_str(), tokens[i].length(), tokens[i][0], tokens[i].substr(tokens[i].length()-2).c_str());
if (invert)
{
if ((tokens[i].substr(0, 2) != "-l") &&
(tokens[i].length() < 2 || tokens[i][0] != '/' || tokens[i].substr(tokens[i].length()-2) != ".a"))
snarfed += (snarfed.length() ? " " : "" ) + tokens[i];
}
else
{
if (tokens[i].substr(0, 2) == "-l")
snarfed += (snarfed.length() ? " " : "" ) + tokens[i].substr(2);
else if (tokens[i].length() > 2 && tokens[i][0] == '/' && tokens[i].substr(tokens[i].length()-2) == ".a")
snarfed += (snarfed.length() ? " " : "" ) + tokens[i];
}
}
return snarfed;
}
string ROSPack::snarf_flags(string flags, string prefix, bool invert)
{
vector<string> tokens = split_space(flags);
string snarfed;
for (size_t i = 0; i < tokens.size(); ++i)
{
if ((tokens[i].substr(0, prefix.length()) == prefix) ^ invert)
{
snarfed += (snarfed.length() ? " " : "" ) + tokens[i].substr(invert ? 0 : prefix.length());
}
}
return snarfed;
}
int ROSPack::cmd_libs_only(string token)
{
string lflags = get_pkg(opt_package)->flags("cpp", "lflags");;
if(!token.compare("-other"))
{
lflags = snarf_libs(lflags, true);
lflags = snarf_flags(lflags, "-L", true);
}
else if(!token.compare("-l"))
{
lflags = snarf_libs(lflags);
}
else
{
lflags = snarf_flags(lflags, token);
// tack on the bindeps path if it appears to be a good time to do so
// note this may be superfluous since rosboost-cfg is likely bringing
// the flags into the string already. perhaps at some point I should
// skim through the lflags and see if getBinDepPath() is already in
// there. It doesn't seem to hurt to duplicate it, though.
// (also: may need to set the rpath if rosboost-cfg hasn't already done it)
if (useBinDepPath())
lflags += string(" ") + getBinDepPath() + string("/lib");
lflags = deduplicate_tokens(lflags);
}
//printf("%s\n", lflags.c_str());
output_acc += lflags + "\n";
return 0;
}
int ROSPack::cmd_cflags_only(string token)
{
string cflags = get_pkg(opt_package)->flags("cpp", "cflags");
if(!token.compare("-other"))
cflags = snarf_flags(cflags, "-I", true);
else
{
cflags = snarf_flags(cflags, token);
// tack on the bindeps path if it appears to be a good time to do so
if (useBinDepPath())
cflags += string(" ") + getBinDepPath() + string("/include");
cflags = deduplicate_tokens(cflags);
}
//printf("%s\n", cflags.c_str());
output_acc += cflags + "\n";
return 0;
}
void ROSPack::export_flags(string pkg, string lang, string attrib)
{
string flags = get_pkg(pkg)->flags(lang, attrib);
// hack up to add the /opt/ros flags for C++ system dependencies
if (useBinDepPath() && lang == string("cpp"))
{
if (attrib == string("cflags"))
flags += string(" -I") + getBinDepPath() + string("/include");
else if (attrib == string("lflags"))
{
flags += string(" -L") + getBinDepPath() + string("/lib");
flags += string(" -Wl,-rpath,") + getBinDepPath() + string("/lib");
}
}
//printf("%s\n", flags.c_str());
output_acc += flags + "\n";
}
int ROSPack::cmd_versioncontrol(int depth)
{
string sds;
sds += get_pkg(opt_package)->versioncontrol();
if(depth < 0)
{
VecPkg descs = get_pkg(opt_package)->deps(Package::POSTORDER);
for(VecPkg::iterator dit = descs.begin();
dit != descs.end();
dit++)
{
sds += (*dit)->versioncontrol();
}
}
//printf("%s", sds.c_str());
output_acc += sds;
return 0;
}
int ROSPack::cmd_rosdep(int depth)
{
string sds;
sds += get_pkg(opt_package)->rosdep();
if(depth < 0)
{
VecPkg descs = get_pkg(opt_package)->deps(Package::POSTORDER);
for(VecPkg::iterator dit = descs.begin();
dit != descs.end();
dit++)
{
sds += (*dit)->rosdep();
}
}
//printf("%s", sds.c_str());
output_acc += sds;
return 0;
}
int ROSPack::cmd_export()
{
export_flags(opt_package, opt_lang, opt_attrib);
return 0;
}
int ROSPack::cmd_plugins()
{
// Don't warn about missing deps
opt_warn_on_missing_deps = false;
Package* p = get_pkg(opt_package);
vector<pair<string, string> > plugins = p->plugins();
vector<pair<string, string> >::iterator it = plugins.begin();
vector<pair<string, string> >::iterator end = plugins.end();
for (; it != end; ++it)
{
//printf("%s %s\n", it->first.c_str(), it->second.c_str());
output_acc += it->first + " " + it->second + "\n";
}
return 0;
}
void ROSPack::freeArgv()
{
if(my_argc)
{
for(int i=0;i<my_argc;i++)
free(my_argv[i]);
free(my_argv);
}
my_argc = 0;
my_argv = NULL;
}
int ROSPack::run(const std::string& cmd)
{
std::vector<std::string> cmd_list;
// TODO: split the input string properly, accounting for quotes, escaped
// quotes, etc. This shouldn't really matter, because rospack shouldn't be
// given arguments with embedded spaces.
string_split(cmd, cmd_list, " ");
// In case we're called more than once.
freeArgv();
my_argc = (int)cmd_list.size() + 1;
my_argv = (char**)malloc(sizeof(char*) * my_argc);
my_argv[0] = strdup("rospack");
for(int i=1;i<my_argc;i++)
my_argv[i] = strdup(cmd_list[i-1].c_str());
return run(my_argc, my_argv);
}
int ROSPack::run(int argc, char **argv)
{
assert(argc >= 2);
int i;
const char* opt_deps_name = "--deps-only";
const char* opt_zombie_name = "--zombie-only";
const char* opt_lang_name = "--lang=";
const char* opt_attrib_name = "--attrib=";
const char* opt_length_name = "--length=";
const char* opt_top_name = "--top=";
const char* opt_target_name = "--target=";
// Reset to defaults.
opt_deps_only = false;
// --lang=
opt_lang = string("");
// --attrib=
opt_attrib = string("");
// --length=
opt_length = string("");
// --top=
opt_top = string("");
// --target=
opt_target = string("");
// The package name
opt_package = string("");
// the number of entries to list in the profile table
opt_profile_length = 0;
// only display zombie directories in profile?
opt_profile_zombie_only = false;
// warn on missing deps
opt_warn_on_missing_deps = true;
output_acc = string("");
string errmsg = string(usage());
i=1;
const char* cmd = argv[i++];
for(;i<argc;i++)
{
if(!strcmp(argv[i], opt_deps_name))
opt_deps_only=true;
else if(!strcmp(argv[i], opt_zombie_name))
opt_profile_zombie_only=true;
else if(!strncmp(argv[i], opt_target_name, strlen(opt_target_name)))
{
if(opt_target.size())
throw runtime_error(errmsg);
else if(strlen(argv[i]) > strlen(opt_target_name))
opt_target = string(argv[i]+strlen(opt_target_name));
else
throw runtime_error(errmsg);
}
else if(!strncmp(argv[i], opt_lang_name, strlen(opt_lang_name)))
{
if(opt_lang.size())
throw runtime_error(errmsg);
else if(strlen(argv[i]) > strlen(opt_lang_name))
opt_lang = string(argv[i]+strlen(opt_lang_name));
else
throw runtime_error(errmsg);
}
else if(!strncmp(argv[i], opt_attrib_name, strlen(opt_attrib_name)))
{
if(opt_attrib.size())
throw runtime_error(errmsg);
else if(strlen(argv[i]) > strlen(opt_attrib_name))
opt_attrib = string(argv[i]+strlen(opt_attrib_name));
else
throw runtime_error(errmsg);
}
else if(!strncmp(argv[i], opt_length_name, strlen(opt_length_name)))
{
if(strlen(argv[i]) > strlen(opt_length_name))
opt_length = string(argv[i]+strlen(opt_length_name));
else
throw runtime_error(errmsg);
}
else if(!strncmp(argv[i], opt_top_name, strlen(opt_top_name)))
{
if(strlen(argv[i]) > strlen(opt_top_name))
opt_top = string(argv[i]+strlen(opt_top_name));
else
throw runtime_error(errmsg);
}
else
break;
}
if((strcmp(cmd, "depends-why") && strcmp(cmd, "deps-why")) && opt_target.size())
throw runtime_error(errmsg);
if((!strcmp(cmd, "depends-why") || !strcmp(cmd, "deps-why")) && !opt_target.size())
throw runtime_error(errmsg);
if(strcmp(cmd, "profile") && (opt_length.size() || opt_profile_zombie_only))
throw runtime_error(errmsg);
// --top= is only valid for plugins
if(strcmp(cmd, "plugins") && opt_top.size())
throw runtime_error(errmsg);
// --attrib= is only valid for export and plugins
if((strcmp(cmd, "export") && strcmp(cmd, "plugins")) &&
opt_attrib.size())
throw runtime_error(errmsg);
// --lang= is only valid for export
if((strcmp(cmd, "export") && opt_lang.size()))
throw runtime_error(errmsg);
// export requires both --lang and --attrib
if(!strcmp(cmd, "export") && (!opt_lang.size() || !opt_attrib.size()))
throw runtime_error(errmsg);
// plugins requires --attrib
if(!strcmp(cmd, "plugins") && !opt_attrib.size())
throw runtime_error(errmsg);
if(opt_deps_only &&
strcmp(cmd, "export") &&
strcmp(cmd, "cflags-only-I") &&
strcmp(cmd, "cflags-only-other") &&
strcmp(cmd, "libs-only-L") &&
strcmp(cmd, "libs-only-l") &&
strcmp(cmd, "libs-only-other"))
throw runtime_error(errmsg);
if(i < argc)
{
if(!strcmp(cmd, "help") ||
!strcmp(cmd, "list") ||
!strcmp(cmd, "list-names") ||
!strcmp(cmd, "langs") ||
!strcmp(cmd, "profile"))
throw runtime_error(errmsg);
opt_package = string(argv[i++]);
}
// Are we sitting in a package?
else
{
char buf[1024];
if(getcwd(buf,sizeof(buf)))
{
if(Package::is_package("."))
opt_package = string(basename(buf));
}
}
if (i != argc)
throw runtime_error(errmsg);
if (!strcmp(cmd, "profile"))
{
if (opt_length.size())
opt_profile_length = atoi(opt_length.c_str());
else
{
if(opt_profile_zombie_only)
opt_profile_length = -1; // default is infinite
else
opt_profile_length = 20; // default is about a screenful or so
}
#ifdef VERBOSE_DEBUG
printf("profile_length = %d\n", opt_profile_length);
#endif
// re-crawl with profiling enabled
crawl_for_packages(true);
return 0;
}
else if (!strcmp(cmd, "find"))
return cmd_find();
else if (!strcmp(cmd, "list"))
return cmd_print_package_list(true);
else if (!strcmp(cmd, "list-names"))
return cmd_print_package_list(false);
else if (!strcmp(cmd, "langs"))
return cmd_print_langs_list();
else if (!strcmp(cmd, "depends") || !strcmp(cmd, "deps"))
return cmd_deps();
else if (!strcmp(cmd, "depends-manifests") || !strcmp(cmd, "deps-manifests"))
return cmd_deps_manifests();
else if (!strcmp(cmd, "depends-msgsrv") || !strcmp(cmd, "deps-msgsrv"))
return cmd_deps_msgsrv();
else if (!strcmp(cmd, "depends1") || !strcmp(cmd, "deps1"))
return cmd_deps1();
else if (!strcmp(cmd, "depends-indent") || !strcmp(cmd, "deps-indent"))
return cmd_depsindent(get_pkg(opt_package), 0);
else if (!strcmp(cmd, "depends-on"))
return cmd_depends_on(true);
else if (!strcmp(cmd, "depends-why") || !strcmp(cmd, "deps-why"))
return cmd_depends_why();
else if (!strcmp(cmd, "depends-on1"))
return cmd_depends_on(false);
/*
else if (!strcmp(argv[i], "predeps"))
return cmd_predeps(argv+i+1, argc-i-1);
*/
else if (!strcmp(cmd, "export"))
return cmd_export();
else if (!strcmp(cmd, "plugins"))
return cmd_plugins();
else if (!strcmp(cmd, "rosdep") || !strcmp(cmd, "rosdeps"))
return cmd_rosdep(-1);
else if (!strcmp(cmd, "rosdep0") || !strcmp(cmd, "rosdeps0"))
return cmd_rosdep(0);
else if (!strcmp(cmd, "vcs"))
return cmd_versioncontrol(-1);
else if (!strcmp(cmd, "vcs0"))
return cmd_versioncontrol(0);
else if (!strcmp(cmd, "libs-only-l"))
return cmd_libs_only("-l");
else if (!strcmp(cmd, "libs-only-L"))
return cmd_libs_only("-L");
else if (!strcmp(cmd, "libs-only-other"))
return cmd_libs_only("-other");
else if (!strcmp(cmd, "cflags-only-I"))
return cmd_cflags_only("-I");
else if (!strcmp(cmd, "cflags-only-other"))
return cmd_cflags_only("-other");
else if (!strcmp(cmd, "help"))
fputs(usage(), stderr);
else
{
throw runtime_error(errmsg);
}
return 0;
}
int ROSPack::cmd_print_package_list(bool print_path)
{
for (VecPkg::iterator i = Package::pkgs.begin();
i != Package::pkgs.end(); ++i)
if (print_path)
{
//printf("%s %s\n", (*i)->name.c_str(), (*i)->path.c_str());
output_acc += (*i)->name + " " + (*i)->path + "\n";
}
else
{
//printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n";
}
return 0;
}
int ROSPack::cmd_print_langs_list()
{
// Don't warn about missing deps
opt_warn_on_missing_deps = false;
// Check for packages that depend directly on roslang
VecPkg lang_pkgs;
Package* roslang;
roslang = get_pkg("roslang");
assert(roslang);
lang_pkgs = roslang->descendants1();
// Filter out packages mentioned in ROS_LANG_DISABLE
char *disable = getenv("ROS_LANG_DISABLE");
vector<string> disable_list;
if(disable)
string_split(disable, disable_list, ":");
for(VecPkg::const_iterator i = lang_pkgs.begin();
i != lang_pkgs.end();
++i)
{
vector<string>::const_iterator j;
for(j = disable_list.begin();
j != disable_list.end();
++j)
{
if((*j) == (*i)->name)
break;
}
if(j == disable_list.end())
{
//printf("%s ", (*i)->name.c_str());
output_acc += (*i)->name + " ";
}
}
//printf("\n");
output_acc += "\n";
return 0;
}
string ROSPack::getCachePath()
{
string cache_file_name;
char* ros_home = getenv("ROS_HOME");
if (ros_home)
{
cache_file_name = ros_home + std::string("/rospack_cache");
}
else
{
// Not cross-platform?
ros_home = getenv("HOME");
if (ros_home)
{
// By providing the trailing slash, stat() will only succeed if the
// path exists AND is a directory.
std::string dotros = ros_home + std::string("/.ros/");
struct stat s;
if(stat(dotros.c_str(), &s))
{
if(mkdir(dotros.c_str(), 0700) != 0)
perror("[rospack] WARNING: cannot create ~/.ros directory");
}
cache_file_name = dotros + "rospack_cache";
}
}
return cache_file_name;
}
bool ROSPack::cache_is_good()
{
string cache_path = getCachePath();
// first see if it's new enough
double cache_max_age = DEFAULT_MAX_CACHE_AGE;
const char *user_cache_time_str = getenv("ROS_CACHE_TIMEOUT");
if(user_cache_time_str)
cache_max_age = atof(user_cache_time_str);
if(cache_max_age == 0.0)
return false;
struct stat s;
if (stat(cache_path.c_str(), &s) == 0)
{
double dt = difftime(time(NULL), s.st_mtime);
#ifdef VERBOSE_DEBUG
printf("cache age: %f\n", dt);
#endif
// Negative cache_max_age means it's always new enough. It's dangerous
// for the user to set this, but rosbash uses it.
if ((cache_max_age > 0.0) && (dt > cache_max_age))
return false;
}
// try to open it
FILE *cache = fopen(cache_path.c_str(), "r");
if (!cache)
return false; // it's not readable by us. sad.
// see if ROS_ROOT and ROS_PACKAGE_PATH are identical
char linebuf[30000];
bool ros_root_ok = false, ros_package_path_ok = false;
const char *ros_package_path = getenv("ROS_PACKAGE_PATH");
for(;;)
{
if (!fgets(linebuf, sizeof(linebuf), cache))
break;
linebuf[strlen(linebuf)-1] = 0; // get rid of trailing newline
if (linebuf[0] == '#')
{
if (!strncmp("#ROS_ROOT=", linebuf, 10))
{
if (!strcmp(linebuf+10, ros_root))
ros_root_ok = true;
}
else if (!strncmp("#ROS_PACKAGE_PATH=", linebuf, 18))
{
if (!ros_package_path)
{
if (!strlen(linebuf+18))
ros_package_path_ok = true;
}
else if (!strcmp(linebuf+18, getenv("ROS_PACKAGE_PATH")))
ros_package_path_ok = true;
}
}
else
break; // we're out of the header. nothing more matters to this check.
}
fclose(cache);
return ros_root_ok && ros_package_path_ok;
}
class CrawlQueueEntry
{
public:
string path;
double start_time, elapsed_time;
size_t start_num_pkgs;
bool has_manifest;
CrawlQueueEntry(string _path)
: path(_path), start_time(0), elapsed_time(0),
start_num_pkgs(0), has_manifest(false){ }
bool operator>(const CrawlQueueEntry &rhs) const
{
return elapsed_time > rhs.elapsed_time;
}
};
double ROSPack::time_since_epoch()
{
struct timeval tod;
gettimeofday(&tod, NULL);
return tod.tv_sec + 1e-6 * tod.tv_usec;
}
bool ROSPack::useBinDepPath()
{
const char *bdp_env = getenv("ROS_BINDEPS_PATH");
if (bdp_env)
return file_exists(string(bdp_env));
else
return file_exists("/opt/ros");
}
string ROSPack::getBinDepPath()
{
// We assume that the caller already checked getBinDepPath()
const char *bdp_env = getenv("ROS_BINDEPS_PATH");
if (bdp_env)
return string(bdp_env);
else
return string("/opt/ros");
}
void ROSPack::crawl_for_packages(bool force_crawl)
{
for (VecPkg::iterator p = Package::pkgs.begin();
p != Package::pkgs.end(); ++p)
Package::deleted_pkgs.push_back(*p);
Package::pkgs.clear();
if(!force_crawl && cache_is_good())
{
string cache_path = getCachePath();
FILE *cache = fopen(cache_path.c_str(), "r");
if (cache) // one last check just in case nutty stuff happened in between
{
#ifdef VERBOSE_DEBUG
printf("trying to use cache...\n");
#endif
char linebuf[30000];
for(;;)
{
if (!fgets(linebuf, sizeof(linebuf), cache))
break; // error in read operation
if (linebuf[0] == '#')
continue;
char *newline_pos = strchr(linebuf, '\n');
if (newline_pos)
*newline_pos = 0;
Package::pkgs.push_back(new Package(linebuf));
}
fclose(cache);
return; // cache load went OK; we're done here.
}
}
// if we get here, this means the cache either bogus or we've been
// instructed to rebuild it.
#ifdef VERBOSE_DEBUG
printf("building cache\n");
#endif
deque<CrawlQueueEntry> q;
q.push_back(CrawlQueueEntry(ros_root));
if (char *rpp = getenv("ROS_PACKAGE_PATH"))
{
vector<string> rppvec;
string_split(rpp, rppvec, ":");
sanitize_rppvec(rppvec);
for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i)
{
if(!i->size())
continue;
// Check whether this part of ROS_PACKAGE_PATH is itself a package
if (Package::is_package(*i))
Package::pkgs.push_back(new Package(*i));
else if (Package::is_no_subdirs(*i))
fprintf(stderr, "[rospack] WARNING: non-package directory in "
"ROS_PACKAGE_PATH marked rospack_nosubdirs:\n\t%s\n",
i->c_str());
else
q.push_back(CrawlQueueEntry(*i));
}
}
const double crawl_start_time = time_since_epoch();
priority_queue<CrawlQueueEntry, vector<CrawlQueueEntry>,
greater<CrawlQueueEntry> > profile;
while (!q.empty())
{
CrawlQueueEntry cqe = q.front();
q.pop_front();
//printf("crawling %s\n", cqe.path.c_str());
if (opt_profile_length != 0)
{
if (cqe.start_time != 0)
{
// this stack symbol means we've already crawled its children, and it's
// just here for timing purposes.
// save the traversal time
cqe.elapsed_time = time_since_epoch() - cqe.start_time;
// Did the number of packages increase since we started crawling
// this directory's children? If not, then this is likely a zombie
// directory that should probably be deleted. We'll mark it as
// such in the profile console output.
if(cqe.start_num_pkgs < total_num_pkgs)
cqe.has_manifest = true;
if(!opt_profile_zombie_only || !cqe.has_manifest)
{
profile.push(cqe);
if ((opt_profile_length > 0) && (profile.size() > opt_profile_length)) // only save the worst guys
profile.pop();
}
continue;
}
cqe.start_time = time_since_epoch();
cqe.start_num_pkgs = total_num_pkgs;
q.push_front(cqe);
}
DIR *d = opendir(cqe.path.c_str());
if (!d)
{
fprintf(stderr, "[rospack] opendir error [%s] while crawling %s\n",
strerror(errno), cqe.path.c_str());
continue;
}
struct dirent *ent;
while ((ent = readdir(d)) != NULL)
{
struct stat s;
string child_path = cqe.path + fs_delim + string(ent->d_name);
int ret;
while ((ret = stat(child_path.c_str(), &s)) != 0 &&
errno == EINTR);
if (ret != 0)
continue;
if (!S_ISDIR(s.st_mode))
continue;
if (ent->d_name[0] == '.')
continue; // ignore hidden dirs
if (Package::is_package(child_path))
{
total_num_pkgs++;
// Filter out duplicates; first encountered takes precedence
Package* newp = new Package(child_path);
// TODO: make this check more efficient
bool dup = false;
for(std::vector<Package *>::const_iterator it = Package::pkgs.begin();
it != Package::pkgs.end();
it++)
{
if((*it)->name == newp->name)
{
dup=true;
break;
}
}
if(dup)
delete newp;
else
Package::pkgs.push_back(newp);
}
//check to make sure we're allowed to descend
else if (!Package::is_no_subdirs(child_path))
q.push_front(CrawlQueueEntry(child_path));
}
closedir(d);
}
crawled = true; // don't try to re-crawl if we can't find something
const double crawl_elapsed_time = time_since_epoch() - crawl_start_time;
// Write the results of this crawl to the cache file. At each step, give
// up on error, printing a warning to stderr.
string cache_path(getCachePath());
if(!cache_path.size())
{
fprintf(stderr, "[rospack] No location available to write cache file. Try setting ROS_HOME or HOME.\n");
}
else
{
char tmp_cache_dir[PATH_MAX];
char tmp_cache_path[PATH_MAX];
strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rospack_cache.XXXXXX", dirname(tmp_cache_dir));
int fd = mkstemp(tmp_cache_path);
if (fd < 0)
{
fprintf(stderr, "[rospack] Unable to create temporary cache file %s: %s\n",
tmp_cache_path, strerror(errno));
}
else
{
FILE *cache = fdopen(fd, "w");
if (!cache)
{
fprintf(stderr, "[rospack] Unable open cache file %s: %s\n",
tmp_cache_path, strerror(errno));
}
else
{
char *rpp = getenv("ROS_PACKAGE_PATH");
fprintf(cache, "#ROS_ROOT=%s\n#ROS_PACKAGE_PATH=%s\n", ros_root,
(rpp ? rpp : ""));
for (VecPkg::iterator pkg = Package::pkgs.begin();
pkg != Package::pkgs.end(); ++pkg)
fprintf(cache, "%s\n", (*pkg)->path.c_str());
fclose(cache);
if(rename(tmp_cache_path, cache_path.c_str()) < 0)
{
fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n",
tmp_cache_path, cache_path.c_str(), strerror(errno));
}
}
}
}
if (opt_profile_length)
{
// dump it into a stack to reverse it (so slowest guys are first)
stack<CrawlQueueEntry> reverse_profile;
// Also build up a separate list of paths that will be used to remove
// children of zombies.
vector<string> zombie_dirs;
zombie_dirs.reserve(profile.size());
while (!profile.empty())
{
reverse_profile.push(profile.top());
zombie_dirs.push_back(profile.top().path);
profile.pop();
}
if(!opt_profile_zombie_only)
{
//printf("\nFull tree crawl took %.6f seconds.\n", crawl_elapsed_time);
//printf("Directories marked with (*) contain no manifest. You may\n");
//printf("want to delete these directories.\n");
//printf("-------------------------------------------------------------\n");
char buf[16];
snprintf(buf, sizeof(buf), "%.6f", crawl_elapsed_time);
output_acc += "\nFull tree crawl took " + string(buf) + " seconds.\n";
output_acc += "Directories marked with (*) contain no manifest. You may\n";
output_acc += "want to delete these directories.\n";
output_acc += "To get just of list of directories without manifests,\n";
output_acc += "re-run the profile with --zombie-only\n.";
output_acc += "-------------------------------------------------------------\n";
}
while (!reverse_profile.empty())
{
CrawlQueueEntry cqe = reverse_profile.top();
reverse_profile.pop();
if(!opt_profile_zombie_only)
{
//printf("%.6f %s %s\n",
//cqe.elapsed_time,
//cqe.has_manifest ? " " : "*",
//cqe.path.c_str());
char buf[16];
snprintf(buf, sizeof(buf), "%.6f", cqe.elapsed_time);
output_acc += string(buf) + " ";
if(cqe.has_manifest)
output_acc += " ";
else
output_acc += "* ";
output_acc += cqe.path;
output_acc += "\n";
}
else
{
bool dup = false;
// Does this directory contain a stack.xml or app.xml? In that
// case, it's an empty stack or app, and should not be considered a
// zombie.
if(file_exists(cqe.path + fs_delim + "stack.xml") ||
file_exists(cqe.path + fs_delim + "app.xml"))
{
continue;
}
//
// Does this entry's parent appear in the list?
for(vector<string>::const_iterator it = zombie_dirs.begin();
it != zombie_dirs.end();
++it)
{
if((cqe.path.size() > it->size()) &&
(cqe.path.substr(0,it->size()) == (*it)))
{
dup = true;
break;
}
}
if(dup)
continue;
//printf("%s\n", cqe.path.c_str());
output_acc += cqe.path + "\n";
}
}
if(!opt_profile_zombie_only)
{
//printf("\n");
output_acc += "\n";
}
}
}
VecPkg ROSPack::partial_crawl(const string &path)
{
deque<CrawlQueueEntry> q;
q.push_back(CrawlQueueEntry(path));
VecPkg partial_pkgs;
while (!q.empty())
{
CrawlQueueEntry cqe = q.front();
//printf("crawling %s\n", cqe.path.c_str());
q.pop_front();
DIR *d = opendir(cqe.path.c_str());
if (!d)
{
fprintf(stderr, "[rospack] opendir error [%s] while crawling %s\n",
strerror(errno), cqe.path.c_str());
continue;
}
struct dirent *ent;
while ((ent = readdir(d)) != NULL)
{
struct stat s;
string child_path = cqe.path + fs_delim + string(ent->d_name);
int ret;
while ((ret = stat(child_path.c_str(), &s)) != 0 &&
errno == EINTR);
if (ret != 0)
continue;
if (!S_ISDIR(s.st_mode))
continue;
if (ent->d_name[0] == '.')
continue; // ignore hidden dirs
if (Package::is_package(child_path))
{
// Filter out duplicates; first encountered takes precedence
Package* newp = new Package(child_path);
// TODO: make this check more efficient
bool dup = false;
for(std::vector<Package *>::const_iterator it = partial_pkgs.begin();
it != partial_pkgs.end();
it++)
{
if((*it)->name == newp->name)
{
dup=true;
break;
}
}
if(dup)
delete newp;
else
partial_pkgs.push_back(newp);
}
//check to make sure we're allowed to descend
else if (!Package::is_no_subdirs(child_path))
q.push_front(CrawlQueueEntry(child_path));
}
closedir(d);
}
return partial_pkgs;
}
//////////////////////////////////////////////////////////////////////////////
void string_split(const string &s, vector<string> &t, const string &d)
{
t.clear();
size_t start = 0, end;
while ((end = s.find_first_of(d, start)) != string::npos)
{
if((end-start) > 0)
t.push_back(s.substr(start, end-start));
start = end + 1;
}
if(start < s.size())
t.push_back(s.substr(start));
}
// Produce a new string by keeping only the first of each repeated token in
// the input string, where tokens are space-separated. I'm sure that Rob
// could point me at the Boost/STL one-liner that does the same thing.
string ROSPack::deduplicate_tokens(const string& s)
{
vector<string> in;
vector<string> out;
string_split(s, in, " ");
for(int i=0; i<in.size(); i++)
{
bool dup = false;
for(int j=0; j<out.size(); j++)
{
if(!out[j].compare(in[i]))
{
dup = true;
break;
}
}
if(!dup)
out.push_back(in[i]);
}
string res;
for(int j=0; j<out.size(); j++)
{
if(!j)
res += out[j];
else
res += string(" ") + out[j];
}
return res;
}
bool file_exists(const string &fname)
{
// this will be different in windows
return (access(fname.c_str(), F_OK) == 0);
}
Package *g_get_pkg(const string &name)
{
// a hack... but I'm lazy and love single-file programs
return g_rospack->get_pkg(name);
}
void ROSPack::sanitize_rppvec(std::vector<std::string> &rppvec)
{
// drop any trailing slashes
for (size_t i = 0; i < rppvec.size(); i++)
{
size_t last_slash_pos;
while((last_slash_pos = rppvec[i].find_last_of("/")) == rppvec[i].length()-1)
{
fprintf(stderr, "[rospack] warning: trailing slash found in "
"ROS_PACKAGE_PATH\n");
rppvec[i].erase(last_slash_pos);
}
}
}
}