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


rospack
Author(s): Brian Gerkey, Morgan Quigley, Dirk Thomas
autogenerated on Tue Mar 5 2019 03:26:37