rospack.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008, Willow Garage, Inc., Morgan Quigley
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice,
7  * this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
12  * contributors may be used to endorse or promote products derived from
13  * this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "rospack/rospack.h"
29 #include "utils.h"
30 #include "tinyxml.h"
31 
32 #include <boost/algorithm/string.hpp>
33 #include <boost/filesystem.hpp>
34 #include <boost/functional/hash.hpp>
35 #include <stdexcept>
36 
37 #if defined(WIN32)
38  #include <windows.h>
39  #include <direct.h>
40  #include <fcntl.h> // for O_RDWR, O_EXCL, O_CREAT
41  // simple workaround - could have issues though. See
42  // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
43  // for potentially better solutions. Similar probably applies for some of the others
44  #define snprintf _snprintf
45  #define pclose _pclose
46  #define popen _popen
47  #define PATH_MAX MAX_PATH
48  #if defined(__MINGW32__)
49  #include <libgen.h> // for dirname
50  #endif
51  #if defined(_MSC_VER)
52  #include <io.h> // _mktemp_s
53  #endif
54 #else
55  #include <sys/types.h>
56  #include <libgen.h>
57  #include <pwd.h>
58  #include <unistd.h>
59  #include <sys/time.h>
60 #endif
61 
62 #include <algorithm>
63 #include <climits>
64 
65 #include <sys/stat.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <time.h>
69 #include <string.h>
70 #include <errno.h>
71 
72 #include <Python.h>
73 
74 /* re-define some String functions for python 2.x */
75 #if PY_VERSION_HEX < 0x03000000
76 #undef PyBytes_AsString
77 #undef PyUnicode_AsUTF8
78 #undef PyUnicode_FromString
79 #define PyBytes_AsString PyString_AsString
80 #define PyUnicode_AsUTF8 PyString_AsString
81 #define PyUnicode_FromString PyString_FromString
82 #endif
83 
84 // TODO:
85 // recrawl on:
86 // package not found in cache
87 // package found in cache, but no manifest.xml present in filesystem
88 
89 namespace fs = boost::filesystem;
90 
91 namespace rospack
92 {
93 static const char* MANIFEST_TAG_PACKAGE = "package";
94 static const char* MANIFEST_TAG_STACK = "stack";
95 static const char* ROSPACK_MANIFEST_NAME = "manifest.xml";
96 static const char* ROSPACKAGE_MANIFEST_NAME = "package.xml";
97 static const char* ROSSTACK_MANIFEST_NAME = "stack.xml";
98 static const char* ROSPACK_CACHE_PREFIX = "rospack_cache";
99 static const char* ROSSTACK_CACHE_PREFIX = "rosstack_cache";
100 static const char* ROSPACK_NOSUBDIRS = "rospack_nosubdirs";
101 static const char* CATKIN_IGNORE = "CATKIN_IGNORE";
102 static const char* DOTROS_NAME = ".ros";
103 static const char* MSG_GEN_GENERATED_DIR = "msg_gen";
104 static const char* MSG_GEN_GENERATED_FILE = "generated";
105 static const char* SRV_GEN_GENERATED_DIR = "srv_gen";
106 static const char* SRV_GEN_GENERATED_FILE = "generated";
107 static const char* MANIFEST_TAG_ROSDEP = "rosdep";
108 static const char* MANIFEST_TAG_VERSIONCONTROL = "versioncontrol";
109 static const char* MANIFEST_TAG_EXPORT = "export";
110 static const char* MANIFEST_ATTR_NAME = "name";
111 static const char* MANIFEST_ATTR_TYPE = "type";
112 static const char* MANIFEST_ATTR_URL = "url";
113 static const char* MANIFEST_PREFIX = "${prefix}";
114 static const int MAX_CRAWL_DEPTH = 1000;
115 static const int MAX_DEPENDENCY_DEPTH = 1000;
116 static const double DEFAULT_MAX_CACHE_AGE = 60.0;
117 
118 TiXmlElement* get_manifest_root(Stackage* stackage);
119 double time_since_epoch();
120 
121 #ifdef __APPLE__
122  static const std::string g_ros_os = "osx";
123 #else
124  #if defined(WIN32)
125  static const std::string g_ros_os = "win32";
126  #else
127  static const std::string g_ros_os = "linux";
128  #endif
129 #endif
130 
131 class Exception : public std::runtime_error
132 {
133  public:
134  Exception(const std::string& what)
135  : std::runtime_error(what)
136  {}
137 };
138 
139 class Stackage
140 {
141  public:
142  // \brief name of the stackage
143  std::string name_;
144  // \brief absolute path to the stackage
145  std::string path_;
146  // \brief absolute path to the stackage manifest
147  std::string manifest_path_;
148  // \brief filename of the stackage manifest
149  std::string manifest_name_;
150  // \brief package's license with a support for multi-license.
151  std::vector<std::string> licenses_;
152  // \brief have we already loaded the manifest?
154  // \brief TinyXML structure, filled in during parsing
155  TiXmlDocument manifest_;
156  std::vector<Stackage*> deps_;
160 
161  Stackage(const std::string& name,
162  const std::string& path,
163  const std::string& manifest_path,
164  const std::string& manifest_name) :
165  name_(name),
166  path_(path),
167  manifest_path_(manifest_path),
168  manifest_name_(manifest_name),
169  manifest_loaded_(false),
170  deps_computed_(false),
171  is_metapackage_(false)
172  {
173  is_wet_package_ = manifest_name_ == ROSPACKAGE_MANIFEST_NAME;
174  }
175 
177  {
178  assert(is_wet_package_);
179  assert(manifest_loaded_);
180  // get name from package.xml instead of folder name
181  TiXmlElement* root = get_manifest_root(this);
182  for(TiXmlElement* el = root->FirstChildElement("name"); el; el = el->NextSiblingElement("name"))
183  {
184  name_ = el->GetText();
185  break;
186  }
187  // Get license texts, where there may be multiple elements for.
188  std::string tagname_license = "license";
189  for(TiXmlElement* el = root->FirstChildElement(tagname_license); el; el = el->NextSiblingElement(tagname_license ))
190  {
191  licenses_.push_back(el->GetText());
192  }
193  // check if package is a metapackage
194  for(TiXmlElement* el = root->FirstChildElement("export"); el; el = el->NextSiblingElement("export"))
195  {
196  if(el->FirstChildElement("metapackage"))
197  {
198  is_metapackage_ = true;
199  break;
200  }
201  }
202  }
203 
204  bool isStack() const
205  {
206  return manifest_name_ == MANIFEST_TAG_STACK || (is_wet_package_ && is_metapackage_);
207  }
208 
209  bool isPackage() const
210  {
211  return manifest_name_ == MANIFEST_TAG_PACKAGE || (is_wet_package_ && !is_metapackage_);
212  }
213 
214 };
215 
217 {
218  public:
219  std::string path_;
220  bool zombie_;
221  double start_time_;
222  double crawl_time_;
224  DirectoryCrawlRecord(std::string path,
225  double start_time,
226  size_t start_num_pkgs) :
227  path_(path),
228  zombie_(false),
229  start_time_(start_time),
230  crawl_time_(0.0),
231  start_num_pkgs_(start_num_pkgs) {}
232 };
235 {
236  return (i->crawl_time_ < j->crawl_time_);
237 }
238 
240 // Rosstackage methods (public/protected)
242 Rosstackage::Rosstackage(const std::string& manifest_name,
243  const std::string& cache_prefix,
244  const std::string& name,
245  const std::string& tag) :
246  manifest_name_(manifest_name),
247  cache_prefix_(cache_prefix),
248  crawled_(false),
249  name_(name),
250  tag_(tag)
251 {
252 }
253 
255 {
256  clearStackages();
257 }
258 
260 {
261 #if BOOST_VERSION < 106500
262  for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
263 #else
264  for(boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
265 #endif
266  it != stackages_.end();
267  ++it)
268  {
269  delete it->second;
270  }
271  stackages_.clear();
272  dups_.clear();
273 }
274 
275 void
276 Rosstackage::logWarn(const std::string& msg,
277  bool append_errno)
278 {
279  log("Warning", msg, append_errno);
280 }
281 void
282 Rosstackage::logError(const std::string& msg,
283  bool append_errno)
284 {
285  log("Error", msg, append_errno);
286 }
287 
288 bool
289 Rosstackage::getSearchPathFromEnv(std::vector<std::string>& sp)
290 {
291  char* rpp = getenv("ROS_PACKAGE_PATH");
292  if(rpp)
293  {
294  // I can't see that boost filesystem has an elegant cross platform
295  // representation for this anywhere like qt/python have.
296  #if defined(WIN32)
297  const char *path_delim = ";";
298  #else
299  const char *path_delim = ":";
300  #endif
301 
302  std::vector<std::string> rpp_strings;
303  boost::split(rpp_strings, rpp,
304  boost::is_any_of(path_delim),
305  boost::token_compress_on);
306  for(std::vector<std::string>::const_iterator it = rpp_strings.begin();
307  it != rpp_strings.end();
308  ++it)
309  {
310  sp.push_back(*it);
311  }
312  }
313  return true;
314 }
315 
316 void
318 {
319  quiet_ = quiet;
320 }
321 
322 bool
323 Rosstackage::isStackage(const std::string& path)
324 {
325  try
326  {
327  if(!fs::is_directory(path))
328  return false;
329  }
330  catch(fs::filesystem_error& e)
331  {
332  logWarn(std::string("error while looking at ") + path + ": " + e.what());
333  return false;
334  }
335 
336  try
337  {
338  for(fs::directory_iterator dit = fs::directory_iterator(path);
339  dit != fs::directory_iterator();
340  ++dit)
341  {
342  if(!fs::is_regular_file(dit->path()))
343  continue;
344 
345  if(dit->path().filename() == manifest_name_)
346  return true;
347 
348  // finding a package.xml is acceptable
349  if(dit->path().filename() == ROSPACKAGE_MANIFEST_NAME)
350  return true;
351  }
352  }
353  catch(fs::filesystem_error& e)
354  {
355  logWarn(std::string("error while crawling ") + path + ": " + e.what());
356  }
357  return false;
358 }
359 
360 void
361 Rosstackage::crawl(std::vector<std::string> search_path,
362  bool force)
363 {
364  if(!force)
365  {
366  bool same_search_paths = (search_path == search_paths_);
367 
368  // if search paths differ, try to reading the cache corresponding to the new paths
369  if(!same_search_paths && readCache())
370  {
371  // If the cache was valid, then the paths in the cache match the ones
372  // we've been asked to crawl. Store them, so that later, methods
373  // like find() can refer to them when recrawling.
374  search_paths_ = search_path;
375  return;
376  }
377 
378  if(crawled_ && same_search_paths)
379  return;
380  }
381 
382  // We're about to crawl, so clear internal storage (in case this is the second
383  // run in this process).
384  clearStackages();
385  search_paths_ = search_path;
386 
387  std::vector<DirectoryCrawlRecord*> dummy;
388 
389 #if BOOST_VERSION < 106500
390  std::tr1::unordered_set<std::string> dummy2;
391 #else
392  boost::unordered_set<std::string> dummy2;
393 #endif
394  for(std::vector<std::string>::const_iterator p = search_paths_.begin();
395  p != search_paths_.end();
396  ++p)
397  crawlDetail(*p, force, 1, false, dummy, dummy2);
398 
399  crawled_ = true;
400 
401  writeCache();
402 }
403 
404 bool
405 Rosstackage::inStackage(std::string& name)
406 {
407  fs::path path;
408  try
409  {
410  // Search upward, as suggested in #3697. This method is only used when
411  // a package is not given on the command-line, and so should not have
412  // performance impact in common usage.
413  for(fs::path path = fs::current_path();
414  !path.empty();
415  path = path.parent_path())
416  {
417  if(Rosstackage::isStackage(path.string()))
418  {
419 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
420  name = fs::path(path).filename();
421 #else
422  // in boostfs3, filename() returns a path, which needs to be stringified
423  name = fs::path(path).filename().string();
424 #endif
425  return true;
426  }
427  }
428  // Search failed.
429  return false;
430  }
431  catch(fs::filesystem_error& e)
432  {
433  // can't determine current directory, or encountered trouble while
434  // searching upward; too bad
435  return false;
436  }
437 }
438 
439 
440 bool
441 Rosstackage::find(const std::string& name, std::string& path)
442 {
443  Stackage* s = findWithRecrawl(name);
444  if(s)
445  {
446  path = s->path_;
447  return true;
448  }
449  else
450  return false;
451 }
452 
453 bool
454 Rosstackage::contents(const std::string& name,
455  std::set<std::string>& packages)
456 {
457  Rospack rp2;
458 #if BOOST_VERSION < 106500
459  std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
460 #else
461  boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
462 #endif
463  if(it != stackages_.end())
464  {
465  std::vector<std::string> search_paths;
466  search_paths.push_back(it->second->path_);
467  rp2.crawl(search_paths, true);
468  std::set<std::pair<std::string, std::string> > names_paths;
469  rp2.list(names_paths);
470  for(std::set<std::pair<std::string, std::string> >::const_iterator iit = names_paths.begin();
471  iit != names_paths.end();
472  ++iit)
473  packages.insert(iit->first);
474  return true;
475  }
476  else
477  {
478  logError(std::string("stack ") + name + " not found");
479  return false;
480  }
481 }
482 
483 bool
484 Rosstackage::contains(const std::string& name,
485  std::string& stack,
486  std::string& path)
487 {
488  Rospack rp2;
489 #if BOOST_VERSION < 106500
490  for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
491 #else
492  for(boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
493 #endif
494  it != stackages_.end();
495  ++it)
496  {
497  std::vector<std::string> search_paths;
498  search_paths.push_back(it->second->path_);
499  rp2.crawl(search_paths, true);
500  std::set<std::pair<std::string, std::string> > names_paths;
501  rp2.list(names_paths);
502  for(std::set<std::pair<std::string, std::string> >::const_iterator iit = names_paths.begin();
503  iit != names_paths.end();
504  ++iit)
505  {
506  if(iit->first == name)
507  {
508  stack = it->first;
509  path = it->second->path_;
510  return true;
511  }
512  }
513  }
514 
515  logError(std::string("stack containing package ") + name + " not found");
516  return false;
517 }
518 
519 void
520 Rosstackage::list(std::set<std::pair<std::string, std::string> >& list)
521 {
522 #if BOOST_VERSION < 106500
523  for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
524 #else
525  for(boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
526 #endif
527  it != stackages_.end();
528  ++it)
529  {
530  std::pair<std::string, std::string> item;
531  item.first = it->first;
532  item.second = it->second->path_;
533  list.insert(item);
534  }
535 }
536 
537 void
538 Rosstackage::listDuplicates(std::vector<std::string>& dups)
539 {
540  dups.resize(dups_.size());
541  int i = 0;
542 #if BOOST_VERSION < 106500
543  for(std::tr1::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
544 #else
545  for(boost::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
546 #endif
547  it != dups_.end();
548  ++it)
549  {
550  dups[i] = it->first;
551  i++;
552  }
553 }
554 
555 void
556 Rosstackage::listDuplicatesWithPaths(std::map<std::string, std::vector<std::string> >& dups)
557 {
558  dups.clear();
559 #if BOOST_VERSION < 106500
560  for(std::tr1::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
561 #else
562  for(boost::unordered_map<std::string, std::vector<std::string> >::const_iterator it = dups_.begin();
563 #endif
564  it != dups_.end();
565  ++it)
566  {
567  dups[it->first].resize(it->second.size());
568  int j = 0;
569  for(std::vector<std::string>::const_iterator jt = it->second.begin();
570  jt != it->second.end();
571  ++jt)
572  {
573  dups[it->first][j] = *jt;
574  j++;
575  }
576  }
577 }
578 
579 bool
580 Rosstackage::deps(const std::string& name, bool direct,
581  std::vector<std::string>& deps)
582 {
583  std::vector<Stackage*> stackages;
584  // Disable errors for the first try
585  bool old_quiet = quiet_;
586  setQuiet(true);
587  if(!depsDetail(name, direct, stackages))
588  {
589  // Recrawl
590  crawl(search_paths_, true);
591  stackages.clear();
592  setQuiet(old_quiet);
593  if(!depsDetail(name, direct, stackages))
594  return false;
595  }
596  setQuiet(old_quiet);
597  for(std::vector<Stackage*>::const_iterator it = stackages.begin();
598  it != stackages.end();
599  ++it)
600  deps.push_back((*it)->name_);
601  return true;
602 }
603 
604 bool
605 Rosstackage::depsOn(const std::string& name, bool direct,
606  std::vector<std::string>& deps)
607 {
608  std::vector<Stackage*> stackages;
609  if(!depsOnDetail(name, direct, stackages))
610  return false;
611  for(std::vector<Stackage*>::const_iterator it = stackages.begin();
612  it != stackages.end();
613  ++it)
614  deps.push_back((*it)->name_);
615  return true;
616 }
617 
618 bool
619 Rosstackage::depsIndent(const std::string& name, bool direct,
620  std::vector<std::string>& deps)
621 {
622  Stackage* stackage = findWithRecrawl(name);
623  if(!stackage)
624  return false;
625  try
626  {
627  computeDeps(stackage);
628  std::vector<Stackage*> deps_vec;
629 #if BOOST_VERSION < 106500
630  std::tr1::unordered_set<Stackage*> deps_hash;
631 #else
632  boost::unordered_set<Stackage*> deps_hash;
633 #endif
634  std::vector<std::string> indented_deps;
635  gatherDepsFull(stackage, direct, POSTORDER, 0, deps_hash, deps_vec, true, indented_deps);
636  for(std::vector<std::string>::const_iterator it = indented_deps.begin();
637  it != indented_deps.end();
638  ++it)
639  deps.push_back(*it);
640  }
641  catch(Exception& e)
642  {
643  logError(e.what());
644  return false;
645  }
646  return true;
647 }
648 
649 bool
650 Rosstackage::depsWhy(const std::string& from,
651  const std::string& to,
652  std::string& output)
653 {
654  Stackage* from_s = findWithRecrawl(from);
655  if(!from_s)
656  return false;
657  Stackage* to_s = findWithRecrawl(to);
658  if(!to_s)
659  return false;
660 
661  std::list<std::list<Stackage*> > acc_list;
662  try
663  {
664  depsWhyDetail(from_s, to_s, acc_list);
665  }
666  catch(Exception& e)
667  {
668  logError(e.what());
669  return true;
670  }
671  output.append(std::string("Dependency chains from ") +
672  from + " to " + to + ":\n");
673  for(std::list<std::list<Stackage*> >::const_iterator it = acc_list.begin();
674  it != acc_list.end();
675  ++it)
676  {
677  output.append("* ");
678  for(std::list<Stackage*>::const_iterator iit = it->begin();
679  iit != it->end();
680  ++iit)
681  {
682  if(iit != it->begin())
683  output.append("-> ");
684  output.append((*iit)->name_ + " ");
685  }
686  output.append("\n");
687  }
688  return true;
689 }
690 
691 bool
692 Rosstackage::depsManifests(const std::string& name, bool direct,
693  std::vector<std::string>& manifests)
694 {
695  Stackage* stackage = findWithRecrawl(name);
696  if(!stackage)
697  return false;
698  try
699  {
700  computeDeps(stackage);
701  std::vector<Stackage*> deps_vec;
702  gatherDeps(stackage, direct, POSTORDER, deps_vec);
703  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
704  it != deps_vec.end();
705  ++it)
706  manifests.push_back((*it)->manifest_path_);
707  }
708  catch(Exception& e)
709  {
710  logError(e.what());
711  return false;
712  }
713  return true;
714 }
715 
716 bool
717 Rosstackage::rosdeps(const std::string& name, bool direct,
718  std::set<std::string>& rosdeps)
719 {
720  Stackage* stackage = findWithRecrawl(name);
721  if(!stackage)
722  return false;
723  try
724  {
725  computeDeps(stackage);
726  std::vector<Stackage*> deps_vec;
727  // rosdeps include the current package
728  deps_vec.push_back(stackage);
729  if(!direct)
730  gatherDeps(stackage, direct, POSTORDER, deps_vec);
731  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
732  it != deps_vec.end();
733  ++it)
734  {
735  if (!stackage->is_wet_package_)
736  {
737  _rosdeps(*it, rosdeps, MANIFEST_TAG_ROSDEP);
738  }
739  else
740  {
741  // package format 1 tags
742  _rosdeps(*it, rosdeps, "build_depend");
743  _rosdeps(*it, rosdeps, "buildtool_depend");
744  _rosdeps(*it, rosdeps, "run_depend");
745  // package format 2 tags
746  _rosdeps(*it, rosdeps, "build_export_depend");
747  _rosdeps(*it, rosdeps, "buildtool_export_depend");
748  _rosdeps(*it, rosdeps, "exec_depend");
749  _rosdeps(*it, rosdeps, "depend");
750  _rosdeps(*it, rosdeps, "doc_depend");
751  _rosdeps(*it, rosdeps, "test_depend");
752  }
753  }
754  }
755  catch(Exception& e)
756  {
757  logError(e.what());
758  return false;
759  }
760  return true;
761 }
762 
763 void
764 Rosstackage::_rosdeps(Stackage* stackage, std::set<std::string>& rosdeps, const char* tag_name)
765 {
766  TiXmlElement* root = get_manifest_root(stackage);
767  for(TiXmlElement* ele = root->FirstChildElement(tag_name);
768  ele;
769  ele = ele->NextSiblingElement(tag_name))
770  {
771  if(!stackage->is_wet_package_)
772  {
773  const char *att_str;
774  if((att_str = ele->Attribute(MANIFEST_ATTR_NAME)))
775  {
776  rosdeps.insert(std::string("name: ") + att_str);
777  }
778  }
779  else
780  {
781  const char* dep_pkgname = ele->GetText();
782  if(isSysPackage(dep_pkgname))
783  {
784  rosdeps.insert(std::string("name: ") + dep_pkgname);
785  }
786  }
787  }
788 }
789 
790 bool
791 Rosstackage::vcs(const std::string& name, bool direct,
792  std::vector<std::string>& vcs)
793 {
794  Stackage* stackage = findWithRecrawl(name);
795  if(!stackage)
796  return false;
797  try
798  {
799  computeDeps(stackage);
800  std::vector<Stackage*> deps_vec;
801  // vcs include the current package
802  deps_vec.push_back(stackage);
803  if(!direct)
804  gatherDeps(stackage, direct, POSTORDER, deps_vec);
805  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
806  it != deps_vec.end();
807  ++it)
808  {
809  TiXmlElement* root = get_manifest_root(*it);
810  for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_VERSIONCONTROL);
811  ele;
812  ele = ele->NextSiblingElement(MANIFEST_TAG_VERSIONCONTROL))
813  {
814  std::string result;
815  const char *att_str;
816  if((att_str = ele->Attribute(MANIFEST_ATTR_TYPE)))
817  {
818  result.append("type: ");
819  result.append(att_str);
820  }
821  if((att_str = ele->Attribute(MANIFEST_ATTR_URL)))
822  {
823  result.append("\turl: ");
824  result.append(att_str);
825  }
826  vcs.push_back(result);
827  }
828  }
829  }
830  catch(Exception& e)
831  {
832  logError(e.what());
833  return false;
834  }
835  return true;
836 }
837 
838 bool
839 Rosstackage::cpp_exports(const std::string& name, const std::string& type,
840  const std::string& attrib, bool deps_only,
841  std::vector<std::pair<std::string, bool> >& flags)
842 {
843  Stackage* stackage = findWithRecrawl(name);
844  if(!stackage)
845  return false;
846 
847  static bool init_py = false;
848  static PyObject* pName;
849  static PyObject* pModule;
850  static PyObject* pDict;
851  static PyObject* pFunc;
852 
853  try
854  {
855  computeDeps(stackage);
856  std::vector<Stackage*> deps_vec;
857  if(!deps_only)
858  deps_vec.push_back(stackage);
859  gatherDeps(stackage, false, PREORDER, deps_vec, true);
860  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
861  it != deps_vec.end();
862  ++it)
863  {
864  if(!(*it)->is_wet_package_)
865  {
866  std::vector<std::string> dry_flags;
867  if(!exports_dry_package(*it, "cpp", attrib, dry_flags))
868  {
869  return false;
870  }
871  for(std::vector<std::string>::const_iterator it = dry_flags.begin(); it != dry_flags.end(); ++it)
872  {
873  flags.push_back(std::pair<std::string, bool>(*it, false));
874  }
875  }
876  else
877  {
878  initPython();
879  PyGILState_STATE gstate = PyGILState_Ensure();
880 
881  if(!init_py)
882  {
883  init_py = true;
884  pName = PyUnicode_FromString("rosdep2.rospack");
885  pModule = PyImport_Import(pName);
886  if(!pModule)
887  {
888  PyErr_Print();
889  PyGILState_Release(gstate);
890  std::string errmsg = "could not find python module 'rosdep2.rospack'. is rosdep up-to-date (at least 0.10.4)?";
891  throw Exception(errmsg);
892  }
893  pDict = PyModule_GetDict(pModule);
894  pFunc = PyDict_GetItemString(pDict, "call_pkg_config");
895  }
896 
897  if(!PyCallable_Check(pFunc))
898  {
899  PyErr_Print();
900  PyGILState_Release(gstate);
901  std::string errmsg = "could not find python function 'rosdep2.rospack.call_pkg_config'. is rosdep up-to-date (at least 0.10.7)?";
902  throw Exception(errmsg);
903  }
904 
905  PyObject* pArgs = PyTuple_New(2);
906  PyObject* pOpt = PyUnicode_FromString(type.c_str());
907  PyTuple_SetItem(pArgs, 0, pOpt);
908  PyObject* pPkg = PyUnicode_FromString((*it)->name_.c_str());
909  PyTuple_SetItem(pArgs, 1, pPkg);
910  PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
911  Py_DECREF(pArgs);
912 
913  if(!pValue)
914  {
915  PyErr_Print();
916  PyGILState_Release(gstate);
917  std::string errmsg = "could not call python function 'rosdep2.rospack.call_pkg_config'";
918  throw Exception(errmsg);
919  }
920  if(pValue == Py_None)
921  {
922  Py_DECREF(pValue);
923  std::string errmsg = "python function 'rosdep2.rospack.call_pkg_config' could not call 'pkg-config " + type + " " + (*it)->name_ + "' without errors";
924  throw Exception(errmsg);
925  }
926 
927  flags.push_back(std::pair<std::string, bool>(PyBytes_AsString(pValue), true));
928  Py_DECREF(pValue);
929 
930  // we want to keep the static objects alive for repeated access
931  // so skip all garbage collection until process ends
932  //Py_DECREF(pFunc);
933  //Py_DECREF(pModule);
934  //Py_DECREF(pName);
935  //Py_Finalize();
936 
937  PyGILState_Release(gstate);
938  }
939  }
940  }
941  catch(Exception& e)
942  {
943  logError(e.what());
944  return false;
945  }
946  return true;
947 }
948 
949 bool
950 Rosstackage::reorder_paths(const std::string& paths, std::string& reordered)
951 {
952  static bool init_py = false;
953  static PyObject* pName;
954  static PyObject* pModule;
955  static PyObject* pFunc;
956 
957  initPython();
958  PyGILState_STATE gstate = PyGILState_Ensure();
959 
960  if(!init_py)
961  {
962  init_py = true;
963  pName = PyUnicode_FromString("catkin_pkg.rospack");
964  pModule = PyImport_Import(pName);
965  if(!pModule)
966  {
967  PyErr_Print();
968  PyGILState_Release(gstate);
969  std::string errmsg = "could not find python module 'catkin_pkg.rospack'. is catkin_pkg up-to-date (at least 0.1.8)?";
970  throw Exception(errmsg);
971  }
972  PyObject* pDict = PyModule_GetDict(pModule);
973  pFunc = PyDict_GetItemString(pDict, "reorder_paths");
974  }
975 
976  if(!PyCallable_Check(pFunc))
977  {
978  PyErr_Print();
979  PyGILState_Release(gstate);
980  std::string errmsg = "could not find python function 'catkin_pkg.rospack.reorder_paths'. is catkin_pkg up-to-date (at least 0.1.8)?";
981  throw Exception(errmsg);
982  }
983 
984 
985  PyObject* pArgs = PyTuple_New(1);
986  PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(paths.c_str()));
987  PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
988  Py_DECREF(pArgs);
989 
990  if(!pValue)
991  {
992  PyErr_Print();
993  PyGILState_Release(gstate);
994  std::string errmsg = "could not call python function 'catkin_pkg.rospack.reorder_paths'";
995  throw Exception(errmsg);
996  }
997 
998  reordered = PyUnicode_AsUTF8(pValue);
999  Py_DECREF(pValue);
1000 
1001  // we want to keep the static objects alive for repeated access
1002  // so skip all garbage collection until process ends
1003  //Py_DECREF(pFunc);
1004  //Py_DECREF(pModule);
1005  //Py_DECREF(pName);
1006  //Py_Finalize();
1007 
1008  PyGILState_Release(gstate);
1009 
1010  return true;
1011 }
1012 
1013 bool
1014 Rosstackage::exports(const std::string& name, const std::string& lang,
1015  const std::string& attrib, bool deps_only,
1016  std::vector<std::string>& flags)
1017 {
1018  Stackage* stackage = findWithRecrawl(name);
1019  if(!stackage)
1020  return false;
1021  try
1022  {
1023  computeDeps(stackage);
1024  std::vector<Stackage*> deps_vec;
1025  if(!deps_only)
1026  deps_vec.push_back(stackage);
1027  gatherDeps(stackage, false, PREORDER, deps_vec);
1028  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1029  it != deps_vec.end();
1030  ++it)
1031  {
1032  if (!exports_dry_package(*it, lang, attrib, flags))
1033  {
1034  return false;
1035  }
1036  }
1037  }
1038  catch(Exception& e)
1039  {
1040  logError(e.what());
1041  return false;
1042  }
1043  return true;
1044 }
1045 
1046 bool
1047 Rosstackage::exports_dry_package(Stackage* stackage, const std::string& lang,
1048  const std::string& attrib,
1049  std::vector<std::string>& flags)
1050 {
1051  TiXmlElement* root = get_manifest_root(stackage);
1052  for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_EXPORT);
1053  ele;
1054  ele = ele->NextSiblingElement(MANIFEST_TAG_EXPORT))
1055  {
1056  bool os_match = false;
1057  const char *best_match = NULL;
1058  for(TiXmlElement* ele2 = ele->FirstChildElement(lang);
1059  ele2;
1060  ele2 = ele2->NextSiblingElement(lang))
1061  {
1062  const char *os_str;
1063  if ((os_str = ele2->Attribute("os")))
1064  {
1065  if(g_ros_os == std::string(os_str))
1066  {
1067  if(os_match)
1068  logWarn(std::string("ignoring duplicate ") + lang + " tag with os=" + os_str + " in export block");
1069  else
1070  {
1071  best_match = ele2->Attribute(attrib.c_str());
1072  os_match = true;
1073  }
1074  }
1075  }
1076  if(!os_match)
1077  {
1078  if(!best_match)
1079  best_match = ele2->Attribute(attrib.c_str());
1080  else
1081  logWarn(std::string("ignoring duplicate ") + lang + " tag in export block");
1082  }
1083 
1084  }
1085  if(best_match)
1086  {
1087  std::string expanded_str;
1088  if(!expandExportString(stackage, best_match, expanded_str))
1089  return false;
1090  flags.push_back(expanded_str);
1091  }
1092  }
1093  // We automatically point to msg_gen and msg_srv directories if
1094  // certain files are present.
1095  // But only if we're looking for cpp/cflags, #3884.
1096  if((lang == "cpp") && (attrib == "cflags"))
1097  {
1098  fs::path msg_gen = fs::path(stackage->path_) / MSG_GEN_GENERATED_DIR;
1099  fs::path srv_gen = fs::path(stackage->path_) / SRV_GEN_GENERATED_DIR;
1100  if(fs::is_regular_file(msg_gen / MSG_GEN_GENERATED_FILE))
1101  {
1102  msg_gen /= fs::path("cpp") / "include";
1103  flags.push_back(std::string("-I" + msg_gen.string()));
1104  }
1105  if(fs::is_regular_file(srv_gen / SRV_GEN_GENERATED_FILE))
1106  {
1107  srv_gen /= fs::path("cpp") / "include";
1108  flags.push_back(std::string("-I" + srv_gen.string()));
1109  }
1110  }
1111  return true;
1112 }
1113 
1114 bool
1115 Rosstackage::plugins(const std::string& name, const std::string& attrib,
1116  const std::string& top,
1117  std::vector<std::string>& flags)
1118 {
1119  // Find everybody who depends directly on the package in question
1120  std::vector<Stackage*> stackages;
1121  if(!depsOnDetail(name, true, stackages, true))
1122  return false;
1123  // Also look in the package itself
1124 #if BOOST_VERSION < 106500
1125  std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
1126 #else
1127  boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.find(name);
1128  #endif
1129  if(it != stackages_.end())
1130  {
1131  // don't warn here; it was done in depsOnDetail()
1132  stackages.push_back(it->second);
1133  }
1134  // If top was given, filter to include only those package on which top
1135  // depends.
1136  if(top.size())
1137  {
1138  std::vector<Stackage*> top_deps;
1139  if(!depsDetail(top, false, top_deps))
1140  return false;
1141 #if BOOST_VERSION < 106500
1142  std::tr1::unordered_set<Stackage*> top_deps_set;
1143 #else
1144  boost::unordered_set<Stackage*> top_deps_set;
1145 #endif
1146  for(std::vector<Stackage*>::iterator it = top_deps.begin();
1147  it != top_deps.end();
1148  ++it)
1149  top_deps_set.insert(*it);
1150  std::vector<Stackage*>::iterator it = stackages.begin();
1151  while(it != stackages.end())
1152  {
1153  if((*it)->name_ != top &&
1154  (top_deps_set.find(*it) == top_deps_set.end()))
1155  it = stackages.erase(it);
1156  else
1157  ++it;
1158  }
1159  }
1160  // Now go looking for the manifest data
1161  for(std::vector<Stackage*>::const_iterator it = stackages.begin();
1162  it != stackages.end();
1163  ++it)
1164  {
1165  TiXmlElement* root = get_manifest_root(*it);
1166  for(TiXmlElement* ele = root->FirstChildElement(MANIFEST_TAG_EXPORT);
1167  ele;
1168  ele = ele->NextSiblingElement(MANIFEST_TAG_EXPORT))
1169  {
1170  for(TiXmlElement* ele2 = ele->FirstChildElement(name);
1171  ele2;
1172  ele2 = ele2->NextSiblingElement(name))
1173  {
1174  const char *att_str;
1175  if((att_str = ele2->Attribute(attrib.c_str())))
1176  {
1177  std::string expanded_str;
1178  if(!expandExportString(*it, att_str, expanded_str))
1179  return false;
1180  flags.push_back((*it)->name_ + " " + expanded_str);
1181  }
1182  }
1183  }
1184  }
1185  return true;
1186 }
1187 
1188 bool
1189 Rosstackage::depsMsgSrv(const std::string& name, bool direct,
1190  std::vector<std::string>& gens)
1191 {
1192  Stackage* stackage = findWithRecrawl(name);
1193  if(!stackage)
1194  return false;
1195  try
1196  {
1197  computeDeps(stackage);
1198  std::vector<Stackage*> deps_vec;
1199  gatherDeps(stackage, direct, POSTORDER, deps_vec);
1200  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1201  it != deps_vec.end();
1202  ++it)
1203  {
1204  fs::path msg_gen = fs::path((*it)->path_) /
1205  MSG_GEN_GENERATED_DIR /
1207  fs::path srv_gen = fs::path((*it)->path_) /
1208  SRV_GEN_GENERATED_DIR /
1210  if(fs::is_regular_file(msg_gen))
1211  gens.push_back(msg_gen.string());
1212  if(fs::is_regular_file(srv_gen))
1213  gens.push_back(srv_gen.string());
1214  }
1215  }
1216  catch(Exception& e)
1217  {
1218  logError(e.what());
1219  return false;
1220  }
1221  return true;
1222 }
1223 
1225 // Rosstackage methods (private)
1227 
1228 void
1229 Rosstackage::log(const std::string& level,
1230  const std::string& msg,
1231  bool append_errno)
1232 {
1233  if(quiet_)
1234  return;
1235  fprintf(stderr, "[%s] %s: %s",
1236  name_.c_str(), level.c_str(), msg.c_str());
1237  if(append_errno)
1238  fprintf(stderr, ": %s", strerror(errno));
1239  fprintf(stderr, "\n");
1240 }
1241 
1242 
1243 Stackage*
1244 Rosstackage::findWithRecrawl(const std::string& name)
1245 {
1246  if(stackages_.count(name))
1247  return stackages_[name];
1248  else
1249  {
1250  // Try to recrawl, in case we loaded from cache
1251  crawl(search_paths_, true);
1252  if(stackages_.count(name))
1253  return stackages_[name];
1254  }
1255 
1256  logError(get_manifest_type() + " '" + name + "' not found");
1257  return NULL;
1258 }
1259 
1260 bool
1261 Rosstackage::depsDetail(const std::string& name, bool direct,
1262  std::vector<Stackage*>& deps)
1263 {
1264  // No recrawl here, because we're in a recursive function. Rely on the
1265  // top level to do it.
1266  if(!stackages_.count(name))
1267  {
1268  logError(std::string("no such package ") + name);
1269  return false;
1270  }
1271  Stackage* stackage = stackages_[name];
1272  try
1273  {
1274  computeDeps(stackage);
1275  std::vector<Stackage*> deps_vec;
1276  gatherDeps(stackage, direct, POSTORDER, deps_vec);
1277  for(std::vector<Stackage*>::const_iterator it = deps_vec.begin();
1278  it != deps_vec.end();
1279  ++it)
1280  deps.push_back(*it);
1281  }
1282  catch(Exception& e)
1283  {
1284  logError(e.what());
1285  return false;
1286  }
1287  return true;
1288 }
1289 
1290 void
1292  Stackage* to,
1293  std::list<std::list<Stackage*> >& acc_list)
1294 {
1295  computeDeps(from);
1296  for(std::vector<Stackage*>::const_iterator it = from->deps_.begin();
1297  it != from->deps_.end();
1298  ++it)
1299  {
1300  if((*it)->name_ == to->name_)
1301  {
1302  std::list<Stackage*> acc;
1303  acc.push_back(from);
1304  acc.push_back(to);
1305  acc_list.push_back(acc);
1306  }
1307  else
1308  {
1309  std::list<std::list<Stackage*> > l;
1310  depsWhyDetail(*it, to, l);
1311  for(std::list<std::list<Stackage*> >::iterator iit = l.begin();
1312  iit != l.end();
1313  ++iit)
1314  {
1315  iit->push_front(from);
1316  acc_list.push_back(*iit);
1317  }
1318  }
1319  }
1320 }
1321 
1322 bool
1323 Rosstackage::depsOnDetail(const std::string& name, bool direct,
1324  std::vector<Stackage*>& deps, bool ignore_missing)
1325 {
1326  // No recrawl here, because depends-on always forces a crawl at the
1327  // start.
1328  if(!stackages_.count(name))
1329  {
1330  logError(std::string("no such package ") + name);
1331  return false;
1332  }
1333  try
1334  {
1335 #if BOOST_VERSION < 106500
1336  for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
1337 #else
1338  for(boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
1339 #endif
1340  it != stackages_.end();
1341  ++it)
1342  {
1343  computeDeps(it->second, true, ignore_missing);
1344  std::vector<Stackage*> deps_vec;
1345  gatherDeps(it->second, direct, POSTORDER, deps_vec);
1346  for(std::vector<Stackage*>::const_iterator iit = deps_vec.begin();
1347  iit != deps_vec.end();
1348  ++iit)
1349  {
1350  if((*iit)->name_ == name)
1351  {
1352  deps.push_back(it->second);
1353  break;
1354  }
1355  }
1356  }
1357  }
1358  catch(Exception& e)
1359  {
1360  logError(e.what());
1361  return false;
1362  }
1363  return true;
1364 }
1365 
1366 bool
1367 Rosstackage::profile(const std::vector<std::string>& search_path,
1368  bool zombie_only,
1369  int length,
1370  std::vector<std::string>& dirs)
1371 {
1372  double start = time_since_epoch();
1373  std::vector<DirectoryCrawlRecord*> dcrs;
1374 #if BOOST_VERSION < 106500
1375  std::tr1::unordered_set<std::string> dcrs_hash;
1376 #else
1377  boost::unordered_set<std::string> dcrs_hash;
1378 #endif
1379  for(std::vector<std::string>::const_iterator p = search_path.begin();
1380  p != search_path.end();
1381  ++p)
1382  {
1383  crawlDetail(*p, true, 1, true, dcrs, dcrs_hash);
1384  }
1385  if(!zombie_only)
1386  {
1387  double total = time_since_epoch() - start;
1388  char buf[16];
1389  snprintf(buf, sizeof(buf), "%.6f", total);
1390  dirs.push_back(std::string("Full tree crawl took ") + buf + " seconds.");
1391  dirs.push_back("Directories marked with (*) contain no manifest. You may");
1392  dirs.push_back("want to delete these directories.");
1393  dirs.push_back("To get just of list of directories without manifests,");
1394  dirs.push_back("re-run the profile with --zombie-only");
1395  dirs.push_back("-------------------------------------------------------------");
1396  }
1397  std::sort(dcrs.begin(), dcrs.end(), cmpDirectoryCrawlRecord);
1398  std::reverse(dcrs.begin(), dcrs.end());
1399  int i=0;
1400  for(std::vector<DirectoryCrawlRecord*>::const_iterator it = dcrs.begin();
1401  it != dcrs.end();
1402  ++it)
1403  {
1404  if(zombie_only)
1405  {
1406  if((*it)->zombie_)
1407  {
1408  if(length < 0 || i < length)
1409  dirs.push_back((*it)->path_);
1410  i++;
1411  }
1412  }
1413  else
1414  {
1415  char buf[16];
1416  snprintf(buf, sizeof(buf), "%.6f", (*it)->crawl_time_);
1417  if(length < 0 || i < length)
1418  dirs.push_back(std::string(buf) + " " +
1419  ((*it)->zombie_ ? "* " : " ") +
1420  (*it)->path_);
1421  i++;
1422  }
1423  delete *it;
1424  }
1425 
1426  writeCache();
1427  return 0;
1428 }
1429 
1430 void
1431 Rosstackage::addStackage(const std::string& path)
1432 {
1433 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
1434  std::string name = fs::path(path).filename();
1435 #else
1436  // in boostfs3, filename() returns a path, which needs to be stringified
1437  std::string name = fs::path(path).filename().string();
1438 #endif
1439 
1440  Stackage* stackage = 0;
1441  fs::path dry_manifest_path = fs::path(path) / manifest_name_;
1442  fs::path wet_manifest_path = fs::path(path) / ROSPACKAGE_MANIFEST_NAME;
1443  if(fs::is_regular_file(dry_manifest_path))
1444  {
1445  stackage = new Stackage(name, path, dry_manifest_path.string(), manifest_name_);
1446  }
1447  else if(fs::is_regular_file(wet_manifest_path))
1448  {
1449  stackage = new Stackage(name, path, wet_manifest_path.string(), ROSPACKAGE_MANIFEST_NAME);
1450  loadManifest(stackage);
1451  stackage->update_wet_information();
1452  }
1453  else
1454  {
1455  return;
1456  }
1457 
1458  // skip the stackage if it is not of correct type
1459  if((manifest_name_ == ROSSTACK_MANIFEST_NAME && stackage->isPackage()) ||
1460  (manifest_name_ == ROSPACK_MANIFEST_NAME && stackage->isStack()))
1461  {
1462  delete stackage;
1463  return;
1464  }
1465 
1466  if(stackages_.find(stackage->name_) != stackages_.end())
1467  {
1468  if (dups_.find(stackage->name_) == dups_.end())
1469  {
1470  std::vector<std::string> dups;
1471  dups.push_back(stackages_[stackage->name_]->path_);
1472  dups_[stackage->name_] = dups;
1473  }
1474  dups_[stackage->name_].push_back(stackage->path_);
1475  delete stackage;
1476  return;
1477  }
1478 
1479  stackages_[stackage->name_] = stackage;
1480 }
1481 
1482 void
1483 Rosstackage::crawlDetail(const std::string& path,
1484  bool force,
1485  int depth,
1486  bool collect_profile_data,
1487  std::vector<DirectoryCrawlRecord*>& profile_data,
1488 #if BOOST_VERSION < 106500
1489  std::tr1::unordered_set<std::string>& profile_hash)
1490 #else
1491  boost::unordered_set<std::string>& profile_hash)
1492 #endif
1493 {
1494  if(depth > MAX_CRAWL_DEPTH)
1495  throw Exception("maximum depth exceeded during crawl");
1496 
1497  try
1498  {
1499  if(!fs::is_directory(path))
1500  return;
1501  }
1502  catch(fs::filesystem_error& e)
1503  {
1504  logWarn(std::string("error while looking at ") + path + ": " + e.what());
1505  return;
1506  }
1507 
1508  fs::path catkin_ignore = fs::path(path) / CATKIN_IGNORE;
1509  try
1510  {
1511  if(fs::is_regular_file(catkin_ignore))
1512  return;
1513  }
1514  catch(fs::filesystem_error& e)
1515  {
1516  logWarn(std::string("error while looking for ") + catkin_ignore.string() + ": " + e.what());
1517  }
1518 
1519  if(isStackage(path))
1520  {
1521  addStackage(path);
1522  return;
1523  }
1524 
1525  fs::path nosubdirs = fs::path(path) / ROSPACK_NOSUBDIRS;
1526  try
1527  {
1528  if(fs::is_regular_file(nosubdirs))
1529  return;
1530  }
1531  catch(fs::filesystem_error& e)
1532  {
1533  logWarn(std::string("error while looking for ") + nosubdirs.string() + ": " + e.what());
1534  }
1535 
1536  // We've already checked above whether CWD contains the kind of manifest
1537  // we're looking for. Don't recurse if we encounter a rospack manifest,
1538  // to avoid having rosstack finding stacks inside packages, #3816.
1539  fs::path rospack_manifest = fs::path(path) / ROSPACK_MANIFEST_NAME;
1540  try
1541  {
1542  if(fs::is_regular_file(rospack_manifest))
1543  return;
1544  }
1545  catch(fs::filesystem_error& e)
1546  {
1547  logWarn(std::string("error while looking for ") + rospack_manifest.string() + ": " + e.what());
1548  }
1549 
1550  DirectoryCrawlRecord* dcr = NULL;
1551  if(collect_profile_data)
1552  {
1553  if(profile_hash.find(path) == profile_hash.end())
1554  {
1555  dcr = new DirectoryCrawlRecord(path,
1556  time_since_epoch(),
1557  stackages_.size());
1558  profile_data.push_back(dcr);
1559  profile_hash.insert(path);
1560  }
1561  }
1562 
1563  try
1564  {
1565  for(fs::directory_iterator dit = fs::directory_iterator(path);
1566  dit != fs::directory_iterator();
1567  ++dit)
1568  {
1569  if(fs::is_directory(dit->path()))
1570  {
1571 #if !defined(BOOST_FILESYSTEM_VERSION) || (BOOST_FILESYSTEM_VERSION == 2)
1572  std::string name = dit->path().filename();
1573 #else
1574  // in boostfs3, filename() returns a path, which needs to be stringified
1575  std::string name = dit->path().filename().string();
1576 #endif
1577  // Ignore directories starting with '.'
1578  if(name.size() == 0 || name[0] == '.')
1579  continue;
1580 
1581  crawlDetail(dit->path().string(), force, depth+1,
1582  collect_profile_data, profile_data, profile_hash);
1583  }
1584  }
1585  }
1586  catch(fs::filesystem_error& e)
1587  {
1588  logWarn(std::string("error while crawling ") + path + ": " + e.what());
1589  }
1590 
1591  if(collect_profile_data && dcr != NULL)
1592  {
1593  // Measure the elapsed time
1594  dcr->crawl_time_ = time_since_epoch() - dcr->start_time_;
1595  // If the number of packages didn't change while crawling,
1596  // then this directory is a zombie
1597  if(stackages_.size() == dcr->start_num_pkgs_)
1598  dcr->zombie_ = true;
1599  }
1600 }
1601 
1602 void
1604 {
1605  if(stackage->manifest_loaded_)
1606  return;
1607 
1608  if(!stackage->manifest_.LoadFile(stackage->manifest_path_))
1609  {
1610  std::string errmsg = std::string("error parsing manifest of package ") +
1611  stackage->name_ + " at " + stackage->manifest_path_;
1612  throw Exception(errmsg);
1613  }
1614  stackage->manifest_loaded_ = true;
1615 }
1616 
1617 void
1618 Rosstackage::computeDeps(Stackage* stackage, bool ignore_errors, bool ignore_missing)
1619 {
1620  if(stackage->deps_computed_)
1621  return;
1622 
1623  stackage->deps_computed_ = true;
1624 
1625  try
1626  {
1627  loadManifest(stackage);
1628  get_manifest_root(stackage);
1629  }
1630  catch(Exception& e)
1631  {
1632  if(ignore_errors)
1633  return;
1634  else
1635  throw e;
1636  }
1637  if (!stackage->is_wet_package_)
1638  {
1639  computeDepsInternal(stackage, ignore_errors, "depend", ignore_missing);
1640  }
1641  else
1642  {
1643  // package format 1 tags
1644  computeDepsInternal(stackage, ignore_errors, "run_depend", ignore_missing);
1645  // package format 2 tags
1646  computeDepsInternal(stackage, ignore_errors, "exec_depend", ignore_missing);
1647  computeDepsInternal(stackage, ignore_errors, "depend", ignore_missing);
1648  }
1649 }
1650 
1651 void
1652 Rosstackage::computeDepsInternal(Stackage* stackage, bool ignore_errors, const std::string& depend_tag, bool ignore_missing)
1653 {
1654  TiXmlElement* root;
1655  root = get_manifest_root(stackage);
1656 
1657  TiXmlNode *dep_node = NULL;
1658  const char* dep_pkgname;
1659  while((dep_node = root->IterateChildren(depend_tag, dep_node)))
1660  {
1661  TiXmlElement *dep_ele = dep_node->ToElement();
1662  if (!stackage->is_wet_package_)
1663  {
1664  dep_pkgname = dep_ele->Attribute(tag_.c_str());
1665  }
1666  else
1667  {
1668  dep_pkgname = dep_ele->GetText();
1669  }
1670  if(!dep_pkgname)
1671  {
1672  if(!ignore_errors)
1673  {
1674  std::string errmsg = std::string("bad depend syntax (no 'package/stack' attribute) in manifest ") + stackage->name_ + " at " + stackage->manifest_path_;
1675  throw Exception(errmsg);
1676  }
1677  }
1678  else if(dep_pkgname == stackage->name_)
1679  {
1680  if(!ignore_errors)
1681  {
1682  std::string errmsg = get_manifest_type() + " '" + stackage->name_ + "' depends on itself";
1683  throw Exception(errmsg);
1684  }
1685  }
1686  else if(!stackages_.count(dep_pkgname))
1687  {
1688  if (stackage->is_wet_package_ && (ignore_missing || isSysPackage(dep_pkgname)))
1689  {
1690  continue;
1691  }
1692  if(ignore_errors)
1693  {
1694  Stackage* dep = new Stackage(dep_pkgname, "", "", "");
1695  stackage->deps_.push_back(dep);
1696  }
1697  else
1698  {
1699  std::string errmsg = get_manifest_type() + " '" + stackage->name_ + "' depends on non-existent package '" + dep_pkgname + "' and rosdep claims that it is not a system dependency. Check the ROS_PACKAGE_PATH or try calling 'rosdep update'";
1700  throw Exception(errmsg);
1701  }
1702  }
1703  else
1704  {
1705  Stackage* dep = stackages_[dep_pkgname];
1706  if (std::find(stackage->deps_.begin(), stackage->deps_.end(), dep) == stackage->deps_.end())
1707  {
1708  stackage->deps_.push_back(dep);
1709  computeDeps(dep, ignore_errors, ignore_missing);
1710  }
1711  }
1712  }
1713 }
1714 
1715 void
1717 {
1718  static bool initialized = false;
1719  if(!initialized)
1720  {
1721  initialized = true;
1722  Py_InitializeEx(0);
1723  }
1724 }
1725 
1726 bool
1727 Rosstackage::isSysPackage(const std::string& pkgname)
1728 {
1729  static std::map<std::string, bool> cache;
1730  if(cache.find(pkgname) != cache.end())
1731  {
1732  return cache.find(pkgname)->second;
1733  }
1734 
1735  initPython();
1736  PyGILState_STATE gstate = PyGILState_Ensure();
1737 
1738  static PyObject* pModule = 0;
1739  static PyObject* pDict = 0;
1740  if(!pModule)
1741  {
1742  PyObject* pName = PyUnicode_FromString("rosdep2.rospack");
1743  pModule = PyImport_Import(pName);
1744  Py_DECREF(pName);
1745  if(!pModule)
1746  {
1747  PyErr_Print();
1748  PyGILState_Release(gstate);
1749  std::string errmsg = "could not find python module 'rosdep2.rospack'. is rosdep up-to-date (at least 0.10.4)?";
1750  throw Exception(errmsg);
1751  }
1752  pDict = PyModule_GetDict(pModule);
1753  }
1754 
1755  static PyObject* pView = 0;
1756  if(!pView)
1757  {
1758  PyObject* pFunc = PyDict_GetItemString(pDict, "init_rospack_interface");
1759  if(!PyCallable_Check(pFunc))
1760  {
1761  PyErr_Print();
1762  PyGILState_Release(gstate);
1763  std::string errmsg = "could not find python function 'rosdep2.rospack.init_rospack_interface'. is rosdep up-to-date (at least 0.10.4)?";
1764  throw Exception(errmsg);
1765  }
1766  pView = PyObject_CallObject(pFunc, NULL);
1767  if(!pView)
1768  {
1769  PyErr_Print();
1770  PyGILState_Release(gstate);
1771  std::string errmsg = "could not call python function 'rosdep2.rospack.init_rospack_interface'";
1772  throw Exception(errmsg);
1773  }
1774  }
1775  static bool rospack_view_not_empty = false;
1776  if(!rospack_view_not_empty)
1777  {
1778  PyObject* pFunc = PyDict_GetItemString(pDict, "is_view_empty");
1779  if(!PyCallable_Check(pFunc))
1780  {
1781  PyErr_Print();
1782  PyGILState_Release(gstate);
1783  std::string errmsg = "could not find python function 'rosdep2.rospack.is_view_empty'. is rosdep up-to-date (at least 0.10.8)?";
1784  throw Exception(errmsg);
1785  }
1786  PyObject* pArgs = PyTuple_New(1);
1787  PyTuple_SetItem(pArgs, 0, pView);
1788  PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
1789  Py_INCREF(pView); // in order to keep the view when garbaging pArgs
1790  Py_DECREF(pArgs);
1791  if(PyObject_IsTrue(pValue))
1792  {
1793  PyErr_Print();
1794  PyGILState_Release(gstate);
1795  std::string errmsg = "the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'";
1796  throw Exception(errmsg);
1797  }
1798  rospack_view_not_empty = true;
1799  }
1800 
1801  PyObject* pFunc = PyDict_GetItemString(pDict, "is_system_dependency");
1802  if(!PyCallable_Check(pFunc))
1803  {
1804  PyErr_Print();
1805  PyGILState_Release(gstate);
1806  std::string errmsg = "could not call python function 'rosdep2.rospack.is_system_dependency'. is rosdep up-to-date (at least 0.10.4)?";
1807  throw Exception(errmsg);
1808  }
1809 
1810  PyObject* pArgs = PyTuple_New(2);
1811  PyTuple_SetItem(pArgs, 0, pView);
1812  PyObject* pDep = PyUnicode_FromString(pkgname.c_str());
1813  PyTuple_SetItem(pArgs, 1, pDep);
1814  PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
1815  Py_INCREF(pView); // in order to keep the view when garbaging pArgs
1816  Py_DECREF(pArgs);
1817 
1818  bool value = PyObject_IsTrue(pValue);
1819  Py_DECREF(pValue);
1820 
1821  // we want to keep the static objects alive for repeated access
1822  // so skip all garbage collection until process ends
1823  //Py_DECREF(pView);
1824  //Py_DECREF(pDict);
1825  //Py_DECREF(pModule);
1826  //Py_Finalize();
1827 
1828  PyGILState_Release(gstate);
1829 
1830  cache[pkgname] = value;
1831 
1832  return value;
1833 }
1834 
1835 void
1836 Rosstackage::gatherDeps(Stackage* stackage, bool direct,
1837  traversal_order_t order,
1838  std::vector<Stackage*>& deps,
1839  bool no_recursion_on_wet)
1840 {
1841 #if BOOST_VERSION < 106500
1842  std::tr1::unordered_set<Stackage*> deps_hash;
1843 #else
1844  boost::unordered_set<Stackage*> deps_hash;
1845 #endif
1846  std::vector<std::string> indented_deps;
1847  gatherDepsFull(stackage, direct, order, 0,
1848  deps_hash, deps, false, indented_deps, no_recursion_on_wet);
1849 }
1850 
1851 void
1852 _gatherDepsFull(Stackage* stackage, bool direct,
1853  traversal_order_t order, int depth,
1854 #if BOOST_VERSION < 106500
1855  std::tr1::unordered_set<Stackage*>& deps_hash,
1856 #else
1857  boost::unordered_set<Stackage*>& deps_hash,
1858 #endif
1859  std::vector<Stackage*>& deps,
1860  bool get_indented_deps,
1861  std::vector<std::string>& indented_deps,
1862  bool no_recursion_on_wet,
1863  std::vector<std::string>& dep_chain)
1864 {
1865  if(stackage->is_wet_package_ && no_recursion_on_wet)
1866  {
1867  return;
1868  }
1869 
1870  if(direct && (stackage->is_wet_package_ || !no_recursion_on_wet))
1871  {
1872  for(std::vector<Stackage*>::const_iterator it = stackage->deps_.begin();
1873  it != stackage->deps_.end();
1874  ++it)
1875  deps.push_back(*it);
1876  return;
1877  }
1878 
1879  if(depth > MAX_DEPENDENCY_DEPTH) {
1880  std::string cycle;
1881  for(std::vector<std::string>::const_iterator it = dep_chain.begin();
1882  it != dep_chain.end();
1883  ++it)
1884  {
1885  std::vector<std::string>::const_iterator begin = dep_chain.begin();
1886  std::vector<std::string>::const_iterator cycle_begin = std::find(begin, it, *it);
1887  if(cycle_begin != it) {
1888  cycle = ": ";
1889  for(std::vector<std::string>::const_iterator jt = cycle_begin; jt != it; ++jt) {
1890  if(jt != cycle_begin) cycle += ", ";
1891  cycle += *jt;
1892  }
1893  break;
1894  }
1895  }
1896  throw Exception(std::string("maximum dependency depth exceeded (likely circular dependency") + cycle + ")");
1897  }
1898 
1899  for(std::vector<Stackage*>::const_iterator it = stackage->deps_.begin();
1900  it != stackage->deps_.end();
1901  ++it)
1902  {
1903  if(get_indented_deps)
1904  {
1905  std::string indented_dep;
1906  for(int i=0; i<depth; i++)
1907  indented_dep.append(" ");
1908  indented_dep.append((*it)->name_);
1909  indented_deps.push_back(indented_dep);
1910  }
1911 
1912  bool first = (deps_hash.find(*it) == deps_hash.end());
1913  if(first)
1914  {
1915  deps_hash.insert(*it);
1916  // We maintain the vector because the original rospack guaranteed
1917  // ordering in dep reporting.
1918  if(order == PREORDER)
1919  deps.push_back(*it);
1920  }
1921  if(!(*it)->is_wet_package_ || !no_recursion_on_wet)
1922  {
1923  // We always descend, even if we're encountering this stackage for the
1924  // nth time, so that we'll throw an error on recursive dependencies
1925  // (detected via max stack depth being exceeded).
1926  dep_chain.push_back((*it)->name_);
1927  _gatherDepsFull(*it, direct, order, depth+1, deps_hash, deps,
1928  get_indented_deps, indented_deps,
1929  no_recursion_on_wet, dep_chain);
1930  dep_chain.pop_back();
1931  }
1932  if(first)
1933  {
1934  if(order == POSTORDER)
1935  deps.push_back(*it);
1936  }
1937  }
1938 }
1939 
1940 // Pre-condition: computeDeps(stackage) succeeded
1941 void
1942 Rosstackage::gatherDepsFull(Stackage* stackage, bool direct,
1943  traversal_order_t order, int depth,
1944 #if BOOST_VERSION < 106500
1945  std::tr1::unordered_set<Stackage*>& deps_hash,
1946 #else
1947  boost::unordered_set<Stackage*>& deps_hash,
1948 #endif
1949  std::vector<Stackage*>& deps,
1950  bool get_indented_deps,
1951  std::vector<std::string>& indented_deps,
1952  bool no_recursion_on_wet)
1953 {
1954  std::vector<std::string> dep_chain;
1955  dep_chain.push_back(stackage->name_);
1956  _gatherDepsFull(stackage, direct,
1957  order, depth,
1958  deps_hash,
1959  deps,
1960  get_indented_deps,
1961  indented_deps,
1962  no_recursion_on_wet,
1963  dep_chain);
1964 }
1965 
1966 std::string
1968 {
1969  fs::path cache_path;
1970 
1971  char* ros_home = getenv("ROS_HOME");
1972  if(ros_home)
1973  cache_path = ros_home;
1974  else
1975  {
1976  // Get the user's home directory by looking up the password entry based
1977  // on UID. If that doesn't work, we fall back on examining $HOME,
1978  // knowing that that can cause trouble when mixed with sudo (#2884).
1979 #if defined(WIN32)
1980  char* home_drive = getenv("HOMEDRIVE");
1981  char* home_path = getenv("HOMEPATH");
1982  if(home_drive && home_path)
1983  cache_path = fs::path(home_drive) / fs::path(home_path) / fs::path(DOTROS_NAME);
1984 #else // UNIX
1985  char* home_path;
1986  struct passwd* passwd_ent;
1987  // Look up based on effective UID, just in case we got here by set-uid
1988  if((passwd_ent = getpwuid(geteuid())))
1989  home_path = passwd_ent->pw_dir;
1990  else
1991  home_path = getenv("HOME");
1992  if(home_path)
1993  cache_path = fs::path(home_path) / fs::path(DOTROS_NAME);
1994 #endif
1995  }
1996 
1997  // If it doesn't exist, create the directory that will hold the cache
1998  try
1999  {
2000  if(!fs::is_directory(cache_path))
2001  {
2002  fs::create_directory(cache_path);
2003  }
2004  }
2005  catch(fs::filesystem_error& e)
2006  {
2007  logWarn(std::string("cannot create rospack cache directory ") +
2008  cache_path.string() + ": " + e.what());
2009  }
2010  cache_path /= cache_prefix_ + "_" + getCacheHash();
2011  return cache_path.string();
2012 }
2013 
2014 std::string
2016 {
2017  size_t value = 0;
2018  char* rpp = getenv("ROS_PACKAGE_PATH");
2019  if(rpp != NULL) {
2020  boost::hash<std::string> hash_func;
2021  value = hash_func(rpp);
2022  }
2023  char buffer[21];
2024  snprintf(buffer, 21, "%020lu", value);
2025  return buffer;
2026 }
2027 
2028 bool
2030 {
2031  FILE* cache = validateCache();
2032  if(cache)
2033  {
2034  // We're about to read from the cache, so clear internal storage (in case this is
2035  // the second run in this process).
2036  clearStackages();
2037  char linebuf[30000];
2038  for(;;)
2039  {
2040  if (!fgets(linebuf, sizeof(linebuf), cache))
2041  break; // error in read operation
2042  if (linebuf[0] == '#')
2043  continue;
2044  char* newline_pos = strchr(linebuf, '\n');
2045  if(newline_pos)
2046  *newline_pos = 0;
2047  addStackage(linebuf);
2048  }
2049  fclose(cache);
2050  return true;
2051  }
2052  else
2053  return false;
2054 }
2055 
2056 // TODO: replace the contents of the method with some fancy cross-platform
2057 // boost thing.
2058 void
2060 {
2061  // Write the results of this crawl to the cache file. At each step, give
2062  // up on error, printing a warning to stderr.
2063  std::string cache_path = getCachePath();
2064  if(!cache_path.size())
2065  {
2066  logWarn("no location available to write cache file. Try setting ROS_HOME or HOME.");
2067  }
2068  else
2069  {
2070  size_t len = cache_path.size() + 1;
2071  char *tmp_cache_dir = new char[len];
2072  strncpy(tmp_cache_dir, cache_path.c_str(), len);
2073 #if defined(_MSC_VER)
2074  // No dirname on Windows; use _splitpath_s instead
2075  char tmp_cache_path[PATH_MAX];
2076  char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
2077  _splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
2078  ext, _MAX_EXT);
2079  char full_dir[_MAX_DRIVE + _MAX_DIR];
2080  _makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
2081  snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rospack_cache.XXXXXX", full_dir);
2082 #elif defined(__MINGW32__)
2083  char tmp_cache_path[PATH_MAX];
2084  char* temp_name = tempnam(dirname(tmp_cache_dir),".rospack_cache.");
2085  snprintf(tmp_cache_path, sizeof(tmp_cache_path), temp_name);
2086  delete temp_name;
2087 #else
2088  char *temp_dirname = dirname(tmp_cache_dir);
2089  len = strlen(temp_dirname) + 22 + 1;
2090  char *tmp_cache_path = new char[len];
2091  snprintf(tmp_cache_path, len, "%s/.rospack_cache.XXXXXX", temp_dirname);
2092 #endif
2093 #if defined(__MINGW32__)
2094  // There is no equivalent of mkstemp or _mktemp_s on mingw, so we resort to a slightly problematic
2095  // tempnam (above) and mktemp method. This has the miniscule chance of a race condition.
2096  int fd = open(tmp_cache_path, O_RDWR | O_EXCL | _O_CREAT, 0644);
2097  if (fd < 0)
2098  {
2099  logWarn(std::string("unable to create temporary cache file ") +
2100  tmp_cache_path, true);
2101  }
2102  else
2103  {
2104  FILE *cache = fdopen(fd, "w");
2105 #elif defined(WIN32)
2106  if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
2107  {
2108  fprintf(stderr,
2109  "[rospack] Unable to generate temporary cache file name: %u",
2110  GetLastError());
2111  }
2112  else
2113  {
2114  FILE *cache = fopen(tmp_cache_path, "w");
2115 #else
2116  int fd = mkstemp(tmp_cache_path);
2117  if (fd < 0)
2118  {
2119  fprintf(stderr, "[rospack] Unable to create temporary cache file %s: %s\n",
2120  tmp_cache_path, strerror(errno));
2121  }
2122  else
2123  {
2124  FILE *cache = fdopen(fd, "w");
2125 #endif
2126  if (!cache)
2127  {
2128  fprintf(stderr, "[rospack] Unable open cache file %s: %s\n",
2129  tmp_cache_path, strerror(errno));
2130  }
2131  else
2132  {
2133  char *rpp = getenv("ROS_PACKAGE_PATH");
2134  fprintf(cache, "#ROS_PACKAGE_PATH=%s\n", (rpp ? rpp : ""));
2135 #if BOOST_VERSION < 106500
2136  for(std::tr1::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
2137 #else
2138  for(boost::unordered_map<std::string, Stackage*>::const_iterator it = stackages_.begin();
2139 #endif
2140  it != stackages_.end();
2141  ++it)
2142  fprintf(cache, "%s\n", it->second->path_.c_str());
2143  fclose(cache);
2144  if(fs::exists(cache_path))
2145  remove(cache_path.c_str());
2146  if(rename(tmp_cache_path, cache_path.c_str()) < 0)
2147  {
2148  fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n",
2149  tmp_cache_path, cache_path.c_str(), strerror(errno));
2150  }
2151  }
2152  }
2153  delete[] tmp_cache_dir;
2154 #if !defined(_MSC_VER) && !defined(__MINGW32__)
2155  delete[] tmp_cache_path;
2156 #endif
2157  }
2158 }
2159 
2160 FILE*
2162 {
2163  std::string cache_path = getCachePath();
2164  // first see if it's new enough
2165  double cache_max_age = DEFAULT_MAX_CACHE_AGE;
2166  const char *user_cache_time_str = getenv("ROS_CACHE_TIMEOUT");
2167  if(user_cache_time_str)
2168  cache_max_age = atof(user_cache_time_str);
2169  if(cache_max_age == 0.0)
2170  return NULL;
2171  struct stat s;
2172  if(stat(cache_path.c_str(), &s) == 0)
2173  {
2174  double dt = difftime(time(NULL), s.st_mtime);
2175  // Negative cache_max_age means it's always new enough. It's dangerous
2176  // for the user to set this, but rosbash uses it.
2177  if ((cache_max_age > 0.0) && (dt > cache_max_age))
2178  return NULL;
2179  }
2180  // try to open it
2181  FILE* cache = fopen(cache_path.c_str(), "r");
2182  if(!cache)
2183  return NULL; // it's not readable by us. sad.
2184 
2185  // see if ROS_PACKAGE_PATH matches
2186  char linebuf[30000];
2187  bool ros_package_path_ok = false;
2188  const char* ros_package_path = getenv("ROS_PACKAGE_PATH");
2189  for(;;)
2190  {
2191  if(!fgets(linebuf, sizeof(linebuf), cache))
2192  break;
2193  linebuf[strlen(linebuf)-1] = 0; // get rid of trailing newline
2194  if (linebuf[0] == '#')
2195  {
2196  if(!strncmp("#ROS_PACKAGE_PATH=", linebuf, 18))
2197  {
2198  if(!ros_package_path)
2199  {
2200  if(!strlen(linebuf+18))
2201  ros_package_path_ok = true;
2202  }
2203  else if(!strcmp(linebuf+18, ros_package_path))
2204  ros_package_path_ok = true;
2205  }
2206  }
2207  else
2208  break; // we're out of the header. nothing more matters to this check.
2209  }
2210  if(ros_package_path_ok)
2211  {
2212  // seek to the beginning and pass back the stream (instead of closing
2213  // and later reopening, which is a race condition, #1666)
2214  fseek(cache, 0, SEEK_SET);
2215  return cache;
2216  }
2217  else
2218  {
2219  fclose(cache);
2220  return NULL;
2221  }
2222 }
2223 
2224 bool
2226  const std::string& instring,
2227  std::string& outstring)
2228 {
2229  outstring = instring;
2230  for(std::string::size_type i = outstring.find(MANIFEST_PREFIX);
2231  i != std::string::npos;
2232  i = outstring.find(MANIFEST_PREFIX))
2233  {
2234  outstring.replace(i, std::string(MANIFEST_PREFIX).length(),
2235  stackage->path_);
2236  }
2237 
2238  // skip substitution attempt when the string neither contains
2239  // a dollar sign for $(command) and $envvar nor
2240  // a backtick wrapping a command
2241  if (outstring.find_first_of("$`") == std::string::npos)
2242  {
2243  return true;
2244  }
2245 
2246  // Do backquote substitution. E.g., if we find this string:
2247  // `pkg-config --cflags gdk-pixbuf-2.0`
2248  // We replace it with the result of executing the command
2249  // contained within the backquotes (reading from its stdout), which
2250  // might be something like:
2251  // -I/usr/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
2252 
2253  // Construct and execute the string
2254  // We do the assignment first to ensure that if backquote expansion (or
2255  // anything else) fails, we'll get a non-zero exit status from pclose().
2256  std::string cmd = std::string("ret=\"") + outstring + "\" && echo $ret";
2257 
2258  // Remove embedded newlines
2259  std::string token("\n");
2260  for (std::string::size_type s = cmd.find(token);
2261  s != std::string::npos;
2262  s = cmd.find(token, s))
2263  cmd.replace(s,token.length(),std::string(" "));
2264 
2265  FILE* p;
2266  if(!(p = popen(cmd.c_str(), "r")))
2267  {
2268  std::string errmsg =
2269  std::string("failed to execute backquote expression ") +
2270  cmd + " in " +
2271  stackage->manifest_path_;
2272  logWarn(errmsg, true);
2273  return false;
2274  }
2275  else
2276  {
2277  char buf[8192];
2278  memset(buf,0,sizeof(buf));
2279  // Read the command's output
2280  do
2281  {
2282  clearerr(p);
2283  while(fgets(buf + strlen(buf),sizeof(buf)-strlen(buf)-1,p));
2284  } while(ferror(p) && errno == EINTR);
2285  // Close the subprocess, checking exit status
2286  if(pclose(p) != 0)
2287  {
2288  std::string errmsg =
2289  std::string("got non-zero exit status from executing backquote expression ") +
2290  cmd + " in " +
2291  stackage->manifest_path_;
2292  return false;
2293  }
2294  else
2295  {
2296  // Strip trailing newline, which was added by our call to echo
2297  buf[strlen(buf)-1] = '\0';
2298  // Replace the backquote expression with the new text
2299  outstring = buf;
2300  }
2301  }
2302 
2303  return true;
2304 }
2305 
2307 // Rospack methods
2310  Rosstackage(ROSPACK_MANIFEST_NAME,
2311  ROSPACK_CACHE_PREFIX,
2312  ROSPACK_NAME,
2313  MANIFEST_TAG_PACKAGE)
2314 {
2315 }
2316 
2317 const char*
2319 {
2320  return "USAGE: rospack <command> [options] [package]\n"
2321  " Allowed commands:\n"
2322  " help\n"
2323  " cflags-only-I [--deps-only] [package]\n"
2324  " cflags-only-other [--deps-only] [package]\n"
2325  " depends [package] (alias: deps)\n"
2326  " depends-indent [package] (alias: deps-indent)\n"
2327  " depends-manifests [package] (alias: deps-manifests)\n"
2328  " depends-msgsrv [package] (alias: deps-msgsrv)\n"
2329  " depends-on [package]\n"
2330  " depends-on1 [package]\n"
2331  " depends-why --target=<target> [package] (alias: deps-why)\n"
2332  " depends1 [package] (alias: deps1)\n"
2333  " export [--deps-only] --lang=<lang> --attrib=<attrib> [package]\n"
2334  " find [package]\n"
2335  " langs\n"
2336  " libs-only-L [--deps-only] [package]\n"
2337  " libs-only-l [--deps-only] [package]\n"
2338  " libs-only-other [--deps-only] [package]\n"
2339  " list\n"
2340  " list-duplicates\n"
2341  " list-names\n"
2342  " plugins --attrib=<attrib> [--top=<toppkg>] [package]\n"
2343  " profile [--length=<length>] [--zombie-only]\n"
2344  " rosdep [package] (alias: rosdeps)\n"
2345  " rosdep0 [package] (alias: rosdeps0)\n"
2346  " vcs [package]\n"
2347  " vcs0 [package]\n"
2348  " Extra options:\n"
2349  " -q Quiets error reports.\n\n"
2350  " If [package] is omitted, the current working directory\n"
2351  " is used (if it contains a package.xml or manifest.xml).\n\n";
2352 }
2353 
2355 {
2356  return "package";
2357 }
2358 
2360 // Rosstack methods
2363  Rosstackage(ROSSTACK_MANIFEST_NAME,
2364  ROSSTACK_CACHE_PREFIX,
2365  ROSSTACK_NAME,
2366  MANIFEST_TAG_STACK)
2367 {
2368 }
2369 
2370 const char*
2372 {
2373  return "USAGE: rosstack [options] <command> [stack]\n"
2374  " Allowed commands:\n"
2375  " help\n"
2376  " find [stack]\n"
2377  " contents [stack]\n"
2378  " list\n"
2379  " list-names\n"
2380  " depends [stack] (alias: deps)\n"
2381  " depends-manifests [stack] (alias: deps-manifests)\n"
2382  " depends1 [stack] (alias: deps1)\n"
2383  " depends-indent [stack] (alias: deps-indent)\n"
2384  " depends-why --target=<target> [stack] (alias: deps-why)\n"
2385  " depends-on [stack]\n"
2386  " depends-on1 [stack]\n"
2387  " contains [package]\n"
2388  " contains-path [package]\n"
2389  " profile [--length=<length>] \n\n"
2390  " If [stack] is omitted, the current working directory\n"
2391  " is used (if it contains a stack.xml).\n\n";
2392 }
2393 
2395 {
2396  return "stack";
2397 }
2398 
2399 TiXmlElement*
2401 {
2402  TiXmlElement* ele = stackage->manifest_.RootElement();
2403  if(!ele)
2404  {
2405  std::string errmsg = std::string("error parsing manifest of package ") +
2406  stackage->name_ + " at " + stackage->manifest_path_;
2407  throw Exception(errmsg);
2408  }
2409  return ele;
2410 }
2411 
2412 double
2414 {
2415 #if defined(WIN32)
2416  #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
2417  #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
2418  #else
2419  #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
2420  #endif
2421  FILETIME ft;
2422  unsigned __int64 tmpres = 0;
2423 
2424  GetSystemTimeAsFileTime(&ft);
2425  tmpres |= ft.dwHighDateTime;
2426  tmpres <<= 32;
2427  tmpres |= ft.dwLowDateTime;
2428  tmpres /= 10;
2429  tmpres -= DELTA_EPOCH_IN_MICROSECS;
2430  return static_cast<double>(tmpres) / 1e6;
2431 #else
2432  struct timeval tod;
2433  gettimeofday(&tod, NULL);
2434  return tod.tv_sec + 1e-6 * tod.tv_usec;
2435 #endif
2436 }
2437 
2438 
2439 
2440 } // namespace rospack
bool exports(const std::string &name, const std::string &lang, const std::string &attrib, bool deps_only, std::vector< std::string > &flags)
Compute exports declared in a package and its dependencies. Used by rosbuild.
Definition: rospack.cpp:1014
virtual const char * usage()
Usage statement.
Definition: rospack.cpp:2318
void addStackage(const std::string &path)
Definition: rospack.cpp:1431
void gatherDeps(Stackage *stackage, bool direct, traversal_order_t order, std::vector< Stackage * > &deps, bool no_recursion_on_wet=false)
Definition: rospack.cpp:1836
void computeDeps(Stackage *stackage, bool ignore_errors=false, bool ignore_missing=false)
Definition: rospack.cpp:1618
static const char * DOTROS_NAME
Definition: rospack.cpp:102
bool depsMsgSrv(const std::string &name, bool direct, std::vector< std::string > &gens)
List the marker files in a packages&#39;s dependencies that indicate that those packages contain auto-gen...
Definition: rospack.cpp:1189
bool depsDetail(const std::string &name, bool direct, std::vector< Stackage * > &deps)
Compute dependencies of a stackage (i.e., stackages that this stackages depends on), taking and returning stackage objects..
Definition: rospack.cpp:1261
std::string path_
Definition: rospack.cpp:145
bool inStackage(std::string &name)
Is the current working directory a stackage?
Definition: rospack.cpp:405
Rospack()
Constructor.
Definition: rospack.cpp:2309
static const char * SRV_GEN_GENERATED_DIR
Definition: rospack.cpp:105
virtual ~Rosstackage()
Destructor.
Definition: rospack.cpp:254
#define PyBytes_AsString
defined(WIN32)
Definition: rospack.cpp:79
std::vector< std::string > search_paths_
Definition: rospack.h:204
std::string tag_
Definition: rospack.h:202
static const char * SRV_GEN_GENERATED_FILE
Definition: rospack.cpp:106
bool depsOnDetail(const std::string &name, bool direct, std::vector< Stackage * > &deps, bool ignore_missing=false)
Compute reverse dependencies of a stackage (i.e., stackages that depend on this stackage), taking and returning stackage objects. Forces crawl.
Definition: rospack.cpp:1323
static const char * ROSPACK_MANIFEST_NAME
Definition: rospack.cpp:95
traversal_order_t
Definition: rospack.h:180
static const char * MANIFEST_ATTR_NAME
Definition: rospack.cpp:110
bool isPackage() const
Definition: rospack.cpp:209
static const char * MANIFEST_TAG_EXPORT
Definition: rospack.cpp:109
static const char * MSG_GEN_GENERATED_FILE
Definition: rospack.cpp:104
bool contents(const std::string &name, std::set< std::string > &packages)
Compute the packages that are contained in a stack.
Definition: rospack.cpp:454
bool reorder_paths(const std::string &paths, std::string &reordered)
Reorder the paths according to the workspace chaining.
Definition: rospack.cpp:950
virtual std::string get_manifest_type()
Definition: rospack.cpp:2394
void listDuplicates(std::vector< std::string > &dups)
Identify duplicate stackages. Forces crawl.
Definition: rospack.cpp:538
void gatherDepsFull(Stackage *stackage, bool direct, traversal_order_t order, int depth, std::tr1::unordered_set< Stackage * > &deps_hash, std::vector< Stackage * > &deps, bool get_indented_deps, std::vector< std::string > &indented_deps, bool no_recursion_on_wet=false)
Definition: rospack.cpp:1942
void loadManifest(Stackage *stackage)
Definition: rospack.cpp:1603
bool isStackage(const std::string &path)
Definition: rospack.cpp:323
bool deps(const std::string &name, bool direct, std::vector< std::string > &deps)
Compute dependencies of a stackage (i.e., stackages that this stackages depends on).
Definition: rospack.cpp:580
static const double DEFAULT_MAX_CACHE_AGE
Definition: rospack.cpp:116
virtual std::string get_manifest_type()
Definition: rospack.h:620
void setQuiet(bool quiet)
Control warning and error console output.
Definition: rospack.cpp:317
std::string manifest_name_
Definition: rospack.h:198
static const char * ROSPACK_NOSUBDIRS
Definition: rospack.cpp:100
std::string name_
Definition: rospack.cpp:143
void depsWhyDetail(Stackage *from, Stackage *to, std::list< std::list< Stackage * > > &acc_list)
Definition: rospack.cpp:1291
#define PyUnicode_AsUTF8
Definition: rospack.cpp:80
static const char * ROSSTACK_MANIFEST_NAME
Definition: rospack.cpp:97
bool rosdeps(const std::string &name, bool direct, std::set< std::string > &rosdeps)
Compute rosdep entries that are declared in manifest of a package and its dependencies. Used by rosmake.
Definition: rospack.cpp:717
void _gatherDepsFull(Stackage *stackage, bool direct, traversal_order_t order, int depth, std::tr1::unordered_set< Stackage * > &deps_hash, std::vector< Stackage * > &deps, bool get_indented_deps, std::vector< std::string > &indented_deps, bool no_recursion_on_wet, std::vector< std::string > &dep_chain)
Definition: rospack.cpp:1852
std::string manifest_name_
Definition: rospack.cpp:149
DirectoryCrawlRecord(std::string path, double start_time, size_t start_num_pkgs)
Definition: rospack.cpp:224
virtual std::string get_manifest_type()
Definition: rospack.cpp:2354
bool isStack() const
Definition: rospack.cpp:204
static const int MAX_DEPENDENCY_DEPTH
Definition: rospack.cpp:115
std::tr1::unordered_map< std::string, std::vector< std::string > > dups_
Definition: rospack.h:206
void listDuplicatesWithPaths(std::map< std::string, std::vector< std::string > > &dups)
Identify duplicate stackages and provide their paths. Forces crawl.
Definition: rospack.cpp:556
void list(std::set< std::pair< std::string, std::string > > &list)
List names and paths of all stackages.
Definition: rospack.cpp:520
static const char * MANIFEST_TAG_PACKAGE
Definition: rospack.cpp:93
static const char * MANIFEST_TAG_ROSDEP
Definition: rospack.cpp:107
static const char * MANIFEST_ATTR_TYPE
Definition: rospack.cpp:111
static const char * CATKIN_IGNORE
Definition: rospack.cpp:101
std::string getCacheHash()
Definition: rospack.cpp:2015
std::string cache_prefix_
Definition: rospack.h:199
static const char * ROSPACK_CACHE_PREFIX
Definition: rospack.cpp:98
static const char * ROSPACKAGE_MANIFEST_NAME
Definition: rospack.cpp:96
TiXmlDocument manifest_
Definition: rospack.cpp:155
bool expandExportString(Stackage *stackage, const std::string &instring, std::string &outstring)
Definition: rospack.cpp:2225
std::string manifest_path_
Definition: rospack.cpp:147
bool cmpDirectoryCrawlRecord(DirectoryCrawlRecord *i, DirectoryCrawlRecord *j)
Definition: rospack.cpp:233
static const char * MANIFEST_TAG_STACK
Definition: rospack.cpp:94
bool contains(const std::string &name, std::string &stack, std::string &path)
Find the stack that contains a package.
Definition: rospack.cpp:484
static const char * ROSSTACK_CACHE_PREFIX
Definition: rospack.cpp:99
Exception(const std::string &what)
Definition: rospack.cpp:134
bool getSearchPathFromEnv(std::vector< std::string > &sp)
Helper method to construct a directory search path by looking at relevant environment variables...
Definition: rospack.cpp:289
TiXmlElement * get_manifest_root(Stackage *stackage)
Definition: rospack.cpp:2400
void log(const std::string &level, const std::string &msg, bool append_errno)
Definition: rospack.cpp:1229
bool depsManifests(const std::string &name, bool direct, std::vector< std::string > &manifests)
List the manifests of a stackage&#39;s dependencies. Used by rosbuild.
Definition: rospack.cpp:692
static const char * MANIFEST_ATTR_URL
Definition: rospack.cpp:112
Package crawler. Create one of these to operate on a package tree. Call public methods inherited from...
Definition: rospack.h:627
void _rosdeps(Stackage *stackage, std::set< std::string > &rosdeps, const char *tag_name)
Definition: rospack.cpp:764
static const std::string g_ros_os
Definition: rospack.cpp:127
std::vector< std::string > licenses_
Definition: rospack.cpp:151
bool depsOn(const std::string &name, bool direct, std::vector< std::string > &deps)
Compute reverse dependencies of a stackage (i.e., stackages that depend on this stackage). Forces crawl.
Definition: rospack.cpp:605
Stackage * findWithRecrawl(const std::string &name)
Definition: rospack.cpp:1244
void update_wet_information()
Definition: rospack.cpp:176
Rosstack()
Constructor.
Definition: rospack.cpp:2362
The base class for package/stack ("stackage") crawlers. Users of the library should use the functiona...
Definition: rospack.h:195
std::vector< Stackage * > deps_
Definition: rospack.cpp:156
virtual const char * usage()
Usage statement.
Definition: rospack.cpp:2371
static const int MAX_CRAWL_DEPTH
Definition: rospack.cpp:114
void crawl(std::vector< std::string > search_path, bool force)
Crawl the filesystem, accumulating a database of stackages. May read results from a cache file instea...
Definition: rospack.cpp:361
bool isSysPackage(const std::string &pkgname)
Definition: rospack.cpp:1727
bool vcs(const std::string &name, bool direct, std::vector< std::string > &vcs)
Compute vcs entries that are declared in manifest of a package and its dependencies. Was used by Hudson build scripts; might not be needed.
Definition: rospack.cpp:791
void logError(const std::string &msg, bool append_errno=false)
Log a error (usually goes to stderr).
Definition: rospack.cpp:282
static const char * ROSSTACK_NAME
Definition: utils.h:43
bool depsIndent(const std::string &name, bool direct, std::vector< std::string > &deps)
Generate indented list of a stackage&#39;s dependencies, including duplicates. Intended for visual debugg...
Definition: rospack.cpp:619
std::tr1::unordered_map< std::string, Stackage * > stackages_
Definition: rospack.h:207
bool cpp_exports(const std::string &name, const std::string &type, const std::string &attrib, bool deps_only, std::vector< std::pair< std::string, bool > > &flags)
Compute cpp exports declared in a package and its dependencies. Used by rosbuild. ...
Definition: rospack.cpp:839
bool profile(const std::vector< std::string > &search_path, bool zombie_only, int length, std::vector< std::string > &dirs)
Report on time taken to crawl for stackages. Intended for use in debugging misconfigured stackage tre...
Definition: rospack.cpp:1367
bool find(const std::string &name, std::string &path)
Look for a stackage.
Definition: rospack.cpp:441
std::string name_
Definition: rospack.h:201
Stackage(const std::string &name, const std::string &path, const std::string &manifest_path, const std::string &manifest_name)
Definition: rospack.cpp:161
static const char * ROSPACK_NAME
Definition: utils.h:42
double time_since_epoch()
Definition: rospack.cpp:2413
#define PyUnicode_FromString
Definition: rospack.cpp:81
std::string getCachePath()
Definition: rospack.cpp:1967
FILE * validateCache()
Definition: rospack.cpp:2161
bool plugins(const std::string &name, const std::string &attrib, const std::string &top, std::vector< std::string > &flags)
Compute exported plugins declared in packages that depend on a package. Forces crawl. Used by rosbuild and roslib.
Definition: rospack.cpp:1115
void computeDepsInternal(Stackage *stackage, bool ignore_errors, const std::string &depend_tag, bool ignore_missing=false)
Definition: rospack.cpp:1652
bool depsWhy(const std::string &from, const std::string &to, std::string &output)
Compute all dependency chains from one stackage to another. Intended for visual debugging of dependen...
Definition: rospack.cpp:650
static const char * MSG_GEN_GENERATED_DIR
Definition: rospack.cpp:103
static const char * MANIFEST_TAG_VERSIONCONTROL
Definition: rospack.cpp:108
void logWarn(const std::string &msg, bool append_errno=false)
Log a warning (usually goes to stderr).
Definition: rospack.cpp:276
Rosstackage(const std::string &manifest_name, const std::string &cache_prefix, const std::string &name, const std::string &tag)
Constructor, only used by derived classes.
Definition: rospack.cpp:242
void crawlDetail(const std::string &path, bool force, int depth, bool collect_profile_data, std::vector< DirectoryCrawlRecord * > &profile_data, std::tr1::unordered_set< std::string > &profile_hash)
Definition: rospack.cpp:1483
bool exports_dry_package(Stackage *stackage, const std::string &lang, const std::string &attrib, std::vector< std::string > &flags)
Compute exports declared in a dry package.
Definition: rospack.cpp:1047
static const char * MANIFEST_PREFIX
Definition: rospack.cpp:113


rospack
Author(s): Brian Gerkey, Morgan Quigley, Dirk Thomas
autogenerated on Thu Dec 10 2020 03:42:11