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


rospack
Author(s): Brian Gerkey, Morgan Quigley, Dirk Thomas
autogenerated on Mon Feb 28 2022 23:32:59