applied windows patch from #3274

This commit is contained in:
Brian Gerkey 2011-01-30 00:12:22 +00:00
parent 3624d67266
commit d353445182
6 changed files with 533 additions and 67 deletions

View File

@ -132,6 +132,18 @@ file called @b manifest.xml.
*/ */
#if defined(WIN32)
#if defined(ROS_STATIC)
#define ROSPACK_EXPORT
#elif defined(rospack_EXPORTS)
#define ROSPACK_EXPORT __declspec(dllexport)
#else
#define ROSPACK_EXPORT __declspec(dllimport)
#endif
#else
#define ROSPACK_EXPORT
#endif
#include <string> #include <string>
#include <vector> #include <vector>
#include <list> #include <list>
@ -155,11 +167,16 @@ typedef std::list<Acc> AccList;
/** /**
* The Package class contains information about a single package * The Package class contains information about a single package
*/ */
class Package class ROSPACK_EXPORT Package
{ {
public: public:
enum traversal_order_t { POSTORDER, PREORDER }; enum traversal_order_t { POSTORDER, PREORDER };
std::string name, path; std::string name, path;
// These will cause warnings on Windows when compiling the DLL because they
// are static. They should more correctly be accessed via accessor functions
// that are exported from the class, rather than directly, in order to
// "prevent data corruption." Since main.cpp is currently the only known
// client and it doesn't use them, I'm not caring about the warnings yet.
static std::vector<Package *> pkgs; static std::vector<Package *> pkgs;
static std::vector<Package *> deleted_pkgs; static std::vector<Package *> deleted_pkgs;
@ -205,7 +222,7 @@ private:
* The ROSPack class contains information the entire package dependency * The ROSPack class contains information the entire package dependency
* tree. * tree.
*/ */
class ROSPack class ROSPACK_EXPORT ROSPack
{ {
public: public:
static const char* usage(); static const char* usage();

View File

@ -40,6 +40,18 @@
a single binary, called \b %rosstack. a single binary, called \b %rosstack.
*/ */
#if defined(WIN32)
#if defined(ROS_STATIC)
#define ROSSTACK_EXPORT
#elif defined(rosstack_EXPORTS)
#define ROSSTACK_EXPORT __declspec(dllexport)
#else
#define ROSSTACK_EXPORT __declspec(dllimport)
#endif
#else
#define ROSSTACK_EXPORT
#endif
#include <string> #include <string>
#include <vector> #include <vector>
@ -49,18 +61,19 @@ a single binary, called \b %rosstack.
namespace rosstack namespace rosstack
{ {
class Stack; class ROSSTACK_EXPORT Stack;
// global helper functions // global helper functions
void string_split(const std::string &s, std::vector<std::string> &t, const std::string &d); void string_split(const std::string &s, std::vector<std::string> &t, const std::string &d);
bool file_exists(const std::string &fname); bool file_exists(const std::string &fname);
extern const char *fs_delim; extern const char *fs_delim;
extern const char *path_delim;
Stack *g_get_stack(const std::string &name); Stack *g_get_stack(const std::string &name);
typedef std::vector<Stack *> VecStack; typedef std::vector<Stack *> VecStack;
/** /**
* The Stack class contains information about a single stack * The Stack class contains information about a single stack
*/ */
class Stack class ROSSTACK_EXPORT Stack
{ {
public: public:
enum traversal_order_t { POSTORDER, PREORDER }; enum traversal_order_t { POSTORDER, PREORDER };
@ -95,7 +108,7 @@ private:
* The ROSStack class contains information the entire stack dependency * The ROSStack class contains information the entire stack dependency
* tree. * tree.
*/ */
class ROSStack class ROSSTACK_EXPORT ROSStack
{ {
public: public:
static const char* usage(); static const char* usage();

View File

@ -31,7 +31,9 @@
#include "rospack/rospack.h" #include "rospack/rospack.h"
#include <stdexcept> #include <stdexcept>
#include <unistd.h> #if !defined(WIN32)
#include <unistd.h>
#endif
#include <stdlib.h> #include <stdlib.h>
int main(int argc, char **argv) int main(int argc, char **argv)
@ -42,6 +44,7 @@ int main(int argc, char **argv)
return 0; return 0;
} }
#if !defined(WIN32)
// If it looks we're running under sudo, try to drop back to the normal // If it looks we're running under sudo, try to drop back to the normal
// user, to avoid writing the cache with inappropriate permissions, // user, to avoid writing the cache with inappropriate permissions,
// #2884. // #2884.
@ -60,6 +63,7 @@ int main(int argc, char **argv)
if(setuid(sudo_uid)) if(setuid(sudo_uid))
perror("[rospack] Failed to change UID; cache permissions may need to be adjusted manually. setuid()"); perror("[rospack] Failed to change UID; cache permissions may need to be adjusted manually. setuid()");
} }
#endif
int ret; int ret;
bool quiet; bool quiet;

View File

@ -37,16 +37,38 @@
#include <stack> #include <stack>
#include <queue> #include <queue>
#include <cassert> #include <cassert>
#include <unistd.h> #if !defined(WIN32)
#include <dirent.h> #include <unistd.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/file.h>
#endif
#include <stdexcept> #include <stdexcept>
#include <sys/time.h>
#include <sys/file.h>
#include <time.h> #include <time.h>
#include <sstream> #include <sstream>
#include <iterator> #include <iterator>
#include <libgen.h> #if !defined(WIN32)
#include <libgen.h>
#endif
#if defined(WIN32)
#include <direct.h>
#include <Winsock2.h> // For struct timeval (that's awful)
#include <time.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#define PATH_MAX MAX_PATH
#define snprintf _snprintf
#define popen _popen
#define pclose _pclose
#define getcwd _getcwd
#define mkdir(a,b) _mkdir(a)
#define fdopen _fdopen
#define access _access
#define F_OK 0x00
#endif
#include "tinyxml-2.5.3/tinyxml.h" #include "tinyxml-2.5.3/tinyxml.h"
#include "rospack/rospack.h" #include "rospack/rospack.h"
@ -60,7 +82,11 @@ const int MAX_DIRECTORY_DEPTH = 1000; // used to detect self-referencing symlink
#include <sys/stat.h> #include <sys/stat.h>
#ifndef S_ISDIR #ifndef S_ISDIR
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) #if defined(WIN32)
#define S_ISDIR(x) (((x) & FILE_ATTRIBUTE_DIRECTORY) != 0)
#else
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
#endif #endif
namespace rospack namespace rospack
@ -69,12 +95,41 @@ namespace rospack
ROSPack *g_rospack = NULL; // singleton ROSPack *g_rospack = NULL; // singleton
#ifdef __APPLE__ #ifdef __APPLE__
const string g_ros_os("osx"); const string g_ros_os("osx");
#else #else
const string g_ros_os("linux"); #if defined(WIN32)
const string g_ros_os("win32");
#else
const string g_ros_os("linux");
#endif
#endif #endif
const char *fs_delim = "/"; // ifdef this for windows #if defined(WIN32)
// This isn't entirely necessary - the Win32 API functions handle / just as
// well as \ for paths, and CMake chokes if we output paths with \ in them
// anyway.
const char *fs_delim = "\\";
const char *path_delim = ";";
#else
const char *fs_delim = "/";
const char *path_delim = ":";
#endif
inline string ToUnixPathDelim(string path)
{
#if defined(WIN32)
// CMake chokes on Windows-style path separators (it thinks they're
// escapes), so either they must be escaped or replaced with UNIX-style
// separators. (If there are any real escapes in s, this will go boom.)
string token("\\");
for (string::size_type ii = path.find(token); ii != string::npos;
ii = path.find(token, ii))
{
path.replace(ii, token.length(), string("/"));
}
#endif
return path;
}
Package::Package(string _path) : path(_path), Package::Package(string _path) : path(_path),
deps_calculated(false), direct_deps_calculated(false), deps_calculated(false), direct_deps_calculated(false),
@ -337,7 +392,7 @@ const vector<Package *> &Package::direct_deps(bool missing_package_as_warning)
if (direct_deps_calculated) if (direct_deps_calculated)
return _direct_deps; return _direct_deps;
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("calculating direct deps for package [%s]\n", name.c_str()); fprintf(stderr, "calculating direct deps for package [%s]\n", name.c_str());
#endif #endif
TiXmlElement *mroot = manifest_root(); TiXmlElement *mroot = manifest_root();
TiXmlNode *dep_node = 0; TiXmlNode *dep_node = 0;
@ -356,7 +411,7 @@ const vector<Package *> &Package::direct_deps(bool missing_package_as_warning)
// cause a recrawl, which blows aways the accumulated data structure. // cause a recrawl, which blows aways the accumulated data structure.
char* dep_pkgname_copy = strdup(dep_pkgname); char* dep_pkgname_copy = strdup(dep_pkgname);
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("direct_deps: pkg %s has dep %s\n", name.c_str(), dep_pkgname_copy); fprintf(stderr, "direct_deps: pkg %s has dep %s\n", name.c_str(), dep_pkgname_copy);
#endif #endif
try try
{ {
@ -833,7 +888,7 @@ int ROSPack::cmd_find()
// todo: obey the search order // todo: obey the search order
Package *p = get_pkg(opt_package); Package *p = get_pkg(opt_package);
//printf("%s\n", p->path.c_str()); //printf("%s\n", p->path.c_str());
output_acc += p->path + "\n"; output_acc += ToUnixPathDelim(p->path) + "\n";
return 0; return 0;
} }
@ -843,7 +898,7 @@ int ROSPack::cmd_deps()
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i) for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{ {
//printf("%s\n", (*i)->name.c_str()); //printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n"; output_acc += ToUnixPathDelim((*i)->name) + "\n";
} }
return 0; return 0;
} }
@ -854,7 +909,7 @@ int ROSPack::cmd_deps_manifests()
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i) for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{ {
//printf("%s/manifest.xml ", (*i)->path.c_str()); //printf("%s/manifest.xml ", (*i)->path.c_str());
output_acc += (*i)->path + "/manifest.xml "; output_acc += ToUnixPathDelim((*i)->path + "/manifest.xml ");
} }
//puts(""); //puts("");
output_acc += "\n"; output_acc += "\n";
@ -867,17 +922,17 @@ int ROSPack::cmd_deps_msgsrv()
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i) for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{ {
Package* p = *i; Package* p = *i;
bool msg_exists = file_exists((p->path + "/msg_gen/generated").c_str()); bool msg_exists = file_exists(ToUnixPathDelim(p->path + "/msg_gen/generated").c_str());
bool srv_exists = file_exists((p->path + "/srv_gen/generated").c_str()); bool srv_exists = file_exists(ToUnixPathDelim(p->path + "/srv_gen/generated").c_str());
if (msg_exists) if (msg_exists)
{ {
output_acc += p->path + "/msg_gen/generated "; output_acc += ToUnixPathDelim(p->path + "/msg_gen/generated ");
} }
if (srv_exists) if (srv_exists)
{ {
output_acc += p->path + "/srv_gen/generated "; output_acc += ToUnixPathDelim(p->path + "/srv_gen/generated ");
} }
} }
output_acc += "\n"; output_acc += "\n";
@ -890,7 +945,7 @@ int ROSPack::cmd_deps1()
for (VecPkg::iterator i = d.begin(); i != d.end(); ++i) for (VecPkg::iterator i = d.begin(); i != d.end(); ++i)
{ {
//printf("%s\n", (*i)->name.c_str()); //printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n"; output_acc += ToUnixPathDelim((*i)->name) + "\n";
} }
return 0; return 0;
} }
@ -907,7 +962,7 @@ int ROSPack::cmd_depsindent(Package* pkg, int indent)
output_acc += " "; output_acc += " ";
} }
//printf("%s\n", (*i)->name.c_str()); //printf("%s\n", (*i)->name.c_str());
output_acc += (*i)->name + "\n"; output_acc += ToUnixPathDelim((*i)->name) + "\n";
cmd_depsindent(*i, indent+2); cmd_depsindent(*i, indent+2);
} }
return 0; return 0;
@ -1004,7 +1059,7 @@ int ROSPack::cmd_libs_only(string token)
lflags = deduplicate_tokens(lflags); lflags = deduplicate_tokens(lflags);
} }
//printf("%s\n", lflags.c_str()); //printf("%s\n", lflags.c_str());
output_acc += lflags + "\n"; output_acc += ToUnixPathDelim(lflags) + "\n";
return 0; return 0;
} }
@ -1019,7 +1074,7 @@ int ROSPack::cmd_cflags_only(string token)
cflags = deduplicate_tokens(cflags); cflags = deduplicate_tokens(cflags);
} }
//printf("%s\n", cflags.c_str()); //printf("%s\n", cflags.c_str());
output_acc += cflags + "\n"; output_acc += ToUnixPathDelim(cflags) + "\n";
return 0; return 0;
} }
@ -1027,7 +1082,7 @@ void ROSPack::export_flags(string pkg, string lang, string attrib)
{ {
string flags = get_pkg(pkg)->flags(lang, attrib); string flags = get_pkg(pkg)->flags(lang, attrib);
//printf("%s\n", flags.c_str()); //printf("%s\n", flags.c_str());
output_acc += flags + "\n"; output_acc += ToUnixPathDelim(flags) + "\n";
} }
int ROSPack::cmd_versioncontrol(int depth) int ROSPack::cmd_versioncontrol(int depth)
@ -1285,7 +1340,24 @@ int ROSPack::run(int argc, char **argv)
if(getcwd(buf,sizeof(buf))) if(getcwd(buf,sizeof(buf)))
{ {
if(Package::is_package(".")) if(Package::is_package("."))
{
#if defined(WIN32)
// No basename on Windows; use _splitpath_s instead
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
_splitpath_s(buf, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
ext, _MAX_EXT);
char filename[_MAX_FNAME + _MAX_EXT];
if (ext[0] != '\0')
{
_makepath_s(filename, _MAX_FNAME + _MAX_EXT, NULL, NULL, fname, ext);
opt_package = string(filename);
}
else
opt_package = string(fname);
#else
opt_package = string(basename(buf)); opt_package = string(basename(buf));
#endif
}
} }
} }
@ -1304,7 +1376,7 @@ int ROSPack::run(int argc, char **argv)
opt_profile_length = 20; // default is about a screenful or so opt_profile_length = 20; // default is about a screenful or so
} }
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("profile_length = %d\n", opt_profile_length); fprintf(stderr, "profile_length = %d\n", opt_profile_length);
#endif #endif
// re-crawl with profiling enabled // re-crawl with profiling enabled
crawl_for_packages(true); crawl_for_packages(true);
@ -1455,7 +1527,11 @@ string ROSPack::getCachePath()
// //
// By providing the trailing slash, stat() will only succeed if the // By providing the trailing slash, stat() will only succeed if the
// path exists AND is a directory. // path exists AND is a directory.
#if defined(WIN32)
std::string ros_home_slash = ros_home + std::string("\\");
#else
std::string ros_home_slash = ros_home + std::string("/"); std::string ros_home_slash = ros_home + std::string("/");
#endif
struct stat s; struct stat s;
if(stat(ros_home_slash.c_str(), &s)) if(stat(ros_home_slash.c_str(), &s))
{ {
@ -1468,6 +1544,7 @@ string ROSPack::getCachePath()
} }
else else
{ {
// UNIXONLY
// Not cross-platform? // Not cross-platform?
ros_home = getenv("HOME"); ros_home = getenv("HOME");
if (ros_home) if (ros_home)
@ -1502,7 +1579,7 @@ bool ROSPack::cache_is_good()
{ {
double dt = difftime(time(NULL), s.st_mtime); double dt = difftime(time(NULL), s.st_mtime);
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("cache age: %f\n", dt); fprintf(stderr, "cache age: %f\n", dt);
#endif #endif
// Negative cache_max_age means it's always new enough. It's dangerous // Negative cache_max_age means it's always new enough. It's dangerous
// for the user to set this, but rosbash uses it. // for the user to set this, but rosbash uses it.
@ -1566,9 +1643,27 @@ public:
double ROSPack::time_since_epoch() double ROSPack::time_since_epoch()
{ {
#if defined(WIN32)
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
FILETIME ft;
unsigned __int64 tmpres = 0;
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
tmpres /= 10;
tmpres -= DELTA_EPOCH_IN_MICROSECS;
return static_cast<double>(tmpres) / 1e6;
#else
struct timeval tod; struct timeval tod;
gettimeofday(&tod, NULL); gettimeofday(&tod, NULL);
return tod.tv_sec + 1e-6 * tod.tv_usec; return tod.tv_sec + 1e-6 * tod.tv_usec;
#endif
} }
// Add package, filtering out duplicates. // Add package, filtering out duplicates.
@ -1617,7 +1712,7 @@ void ROSPack::crawl_for_packages(bool force_crawl)
if (cache) // one last check just in case nutty stuff happened in between if (cache) // one last check just in case nutty stuff happened in between
{ {
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("trying to use cache...\n"); fprintf(stderr, "trying to use cache...\n");
#endif #endif
char linebuf[30000]; char linebuf[30000];
for(;;) for(;;)
@ -1639,14 +1734,14 @@ void ROSPack::crawl_for_packages(bool force_crawl)
// if we get here, this means the cache either bogus or we've been // if we get here, this means the cache either bogus or we've been
// instructed to rebuild it. // instructed to rebuild it.
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("building cache\n"); fprintf(stderr, "building cache\n");
#endif #endif
deque<CrawlQueueEntry> q; deque<CrawlQueueEntry> q;
q.push_back(CrawlQueueEntry(ros_root)); q.push_back(CrawlQueueEntry(ros_root));
if (char *rpp = getenv("ROS_PACKAGE_PATH")) if (char *rpp = getenv("ROS_PACKAGE_PATH"))
{ {
vector<string> rppvec; vector<string> rppvec;
string_split(rpp, rppvec, ":"); string_split(rpp, rppvec, path_delim);
sanitize_rppvec(rppvec); sanitize_rppvec(rppvec);
for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i) for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i)
{ {
@ -1716,6 +1811,62 @@ void ROSPack::crawl_for_packages(bool force_crawl)
cqe.start_num_pkgs = total_num_pkgs; cqe.start_num_pkgs = total_num_pkgs;
q.push_front(cqe); q.push_front(cqe);
} }
#if defined(WIN32)
WIN32_FIND_DATA find_file_data;
HANDLE hfind = INVALID_HANDLE_VALUE;
if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
&find_file_data)) == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "[rospack] FindFirstFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
do
{
if (!S_ISDIR(find_file_data.dwFileAttributes))
continue; // Ignore non-directories
if (find_file_data.cFileName[0] == '.')
continue; // Ignore hidden directories
string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
if (Package::is_package(child_path))
{
total_num_pkgs++;
// Filter out duplicates; first encountered takes precedence
Package* newp = new Package(child_path);
// TODO: make this check more efficient
bool dup = false;
for(std::vector<Package *>::const_iterator it = Package::pkgs.begin();
it != Package::pkgs.end();
it++)
{
if((*it)->name == newp->name)
{
dup=true;
break;
}
}
if(dup)
delete newp;
else
{
Package::pkgs.push_back(newp);
}
}
//check to make sure we're allowed to descend
else if (!Package::is_no_subdirs(child_path))
q.push_front(CrawlQueueEntry(child_path));
}
while (FindNextFile(hfind, &find_file_data) != 0);
DWORD last_error = GetLastError();
FindClose(hfind);
if (last_error != ERROR_NO_MORE_FILES)
{
fprintf(stderr, "[rospack] FindNextFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
#else
DIR *d = opendir(cqe.path.c_str()); DIR *d = opendir(cqe.path.c_str());
if (!d) if (!d)
{ {
@ -1747,6 +1898,7 @@ void ROSPack::crawl_for_packages(bool force_crawl)
q.push_front(CrawlQueueEntry(child_path)); q.push_front(CrawlQueueEntry(child_path));
} }
closedir(d); closedir(d);
#endif
} }
crawled = true; // don't try to re-crawl if we can't find something crawled = true; // don't try to re-crawl if we can't find something
const double crawl_elapsed_time = time_since_epoch() - crawl_start_time; const double crawl_elapsed_time = time_since_epoch() - crawl_start_time;
@ -1762,7 +1914,31 @@ void ROSPack::crawl_for_packages(bool force_crawl)
char tmp_cache_dir[PATH_MAX]; char tmp_cache_dir[PATH_MAX];
char tmp_cache_path[PATH_MAX]; char tmp_cache_path[PATH_MAX];
strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir)); strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
#if defined(WIN32)
// No dirname on Windows; use _splitpath_s instead
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
_splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
ext, _MAX_EXT);
char full_dir[_MAX_DRIVE + _MAX_DIR];
_makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rospack_cache.XXXXXX", full_dir);
#else
snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rospack_cache.XXXXXX", dirname(tmp_cache_dir)); snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rospack_cache.XXXXXX", dirname(tmp_cache_dir));
#endif
#if defined(WIN32)
// This one is particularly nasty: on Windows, there is no equivalent of
// mkstemp, so we're stuck with the security risks of mktemp. Hopefully not a
// problem in our use cases.
if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
{
fprintf(stderr,
"[rospack] Unable to generate temporary cache file name: %u",
GetLastError());
}
else
{
FILE *cache = fopen(tmp_cache_path, "w");
#else
int fd = mkstemp(tmp_cache_path); int fd = mkstemp(tmp_cache_path);
if (fd < 0) if (fd < 0)
{ {
@ -1772,6 +1948,7 @@ void ROSPack::crawl_for_packages(bool force_crawl)
else else
{ {
FILE *cache = fdopen(fd, "w"); FILE *cache = fdopen(fd, "w");
#endif
if (!cache) if (!cache)
{ {
fprintf(stderr, "[rospack] Unable open cache file %s: %s\n", fprintf(stderr, "[rospack] Unable open cache file %s: %s\n",
@ -1786,6 +1963,8 @@ void ROSPack::crawl_for_packages(bool force_crawl)
pkg != Package::pkgs.end(); ++pkg) pkg != Package::pkgs.end(); ++pkg)
fprintf(cache, "%s\n", (*pkg)->path.c_str()); fprintf(cache, "%s\n", (*pkg)->path.c_str());
fclose(cache); fclose(cache);
if(file_exists(cache_path.c_str()))
remove(cache_path.c_str());
if(rename(tmp_cache_path, cache_path.c_str()) < 0) if(rename(tmp_cache_path, cache_path.c_str()) < 0)
{ {
fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n", fprintf(stderr, "[rospack] Error: failed to rename cache file %s to %s: %s\n",
@ -1894,6 +2073,59 @@ VecPkg ROSPack::partial_crawl(const string &path)
CrawlQueueEntry cqe = q.front(); CrawlQueueEntry cqe = q.front();
//printf("crawling %s\n", cqe.path.c_str()); //printf("crawling %s\n", cqe.path.c_str());
q.pop_front(); q.pop_front();
#if defined(WIN32)
WIN32_FIND_DATA find_file_data;
HANDLE hfind = INVALID_HANDLE_VALUE;
if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
&find_file_data)) == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "[rospack] FindFirstFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
do
{
if (!S_ISDIR(find_file_data.dwFileAttributes))
continue; // Ignore non-directories
if (find_file_data.cFileName[0] == '.')
continue; // Ignore hidden directories
string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
if (Package::is_package(child_path))
{
// Filter out duplicates; first encountered takes precedence
Package* newp = new Package(child_path);
// TODO: make this check more efficient
bool dup = false;
for(std::vector<Package *>::const_iterator it = partial_pkgs.begin();
it != partial_pkgs.end();
it++)
{
if((*it)->name == newp->name)
{
dup=true;
break;
}
}
if(dup)
delete newp;
else
partial_pkgs.push_back(newp);
}
//check to make sure we're allowed to descend
else if (!Package::is_no_subdirs(child_path))
q.push_front(CrawlQueueEntry(child_path));
}
while (FindNextFile(hfind, &find_file_data) != 0);
DWORD last_error = GetLastError();
FindClose(hfind);
if (last_error != ERROR_NO_MORE_FILES)
{
fprintf(stderr, "[rospack] FindNextFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
#else
DIR *d = opendir(cqe.path.c_str()); DIR *d = opendir(cqe.path.c_str());
if (!d) if (!d)
{ {
@ -1941,6 +2173,7 @@ VecPkg ROSPack::partial_crawl(const string &path)
q.push_front(CrawlQueueEntry(child_path)); q.push_front(CrawlQueueEntry(child_path));
} }
closedir(d); closedir(d);
#endif
} }
return partial_pkgs; return partial_pkgs;
} }
@ -1999,6 +2232,7 @@ string ROSPack::deduplicate_tokens(const string& s)
bool file_exists(const string &fname) bool file_exists(const string &fname)
{ {
// this will be different in windows // this will be different in windows
// ^^ For once, it's actually _not_ that different
return (access(fname.c_str(), F_OK) == 0); return (access(fname.c_str(), F_OK) == 0);
} }

View File

@ -38,17 +38,38 @@
#include <stack> #include <stack>
#include <queue> #include <queue>
#include <cassert> #include <cassert>
#include <unistd.h> #if !defined(WIN32)
#include <dirent.h> #include <unistd.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/file.h>
#include <stdint.h>
#endif
#include <stdexcept> #include <stdexcept>
#include <sys/time.h>
#include <sys/file.h>
#include <time.h> #include <time.h>
#include <sstream> #include <sstream>
#include <iterator> #include <iterator>
#include <stdint.h>
#include <libgen.h> #if !defined(WIN32)
#include <libgen.h>
#endif
#if defined(WIN32)
#include <direct.h>
#include <time.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#define PATH_MAX MAX_PATH
#define snprintf _snprintf
#define getcwd _getcwd
#define fdopen _fdopen
#define access _access
#define F_OK 0x00
#define W_OK 0x02
#define R_OK 0x04
#define mkdir(a,b) _mkdir(a)
#endif
#include "tinyxml-2.5.3/tinyxml.h" #include "tinyxml-2.5.3/tinyxml.h"
#include "rospack/rosstack.h" #include "rospack/rosstack.h"
@ -68,9 +89,45 @@ using namespace rosstack;
#ifdef __APPLE__ #ifdef __APPLE__
const string g_ros_os("osx"); const string g_ros_os("osx");
#else #else
const string g_ros_os("linux"); #if defined(WIN32)
const string g_ros_os("win32");
#else
const string g_ros_os("linux");
#endif
#endif #endif
#if defined(WIN32)
// The MS compiler complains bitterly about undefined symbols due to the
// static members of the TiXmlBase class. They need to exist in every
// compilation unit (i.e. DLL or EXE), but they don't get exported
// properly across DLL boundaries (for reasons I've tried to investigate,
// before deciding it was a waste of my time). So they're defined here as well
// to keep rosstack happy (rospack's lib links directly to tinyxml.cpp,
// rosstack's lib does not).
// I'll fix this later. Thanks, MS, for creating yet another broken system.
const int TiXmlBase::utf8ByteTable[256] =
{
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
};
bool TiXmlBase::condenseWhiteSpace = true;
#endif
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Global storage for --foo options // Global storage for --foo options
// --deps-only // --deps-only
@ -80,20 +137,30 @@ string g_length;
// The stack name // The stack name
string g_stack; string g_stack;
// the number of entries to list in the profile table // the number of entries to list in the profile table
uint32_t g_profile_length = 0; unsigned int g_profile_length = 0;
// global singleton rosstack pointer... yeah, I know. // global singleton rosstack pointer... yeah, I know.
ROSStack *g_rosstack = NULL; ROSStack *g_rosstack = NULL;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
const char *rosstack::fs_delim = "/"; // ifdef this for windows someday #if defined(WIN32)
// This isn't entirely necessary - the Win32 API functions handle / just as
// well as \ for paths, and CMake chokes if we output paths with \ in them
// anyway.
const char *rosstack::fs_delim = "\\";
const char *rosstack::path_delim = ";";
#else
const char *rosstack::fs_delim = "/";
const char *rosstack::path_delim = ":";
#endif
Stack::Stack(string _path) : path(_path), Stack::Stack(string _path) : path(_path),
deps_calculated(false), direct_deps_calculated(false), deps_calculated(false), direct_deps_calculated(false),
descendants_calculated(false), manifest_loaded(false) descendants_calculated(false), manifest_loaded(false)
{ {
vector<string> path_tokens; vector<string> path_tokens;
string_split(path, path_tokens, "/"); string_split(path, path_tokens, fs_delim);
name = path_tokens.back(); name = path_tokens.back();
// Don't load the manifest here, because it causes spurious errors to be // Don't load the manifest here, because it causes spurious errors to be
// printed if the stack has been moved (#1785). Presumably the manifest // printed if the stack has been moved (#1785). Presumably the manifest
@ -120,7 +187,7 @@ const VecStack &Stack::deps(traversal_order_t order, int depth)
{ {
if (depth > 1000) if (depth > 1000)
{ {
fprintf(stderr,"[rospack] woah! expanding the dependency tree made it blow " fprintf(stderr,"[rosstack] woah! expanding the dependency tree made it blow "
"up.\n There must be a circular dependency somewhere.\n"); "up.\n There must be a circular dependency somewhere.\n");
throw runtime_error(string("circular dependency")); throw runtime_error(string("circular dependency"));
} }
@ -503,7 +570,7 @@ string ROSStack::lookup_owner(string pkg_name, bool just_owner_name)
if (rpp) if (rpp)
{ {
vector<string> rppvec; vector<string> rppvec;
string_split(rpp, rppvec, ":"); string_split(rpp, rppvec, path_delim);
sanitize_rppvec(rppvec); sanitize_rppvec(rppvec);
for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i) for (vector<string>::iterator i = rppvec.begin(); i != rppvec.end(); ++i)
bases[*i] = string(""); bases[*i] = string("");
@ -648,7 +715,22 @@ int ROSStack::run(int argc, char **argv)
char buf[1024]; char buf[1024];
if(!getcwd(buf,sizeof(buf))) if(!getcwd(buf,sizeof(buf)))
throw runtime_error(errmsg); throw runtime_error(errmsg);
#if defined(WIN32)
// No basename on Windows; use _splitpath_s instead
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
_splitpath_s(buf, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
ext, _MAX_EXT);
char filename[_MAX_FNAME + _MAX_EXT];
if (ext[0] != '\0')
{
_makepath_s(filename, _MAX_FNAME + _MAX_EXT, NULL, NULL, fname, ext);
g_stack = string(filename);
}
else
g_stack = string(fname);
#else
g_stack = string(basename(buf)); g_stack = string(basename(buf));
#endif
} }
if (i != argc) if (i != argc)
@ -741,13 +823,13 @@ void ROSStack::createROSHomeDirectory()
string ROSStack::getCachePath() string ROSStack::getCachePath()
{ {
string path; string path;
path = string(ros_root) + "/.rosstack_cache"; path = string(ros_root) + fs_delim + ".rosstack_cache";
if (access(ros_root, W_OK) == 0) if (access(ros_root, W_OK) == 0)
return path; return path;
// if we cannot write into the ros_root, then let's try to // if we cannot write into the ros_root, then let's try to
// write into the user's .ros directory. // write into the user's .ros directory.
createROSHomeDirectory(); createROSHomeDirectory();
path = string(getenv("HOME")) + "/.ros/rosstack_cache"; path = string(getenv("HOME")) + fs_delim + ".ros" + fs_delim + "rosstack_cache";
return path; return path;
} }
@ -837,9 +919,27 @@ public:
double ROSStack::time_since_epoch() double ROSStack::time_since_epoch()
{ {
#if defined(WIN32)
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
FILETIME ft;
unsigned __int64 tmpres = 0;
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
tmpres /= 10;
tmpres -= DELTA_EPOCH_IN_MICROSECS;
return static_cast<double>(tmpres) / 1e6;
#else
struct timeval tod; struct timeval tod;
gettimeofday(&tod, NULL); gettimeofday(&tod, NULL);
return tod.tv_sec + 1e-6 * tod.tv_usec; return tod.tv_sec + 1e-6 * tod.tv_usec;
#endif
} }
// Add stack, filtering out duplicates. // Add stack, filtering out duplicates.
@ -925,7 +1025,7 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
char *rpp = getenv("ROS_PACKAGE_PATH"); char *rpp = getenv("ROS_PACKAGE_PATH");
if (rpp) if (rpp)
rsp = string(rpp); rsp = string(rpp);
string_split(rsp, rspvec, ":"); string_split(rsp, rspvec, path_delim);
sanitize_rppvec(rspvec); sanitize_rppvec(rspvec);
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
printf("seeding crawler with [%s], which has %lu entries\n", rsp.c_str(), rspvec.size()); printf("seeding crawler with [%s], which has %lu entries\n", rsp.c_str(), rspvec.size());
@ -975,6 +1075,64 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
cqe.start_time = time_since_epoch(); cqe.start_time = time_since_epoch();
q.push_front(cqe); q.push_front(cqe);
} }
#if defined(WIN32)
// And again...
WIN32_FIND_DATA find_file_data;
HANDLE hfind = INVALID_HANDLE_VALUE;
if ((hfind = FindFirstFile((cqe.path + "\\*").c_str(),
&find_file_data)) == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "[rosstack] FindFirstFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
do
{
if (!S_ISDIR(find_file_data.dwFileAttributes))
continue; // Ignore non-directories
if (find_file_data.cFileName[0] == '.')
continue; // Ignore hidden directories
string child_path = cqe.path + fs_delim + string(find_file_data.cFileName);
if (Stack::is_stack(child_path))
continue; // Ignore leaves.
if (Stack::is_stack(child_path))
{
// Filter out duplicates; first encountered takes precedence
Stack *newp = new Stack(child_path);
//printf("found stack %s\n", child_path.c_str());
// TODO: make this check more efficient
bool dup = false;
for(std::vector<Stack *>::const_iterator it = Stack::stacks.begin();
it != Stack::stacks.end();
it++)
{
if((*it)->name == newp->name)
{
dup=true;
break;
}
}
if(dup)
delete newp;
else
Stack::stacks.push_back(newp);
}
//check to make sure we're allowed to descend
else if (!Stack::is_no_subdirs(child_path))
q.push_front(CrawlQueueEntry(child_path));
}
while (FindNextFile(hfind, &find_file_data) != 0);
DWORD last_error = GetLastError();
FindClose(hfind);
if (last_error != ERROR_NO_MORE_FILES)
{
fprintf(stderr, "[rosstack] FindNextFile error %u while crawling %s\n",
GetLastError(), cqe.path.c_str());
continue;
}
#else
DIR *d = opendir(cqe.path.c_str()); DIR *d = opendir(cqe.path.c_str());
if (!d) if (!d)
{ {
@ -1025,6 +1183,7 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
q.push_front(CrawlQueueEntry(child_path)); q.push_front(CrawlQueueEntry(child_path));
} }
closedir(d); closedir(d);
#endif
} }
crawled = true; // don't try to re-crawl if we can't find something crawled = true; // don't try to re-crawl if we can't find something
const double crawl_elapsed_time = time_since_epoch() - crawl_start_time; const double crawl_elapsed_time = time_since_epoch() - crawl_start_time;
@ -1033,7 +1192,30 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
char tmp_cache_dir[PATH_MAX]; char tmp_cache_dir[PATH_MAX];
char tmp_cache_path[PATH_MAX]; char tmp_cache_path[PATH_MAX];
strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir)); strncpy(tmp_cache_dir, cache_path.c_str(), sizeof(tmp_cache_dir));
#if defined(WIN32)
// No dirname on Windows; use _splitpath_s instead
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
_splitpath_s(tmp_cache_dir, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME,
ext, _MAX_EXT);
char full_dir[_MAX_DRIVE + _MAX_DIR];
_makepath_s(full_dir, _MAX_DRIVE + _MAX_DIR, drive, dir, NULL, NULL);
snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s\\.rosstack_cache.XXXXXX", full_dir);
#else
snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rosstack_cache.XXXXXX", dirname(tmp_cache_dir)); snprintf(tmp_cache_path, sizeof(tmp_cache_path), "%s/.rosstack_cache.XXXXXX", dirname(tmp_cache_dir));
#endif
#if defined(WIN32)
// This one is particularly nasty: on Windows, there is no equivalent of
// mkstemp, so we're stuck with the security risks of mktemp. Hopefully not a
// problem in our use cases.
if (_mktemp_s(tmp_cache_path, PATH_MAX) != 0)
{
fprintf(stderr,
"[rosstack] Unable to generate temporary cache file name: %u",
GetLastError());
throw runtime_error(string("Failed to create tmp cache file name"));
}
FILE *cache = fopen(tmp_cache_path, "w");
#else
int fd = mkstemp(tmp_cache_path); int fd = mkstemp(tmp_cache_path);
if (fd < 0) if (fd < 0)
{ {
@ -1042,6 +1224,7 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
} }
FILE *cache = fdopen(fd, "w"); FILE *cache = fdopen(fd, "w");
#endif
if (!cache) if (!cache)
{ {
fprintf(stderr, "woah! couldn't create the cache file. Please check " fprintf(stderr, "woah! couldn't create the cache file. Please check "
@ -1053,16 +1236,19 @@ void ROSStack::crawl_for_stacks(bool force_crawl)
for (VecStack::iterator s = Stack::stacks.begin(); for (VecStack::iterator s = Stack::stacks.begin();
s != Stack::stacks.end(); ++s) s != Stack::stacks.end(); ++s)
fprintf(cache, "%s\n", (*s)->path.c_str()); fprintf(cache, "%s\n", (*s)->path.c_str());
fclose(cache);
if(file_exists(cache_path.c_str()))
remove(cache_path.c_str());
if(rename(tmp_cache_path, cache_path.c_str()) < 0) if(rename(tmp_cache_path, cache_path.c_str()) < 0)
{ {
fprintf(stderr, "[rospack] Error: failed rename cache file %s to %s\n", tmp_cache_path, cache_path.c_str()); fprintf(stderr,
perror("rename"); "[rospack] Error: failed rename cache file %s to %s\n",
tmp_cache_path, cache_path.c_str());
perror("rename");
throw runtime_error(string("failed to rename cache file")); throw runtime_error(string("failed to rename cache file"));
} }
fclose(cache);
if (g_profile_length) if (g_profile_length)
{ {
// dump it into a stack to reverse it (so slowest guys are first) // dump it into a stack to reverse it (so slowest guys are first)

View File

@ -82,6 +82,18 @@ distribution.
#endif #endif
#endif #endif
#if defined(WIN32)
#if defined(ROS_STATIC)
#define TINYXML_EXPORT
#elif defined(rospack_EXPORTS) || defined(rosstack_EXPORTS)
#define TINYXML_EXPORT __declspec(dllexport)
#else
#define TINYXML_EXPORT __declspec(dllimport)
#endif
#else
#define TINYXML_EXPORT
#endif
class TiXmlDocument; class TiXmlDocument;
class TiXmlElement; class TiXmlElement;
class TiXmlComment; class TiXmlComment;
@ -126,7 +138,7 @@ struct TiXmlCursor
@sa TiXmlNode::Accept() @sa TiXmlNode::Accept()
*/ */
class TiXmlVisitor class TINYXML_EXPORT TiXmlVisitor
{ {
public: public:
virtual ~TiXmlVisitor() {} virtual ~TiXmlVisitor() {}
@ -192,7 +204,7 @@ const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
A Decleration contains: Attributes (not on tree) A Decleration contains: Attributes (not on tree)
@endverbatim @endverbatim
*/ */
class TiXmlBase class TINYXML_EXPORT TiXmlBase
{ {
friend class TiXmlNode; friend class TiXmlNode;
friend class TiXmlElement; friend class TiXmlElement;
@ -249,7 +261,7 @@ public:
void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data.
const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data.
// Table that returs, for a given lead byte, the total number of bytes // Table that returns, for a given lead byte, the total number of bytes
// in the UTF-8 sequence. // in the UTF-8 sequence.
static const int utf8ByteTable[256]; static const int utf8ByteTable[256];
@ -421,7 +433,7 @@ private:
in a document, or stand on its own. The type of a TiXmlNode in a document, or stand on its own. The type of a TiXmlNode
can be queried, and it can be cast to its more defined type. can be queried, and it can be cast to its more defined type.
*/ */
class TiXmlNode : public TiXmlBase class TINYXML_EXPORT TiXmlNode : public TiXmlBase
{ {
friend class TiXmlDocument; friend class TiXmlDocument;
friend class TiXmlElement; friend class TiXmlElement;
@ -777,7 +789,7 @@ private:
part of the tinyXML document object model. There are other part of the tinyXML document object model. There are other
suggested ways to look at this problem. suggested ways to look at this problem.
*/ */
class TiXmlAttribute : public TiXmlBase class TINYXML_EXPORT TiXmlAttribute : public TiXmlBase
{ {
friend class TiXmlAttributeSet; friend class TiXmlAttributeSet;
@ -901,7 +913,7 @@ private:
- I like circular lists - I like circular lists
- it demonstrates some independence from the (typical) doubly linked list. - it demonstrates some independence from the (typical) doubly linked list.
*/ */
class TiXmlAttributeSet class TINYXML_EXPORT TiXmlAttributeSet
{ {
public: public:
TiXmlAttributeSet(); TiXmlAttributeSet();
@ -941,7 +953,7 @@ private:
and can contain other elements, text, comments, and unknowns. and can contain other elements, text, comments, and unknowns.
Elements also contain an arbitrary number of attributes. Elements also contain an arbitrary number of attributes.
*/ */
class TiXmlElement : public TiXmlNode class TINYXML_EXPORT TiXmlElement : public TiXmlNode
{ {
public: public:
/// Construct an element. /// Construct an element.
@ -1151,7 +1163,7 @@ private:
/** An XML comment. /** An XML comment.
*/ */
class TiXmlComment : public TiXmlNode class TINYXML_EXPORT TiXmlComment : public TiXmlNode
{ {
public: public:
/// Constructs an empty comment. /// Constructs an empty comment.
@ -1201,7 +1213,7 @@ private:
you generally want to leave it alone, but you can change the output mode with you generally want to leave it alone, but you can change the output mode with
SetCDATA() and query it with CDATA(). SetCDATA() and query it with CDATA().
*/ */
class TiXmlText : public TiXmlNode class TINYXML_EXPORT TiXmlText : public TiXmlNode
{ {
friend class TiXmlElement; friend class TiXmlElement;
public: public:
@ -1274,7 +1286,7 @@ private:
handled as special cases, not generic attributes, simply handled as special cases, not generic attributes, simply
because there can only be at most 3 and they are always the same. because there can only be at most 3 and they are always the same.
*/ */
class TiXmlDeclaration : public TiXmlNode class TINYXML_EXPORT TiXmlDeclaration : public TiXmlNode
{ {
public: public:
/// Construct an empty declaration. /// Construct an empty declaration.
@ -1343,7 +1355,7 @@ private:
DTD tags get thrown into TiXmlUnknowns. DTD tags get thrown into TiXmlUnknowns.
*/ */
class TiXmlUnknown : public TiXmlNode class TINYXML_EXPORT TiXmlUnknown : public TiXmlNode
{ {
public: public:
TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {}
@ -1382,7 +1394,7 @@ private:
XML pieces. It can be saved, loaded, and printed to the screen. XML pieces. It can be saved, loaded, and printed to the screen.
The 'value' of a document node is the xml file name. The 'value' of a document node is the xml file name.
*/ */
class TiXmlDocument : public TiXmlNode class TINYXML_EXPORT TiXmlDocument : public TiXmlNode
{ {
public: public:
/// Create an empty document, that has no name. /// Create an empty document, that has no name.
@ -1631,7 +1643,7 @@ private:
} }
@endverbatim @endverbatim
*/ */
class TiXmlHandle class TINYXML_EXPORT TiXmlHandle
{ {
public: public:
/// Create a handle from any node (at any depth of the tree.) This can be a null pointer. /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
@ -1730,7 +1742,7 @@ private:
fprintf( stdout, "%s", printer.CStr() ); fprintf( stdout, "%s", printer.CStr() );
@endverbatim @endverbatim
*/ */
class TiXmlPrinter : public TiXmlVisitor class TINYXML_EXPORT TiXmlPrinter : public TiXmlVisitor
{ {
public: public:
TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),