561 lines
18 KiB
C++
561 lines
18 KiB
C++
// Filename: ppDirectoryTree.cxx
|
|
// Created by: drose (28Sep00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "ppDirectoryTree.h"
|
|
#include "ppScope.h"
|
|
#include "ppNamedScopes.h"
|
|
#include "ppCommandFile.h"
|
|
#include "tokenize.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <algorithm>
|
|
|
|
PPDirectoryTree *current_output_directory = (PPDirectoryTree *)NULL;
|
|
|
|
// An STL object to sort directories in order by dependency and then
|
|
// by name, used in get_child_dirnames().
|
|
class SortDirectoriesByDependencyAndName {
|
|
public:
|
|
bool operator () (const PPDirectoryTree *a, const PPDirectoryTree *b) const {
|
|
if (a->get_depends_index() != b->get_depends_index()) {
|
|
return a->get_depends_index() < b->get_depends_index();
|
|
}
|
|
return a->get_dirname() < b->get_dirname();
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::Constructor
|
|
// Access: Public
|
|
// Description: Creates the root level of the PPDirectoryTree.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPDirectoryTree::
|
|
PPDirectoryTree() {
|
|
_scope = (PPScope *)NULL;
|
|
_source = (PPCommandFile *)NULL;
|
|
_parent = (PPDirectoryTree *)NULL;
|
|
_depth = 0;
|
|
_depends_index = 0;
|
|
_computing_depends_index = false;
|
|
_dirnames = new Dirnames;
|
|
|
|
_dirname = "top";
|
|
(*_dirnames).insert(Dirnames::value_type(_dirname, this));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::Destructor
|
|
// Access: Public
|
|
// Description: When a tree root destructs, all of its children are
|
|
// also destroyed.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPDirectoryTree::
|
|
~PPDirectoryTree() {
|
|
Children::iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
delete (*ci);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::Constructor
|
|
// Access: Protected
|
|
// Description: Creates a new directory level that automatically adds
|
|
// itself to its parent's children list.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPDirectoryTree::
|
|
PPDirectoryTree(const string &dirname, PPDirectoryTree *parent) :
|
|
_dirname(dirname),
|
|
_parent(parent)
|
|
{
|
|
assert(_parent != (PPDirectoryTree *)NULL);
|
|
_scope = (PPScope *)NULL;
|
|
_source = (PPCommandFile *)NULL;
|
|
_parent->_children.push_back(this);
|
|
_depth = _parent->_depth + 1;
|
|
_depends_index = 0;
|
|
_computing_depends_index = false;
|
|
_dirnames = _parent->_dirnames;
|
|
|
|
bool inserted =
|
|
(*_dirnames).insert(Dirnames::value_type(_dirname, this)).second;
|
|
if (!inserted) {
|
|
cerr << "Warning: multiple directories encountered named "
|
|
<< _dirname << "\n";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::scan_source
|
|
// Access: Public
|
|
// Description: Reads in the complete hierarchy of source files,
|
|
// beginning at the current directory.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
scan_source(PPNamedScopes *named_scopes) {
|
|
if (!r_scan("")) {
|
|
return false;
|
|
}
|
|
|
|
if (!read_source_file("", named_scopes)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::scan_depends
|
|
// Access: Public
|
|
// Description: Reads in the depends file for each source file, and
|
|
// then sorts the files into dependency order.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
scan_depends(PPNamedScopes *named_scopes) {
|
|
if (!read_depends_file(named_scopes)) {
|
|
return false;
|
|
}
|
|
|
|
if (!resolve_dependencies()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::count_source_files
|
|
// Access: Public
|
|
// Description: Returns the number of directories within the tree
|
|
// that actually have a Sources.pp file that was read.
|
|
////////////////////////////////////////////////////////////////////
|
|
int PPDirectoryTree::
|
|
count_source_files() const {
|
|
int count = 0;
|
|
if (_source != (PPCommandFile *)NULL) {
|
|
count++;
|
|
}
|
|
|
|
Children::const_iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
count += (*ci)->count_source_files();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_dirname
|
|
// Access: Public
|
|
// Description: Returns the name of this particular directory level.
|
|
////////////////////////////////////////////////////////////////////
|
|
const string &PPDirectoryTree::
|
|
get_dirname() const {
|
|
return _dirname;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_depends_index
|
|
// Access: Public
|
|
// Description: Returns the dependency index associated with this
|
|
// directory. It is generally true that if directory A
|
|
// depends on B, then A.get_depends_index() >
|
|
// B.get_depends_index().
|
|
////////////////////////////////////////////////////////////////////
|
|
int PPDirectoryTree::
|
|
get_depends_index() const {
|
|
return _depends_index;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_path
|
|
// Access: Public
|
|
// Description: Returns the relative path from the root to this
|
|
// particular directory. This does not include the root
|
|
// name itself, and does not include a trailing slash.
|
|
////////////////////////////////////////////////////////////////////
|
|
string PPDirectoryTree::
|
|
get_path() const {
|
|
if (_parent == (PPDirectoryTree *)NULL) {
|
|
return ".";
|
|
}
|
|
if (_parent->_parent == (PPDirectoryTree *)NULL) {
|
|
return _dirname;
|
|
}
|
|
return _parent->get_path() + "/" + _dirname;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_rel_to
|
|
// Access: Public
|
|
// Description: Returns the relative path to the other directory from
|
|
// this one. This does not include a trailing slash.
|
|
////////////////////////////////////////////////////////////////////
|
|
string PPDirectoryTree::
|
|
get_rel_to(const PPDirectoryTree *other) const {
|
|
const PPDirectoryTree *a = this;
|
|
const PPDirectoryTree *b = other;
|
|
|
|
if (a == b) {
|
|
return ".";
|
|
}
|
|
|
|
string prefix, postfix;
|
|
while (a->_depth > b->_depth) {
|
|
prefix += "../";
|
|
a = a->_parent;
|
|
assert(a != (PPDirectoryTree *)NULL);
|
|
}
|
|
|
|
while (b->_depth > a->_depth) {
|
|
postfix = b->_dirname + "/" + postfix;
|
|
b = b->_parent;
|
|
assert(b != (PPDirectoryTree *)NULL);
|
|
}
|
|
|
|
while (a != b) {
|
|
prefix += "../";
|
|
postfix = b->_dirname + "/" + postfix;
|
|
a = a->_parent;
|
|
b = b->_parent;
|
|
assert(a != (PPDirectoryTree *)NULL);
|
|
assert(b != (PPDirectoryTree *)NULL);
|
|
}
|
|
|
|
string result = prefix + postfix;
|
|
assert(!result.empty());
|
|
return result.substr(0, result.length() - 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::find_dirname
|
|
// Access: Public
|
|
// Description: Searches for the first subdirectory found with the
|
|
// matching dirname. This is just the name of the
|
|
// directory itself, not the relative path to the
|
|
// directory.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPDirectoryTree *PPDirectoryTree::
|
|
find_dirname(const string &dirname) {
|
|
assert(_dirnames != (Dirnames *)NULL);
|
|
Dirnames::const_iterator di;
|
|
di = _dirnames->find(dirname);
|
|
if (di != _dirnames->end()) {
|
|
return (*di).second;
|
|
}
|
|
|
|
// No such dirname; too bad.
|
|
return (PPDirectoryTree *)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_source
|
|
// Access: Public
|
|
// Description: Returns the source file associated with this level
|
|
// of the directory hierarchy. This *might* be NULL.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPCommandFile *PPDirectoryTree::
|
|
get_source() const {
|
|
return _source;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_num_children
|
|
// Access: Public
|
|
// Description: Returns the number of subdirectories below this
|
|
// level.
|
|
////////////////////////////////////////////////////////////////////
|
|
int PPDirectoryTree::
|
|
get_num_children() const {
|
|
return _children.size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_child
|
|
// Access: Public
|
|
// Description: Returns the nth subdirectory below this level.
|
|
////////////////////////////////////////////////////////////////////
|
|
PPDirectoryTree *PPDirectoryTree::
|
|
get_child(int n) const {
|
|
assert(n >= 0 && n < (int)_children.size());
|
|
return _children[n];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_child_dirnames
|
|
// Access: Public
|
|
// Description: Returns a single string listing the names of all the
|
|
// subdirectories of this level, delimited by spaces.
|
|
//
|
|
// The list is sorted in dependency order such that a
|
|
// directory is listed after the other directories it
|
|
// might depend on.
|
|
////////////////////////////////////////////////////////////////////
|
|
string PPDirectoryTree::
|
|
get_child_dirnames() const {
|
|
Children copy_children = _children;
|
|
sort(copy_children.begin(), copy_children.end(),
|
|
SortDirectoriesByDependencyAndName());
|
|
|
|
vector<string> words;
|
|
Children::const_iterator ci;
|
|
for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
|
|
words.push_back((*ci)->get_dirname());
|
|
}
|
|
|
|
string result = repaste(words, " ");
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::get_complete_subtree
|
|
// Access: Public
|
|
// Description: Returns a single string listing the relative path
|
|
// from the source root to each source directory at this
|
|
// level and below, delimited by spaces.
|
|
////////////////////////////////////////////////////////////////////
|
|
string PPDirectoryTree::
|
|
get_complete_subtree() const {
|
|
Children copy_children = _children;
|
|
sort(copy_children.begin(), copy_children.end(),
|
|
SortDirectoriesByDependencyAndName());
|
|
|
|
vector<string> words;
|
|
words.push_back(get_path());
|
|
|
|
Children::const_iterator ci;
|
|
for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
|
|
words.push_back((*ci)->get_complete_subtree());
|
|
}
|
|
|
|
string result = repaste(words, " ");
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::r_scan
|
|
// Access: Private
|
|
// Description: The recursive implementation of scan().
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
r_scan(const string &prefix) {
|
|
string root_name = ".";
|
|
if (!prefix.empty()) {
|
|
root_name = prefix.substr(0, prefix.length() - 1);
|
|
}
|
|
|
|
DIR *root = opendir(root_name.c_str());
|
|
if (root == (DIR *)NULL) {
|
|
cerr << "Unable to scan directory " << root_name << "\n";
|
|
return false;
|
|
}
|
|
|
|
struct dirent *d;
|
|
d = readdir(root);
|
|
while (d != (struct dirent *)NULL) {
|
|
string dirname = d->d_name;
|
|
|
|
if (!dirname.empty() && dirname[0] != '.') {
|
|
// Do we have a source file in this subdirectory (if it is a
|
|
// subdirectory)?
|
|
string next_prefix = prefix + dirname + "/";
|
|
string source_filename = next_prefix + SOURCE_FILENAME;
|
|
if (access(source_filename.c_str(), F_OK) == 0) {
|
|
PPDirectoryTree *subtree = new PPDirectoryTree(dirname, this);
|
|
|
|
if (!subtree->r_scan(next_prefix)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
d = readdir(root);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::read_source_file
|
|
// Access: Private
|
|
// Description: Recursively reads in the source file at each level,
|
|
// if defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
read_source_file(const string &prefix, PPNamedScopes *named_scopes) {
|
|
string source_filename = prefix + SOURCE_FILENAME;
|
|
|
|
ifstream in(source_filename.c_str());
|
|
if (in) {
|
|
// cerr << "Reading " << source_filename << "\n";
|
|
|
|
named_scopes->set_current(_dirname);
|
|
_scope = named_scopes->make_scope("");
|
|
|
|
_scope->define_variable("SOURCEFILE", SOURCE_FILENAME);
|
|
_scope->define_variable("DIRNAME", _dirname);
|
|
_scope->define_variable("DIRPREFIX", prefix);
|
|
_scope->define_variable("PATH", get_path());
|
|
_scope->define_variable("SUBDIRS", get_child_dirnames());
|
|
_scope->define_variable("SUBTREE", get_complete_subtree());
|
|
_scope->set_directory(this);
|
|
|
|
_source = new PPCommandFile(_scope);
|
|
|
|
if (!_source->read_stream(in)) {
|
|
cerr << "Error when reading " << source_filename << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Children::iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
if (!(*ci)->read_source_file(prefix + (*ci)->get_dirname() + "/",
|
|
named_scopes)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::read_depends_file
|
|
// Access: Private
|
|
// Description: Recursively reads in the dependency definition file
|
|
// for each source file.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
read_depends_file(PPNamedScopes *named_scopes) {
|
|
if (_scope != (PPScope *)NULL) {
|
|
// Read the depends file, so we can determine the relationship
|
|
// between this source file and all of the other source files.
|
|
|
|
string depends_filename = _scope->expand_variable("DEPENDS_FILE");
|
|
if (depends_filename.empty()) {
|
|
cerr << "No definition given for $[DEPENDS_FILE], cannot process.\n";
|
|
return false;
|
|
}
|
|
|
|
named_scopes->set_current(_dirname);
|
|
PPCommandFile depends(_scope);
|
|
if (!depends.read_file(depends_filename)) {
|
|
cerr << "Error reading dependency definition file "
|
|
<< depends_filename << ".\n";
|
|
return false;
|
|
}
|
|
|
|
// This should have defined the variable DEPENDS, which lists the
|
|
// various dirnames this source file depends on.
|
|
|
|
vector<string> dirnames;
|
|
tokenize_whitespace(_scope->expand_variable("DEPENDS"), dirnames);
|
|
|
|
vector<string>::const_iterator ni;
|
|
for (ni = dirnames.begin(); ni != dirnames.end(); ++ni) {
|
|
const string &dirname = (*ni);
|
|
PPDirectoryTree *dir = find_dirname(dirname);
|
|
if (dir == (PPDirectoryTree *)NULL) {
|
|
cerr << "Could not find dependent dirname " << dirname << "\n";
|
|
} else {
|
|
if (dir != this) {
|
|
_i_depend_on.insert(dir);
|
|
dir->_depends_on_me.insert(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Children::iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
if (!(*ci)->read_depends_file(named_scopes)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::resolve_dependencies
|
|
// Access: Private
|
|
// Description: Visits each directory and assigns a correct
|
|
// _depends_index to each one, such that if directory A
|
|
// depends on directory B then A._depends_index >
|
|
// B._depends_index.
|
|
//
|
|
// This also detects cycles in the directory dependency
|
|
// graph.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
resolve_dependencies() {
|
|
if (!compute_depends_index()) {
|
|
return false;
|
|
}
|
|
|
|
Children::iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
if (!(*ci)->resolve_dependencies()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Now that we've resolved all of our children's dependencies,
|
|
// redefine our SUBDIRS and SUBTREE variables to put things in the
|
|
// right order.
|
|
if (_scope != (PPScope *)NULL) {
|
|
_scope->define_variable("SUBDIRS", get_child_dirnames());
|
|
_scope->define_variable("SUBTREE", get_complete_subtree());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PPDirectoryTree::compute_depends_index
|
|
// Access: Private
|
|
// Description: Computes the dependency score for a particular
|
|
// directory. See resolve_dependencies().
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PPDirectoryTree::
|
|
compute_depends_index() {
|
|
if (_depends_index != 0) {
|
|
return true;
|
|
}
|
|
|
|
if (_i_depend_on.empty()) {
|
|
_depends_index = 1;
|
|
return true;
|
|
}
|
|
|
|
_computing_depends_index = true;
|
|
int max_index = 0;
|
|
|
|
Depends::iterator di;
|
|
for (di = _i_depend_on.begin(); di != _i_depend_on.end(); ++di) {
|
|
if ((*di)->_computing_depends_index) {
|
|
// Oops, we have a cycle!
|
|
cerr << "Cycle detected in inter-directory dependencies!\n"
|
|
<< _dirname << " depends on " << (*di)->_dirname << "\n";
|
|
return false;
|
|
}
|
|
|
|
if (!(*di)->compute_depends_index()) {
|
|
// Keep reporting the cycle as we unroll the recursion.
|
|
cerr << _dirname << " depends on " << (*di)->_dirname << "\n";
|
|
return false;
|
|
}
|
|
|
|
max_index = max(max_index, (*di)->_depends_index);
|
|
}
|
|
|
|
_computing_depends_index = false;
|
|
_depends_index = max_index + 1;
|
|
|
|
return true;
|
|
}
|