Panda3D
|
00001 // Filename: softCVS.cxx 00002 // Created by: drose (10Nov00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "softCVS.h" 00016 00017 #include "pnotify.h" 00018 #include "multifile.h" 00019 #include "pystub.h" 00020 00021 #include <algorithm> 00022 00023 //////////////////////////////////////////////////////////////////// 00024 // Function: SoftCVS::Constructor 00025 // Access: Public 00026 // Description: 00027 //////////////////////////////////////////////////////////////////// 00028 SoftCVS:: 00029 SoftCVS() { 00030 _cvs_binary = "cvs"; 00031 00032 set_program_description 00033 ("softcvs is designed to prepare a directory hierarchy " 00034 "representing a SoftImage database for adding to CVS. " 00035 "First, it eliminates SoftImage's silly filename-based " 00036 "versioning system by renaming versioned filenames higher " 00037 "than 1-0 back to version 1-0. Then, it rolls up all the " 00038 "files for each scene except the texture images into a Panda " 00039 "multifile, which is added to CVS; the texture images are " 00040 "directly added to CVS where they are.\n\n" 00041 00042 "The reduction of hundreds of SoftImage files per scene down to one " 00043 "multifile and a handle of texture images should greatly improve " 00044 "the update and commit times of CVS.\n\n" 00045 00046 "You must run this from within the root of a SoftImage database " 00047 "directory; e.g. the directory that contains SCENES, PICTURES, MODELS, " 00048 "and so on."); 00049 00050 clear_runlines(); 00051 add_runline("[opts]"); 00052 00053 add_option 00054 ("nc", "", 80, 00055 "Do not attempt to add newly-created files to CVS. The default " 00056 "is to add them.", 00057 &SoftCVS::dispatch_none, &_no_cvs); 00058 00059 add_option 00060 ("cvs", "cvs_binary", 80, 00061 "Specify how to run the cvs program for adding newly-created files. " 00062 "The default is simply \"cvs\".", 00063 &SoftCVS::dispatch_string, NULL, &_cvs_binary); 00064 } 00065 00066 00067 //////////////////////////////////////////////////////////////////// 00068 // Function: SoftCVS::run 00069 // Access: Public 00070 // Description: 00071 //////////////////////////////////////////////////////////////////// 00072 void SoftCVS:: 00073 run() { 00074 // First, check for the scenes directory. If it doesn't exist, we 00075 // must not be in the root of a soft database. 00076 Filename scenes = "SCENES/."; 00077 if (!scenes.exists()) { 00078 nout << "No SCENES directory found; you are not in the root of a " 00079 "SoftImage database.\n"; 00080 exit(1); 00081 } 00082 00083 // Also, if we're expecting to use CVS, make sure the CVS directory 00084 // exists. 00085 Filename cvs_entries = "CVS/Entries"; 00086 if (!_no_cvs && !cvs_entries.exists()) { 00087 nout << "You do not appear to be within a CVS-controlled source " 00088 "directory.\n"; 00089 exit(1); 00090 } 00091 00092 // Scan all the files in the database. 00093 traverse_root(); 00094 00095 // Collapse out the higher-versioned scene files. 00096 collapse_scene_files(); 00097 00098 // Now determine which element files are actually referenced by at 00099 // least one of the scene files. 00100 if (!get_scenes()) { 00101 exit(1); 00102 } 00103 00104 // Finally, remove all the element files that are no longer 00105 // referenced by any scenes. 00106 remove_unused_elements(); 00107 00108 // Now do all the cvs adding and removing we need. 00109 if (!_no_cvs) { 00110 cvs_add_or_remove("remove", _cvs_remove); 00111 cvs_add_or_remove("add -kb", _cvs_add); 00112 } 00113 } 00114 00115 //////////////////////////////////////////////////////////////////// 00116 // Function: SoftCVS::traverse_root 00117 // Access: Private 00118 // Description: Reads all of the toplevel directory names, 00119 // e.g. SCENES, MATERIALS, etc., and traverses them. 00120 //////////////////////////////////////////////////////////////////// 00121 void SoftCVS:: 00122 traverse_root() { 00123 Filename root("."); 00124 00125 // Get the list of subdirectories. 00126 vector_string subdirs; 00127 if (!root.scan_directory(subdirs)) { 00128 nout << "Unable to scan directory.\n"; 00129 return; 00130 } 00131 00132 vector_string::const_iterator di; 00133 for (di = subdirs.begin(); di != subdirs.end(); ++di) { 00134 Filename subdir = (*di); 00135 if (subdir.is_directory() && subdir != "CVS") { 00136 traverse_subdir(subdir); 00137 } 00138 } 00139 } 00140 00141 //////////////////////////////////////////////////////////////////// 00142 // Function: SoftCVS::traverse_subdir 00143 // Access: Private 00144 // Description: Reads the directory indicated by prefix and 00145 // identifies all of the SoftImage files stored there. 00146 //////////////////////////////////////////////////////////////////// 00147 void SoftCVS:: 00148 traverse_subdir(const Filename &directory) { 00149 // Get the list of files in the directory. 00150 vector_string files; 00151 if (!directory.scan_directory(files)) { 00152 nout << "Unable to scan directory " << directory << "\n"; 00153 return; 00154 } 00155 00156 // We need to know the set of files in this directory that are CVS 00157 // elements. 00158 pset<string> cvs_elements; 00159 bool in_cvs = false; 00160 if (!_no_cvs) { 00161 in_cvs = scan_cvs(directory, cvs_elements); 00162 } 00163 00164 bool is_scenes = false; 00165 bool keep_all = false; 00166 bool wants_cvs = false; 00167 00168 // Now make some special-case behavior based on the particular 00169 // SoftImage subdirectory we're in. 00170 string dirname = directory.get_basename(); 00171 if (dirname == "SCENES") { 00172 is_scenes = true; 00173 00174 } else if (dirname == "CAMERAS") { 00175 // We don't want anything in the cameras directory. These may 00176 // change arbitrarily and have no bearing on the model or 00177 // animation that we will extract, so avoid them altogether. 00178 return; 00179 00180 } else if (dirname == "PICTURES") { 00181 // In the pictures directory, we must keep everything, since the 00182 // scene files don't explicitly reference these but they're still 00183 // important. Textures that are no longer used will pile up; we 00184 // leave this as the user's problem. 00185 00186 // We not only keep the textures, but we also move them into CVS, 00187 // since (again) they're not part of the scene files and thus 00188 // won't get added to the multifiles. Also, some textures are 00189 // shared between different scenes, and it would be wasteful to 00190 // add them to each scene multifile; furthermore, some scenes are 00191 // used for animation only, and we don't want to modify these 00192 // multifiles when the textures change. 00193 00194 keep_all = true; 00195 wants_cvs = !_no_cvs; 00196 } 00197 00198 vector_string::const_iterator fi; 00199 for (fi = files.begin(); fi != files.end(); ++fi) { 00200 const string &filename = (*fi); 00201 if (filename == "CVS") { 00202 // This special filename is not to be considered. 00203 00204 } else if (filename == "Chapter.rsrc") { 00205 // This special filename should not be considered, except to add 00206 // it to the multifiles. 00207 _global_files.push_back(Filename(directory, filename)); 00208 00209 } else { 00210 SoftFilename soft(directory, filename); 00211 00212 if (cvs_elements.count(filename) != 0) { 00213 // This file is known to be in CVS. 00214 soft.set_in_cvs(true); 00215 } 00216 00217 if (keep_all) { 00218 soft.increment_use_count(); 00219 } 00220 if (wants_cvs && !in_cvs) { 00221 // Try to CVSify the directory. 00222 cvs_add(directory); 00223 in_cvs = true; 00224 } 00225 soft.set_wants_cvs(wants_cvs); 00226 00227 if (is_scenes && soft.has_version() && soft.get_extension() == ".dsc") { 00228 _scene_files.push_back(soft); 00229 } else { 00230 _element_files.insert(soft); 00231 } 00232 } 00233 } 00234 } 00235 00236 //////////////////////////////////////////////////////////////////// 00237 // Function: SoftCVS::collapse_scene_files 00238 // Access: Private 00239 // Description: Walks through the list of scene files found, and 00240 // renames the higher-versioned ones to version 1-0, 00241 // removing the intervening versions. 00242 //////////////////////////////////////////////////////////////////// 00243 void SoftCVS:: 00244 collapse_scene_files() { 00245 // Get a copy of the scene files vector so we can modify it. Also 00246 // empty out the _scene_files at the same time so we can fill it up 00247 // again. 00248 SceneFiles versions; 00249 versions.swap(_scene_files); 00250 00251 // And sort them into order so we can easily compare higher and 00252 // lower versions. 00253 sort(versions.begin(), versions.end()); 00254 00255 SceneFiles::iterator vi; 00256 vi = versions.begin(); 00257 while (vi != versions.end()) { 00258 SoftFilename &file = (*vi); 00259 00260 if (!file.is_1_0()) { 00261 // Here's a file that needs to be renamed. But first, identify 00262 // all the other versions of the same file. 00263 SceneFiles::iterator start_vi; 00264 start_vi = vi; 00265 while (vi != versions.end() && (*vi).is_same_file(file)) { 00266 ++vi; 00267 } 00268 00269 rename_file(start_vi, vi); 00270 00271 } else { 00272 ++vi; 00273 } 00274 00275 file.make_1_0(); 00276 _scene_files.push_back(file); 00277 } 00278 } 00279 00280 //////////////////////////////////////////////////////////////////// 00281 // Function: SoftCVS::get_scenes 00282 // Access: Private 00283 // Description: Walks through the list of scene files and looks for 00284 // the set of element files referenced by each one, 00285 // updating multifile accordingly. 00286 //////////////////////////////////////////////////////////////////// 00287 bool SoftCVS:: 00288 get_scenes() { 00289 bool okflag = true; 00290 00291 // We will be added the multifiles to CVS if they're not already 00292 // added, so we have to know which files are in CVS already. 00293 pset<string> cvs_elements; 00294 if (!_no_cvs) { 00295 scan_cvs(".", cvs_elements); 00296 } 00297 00298 SceneFiles::const_iterator vi; 00299 for (vi = _scene_files.begin(); vi != _scene_files.end(); ++vi) { 00300 const SoftFilename &sf = (*vi); 00301 Filename file(sf.get_dirname(), sf.get_filename()); 00302 00303 file.set_text(); 00304 ifstream in; 00305 if (!file.open_read(in)) { 00306 nout << "Unable to read " << file << "\n"; 00307 } else { 00308 nout << "Scanning " << file << "\n"; 00309 00310 Multifile multifile; 00311 Filename multifile_name = sf.get_base() + "mf"; 00312 00313 if (!multifile.open_read_write(multifile_name)) { 00314 nout << "Unable to open " << multifile_name << " for updating.\n"; 00315 okflag = false; 00316 00317 } else { 00318 if (!scan_scene_file(in, multifile)) { 00319 okflag = false; 00320 } 00321 00322 // Add all the global files to the multifile too. These 00323 // probably can't take compression (since in SoftImage they're 00324 // just the Chapter.rsrc files, each very tiny). 00325 vector_string::const_iterator gi; 00326 for (gi = _global_files.begin(); gi != _global_files.end(); ++gi) { 00327 if (multifile.update_subfile((*gi), (*gi), 0).empty()) { 00328 nout << "Unable to add " << (*gi) << "\n"; 00329 okflag = false; 00330 } 00331 } 00332 00333 // Also add the scene file itself. 00334 if (multifile.update_subfile(file, file, 6).empty()) { 00335 nout << "Unable to add " << file << "\n"; 00336 okflag = false; 00337 } 00338 00339 bool flushed = false; 00340 if (multifile.needs_repack()) { 00341 flushed = multifile.repack(); 00342 } else { 00343 flushed = multifile.flush(); 00344 } 00345 if (!flushed) { 00346 nout << "Failed to write " << multifile_name << ".\n"; 00347 okflag = false; 00348 } else { 00349 nout << "Wrote " << multifile_name << ".\n"; 00350 00351 if (!_no_cvs && cvs_elements.count(multifile_name) == 0) { 00352 // Add the multifile to CVS. 00353 _cvs_add.push_back(multifile_name); 00354 } 00355 } 00356 } 00357 } 00358 } 00359 00360 return okflag; 00361 } 00362 00363 00364 //////////////////////////////////////////////////////////////////// 00365 // Function: SoftCVS::remove_unused_elements 00366 // Access: Private 00367 // Description: Remove all the element files that weren't referenced 00368 // by any scene file. Also plan to cvs add all those 00369 // that were referenced. 00370 //////////////////////////////////////////////////////////////////// 00371 void SoftCVS:: 00372 remove_unused_elements() { 00373 ElementFiles::const_iterator fi; 00374 for (fi = _element_files.begin(); fi != _element_files.end(); ++fi) { 00375 const SoftFilename &sf = (*fi); 00376 Filename file(sf.get_dirname(), sf.get_filename()); 00377 00378 if (sf.get_use_count() == 0) { 00379 nout << file << " is unused.\n"; 00380 00381 if (!file.unlink()) { 00382 nout << "Unable to remove " << file << ".\n"; 00383 00384 } else if (sf.get_in_cvs()) { 00385 _cvs_remove.push_back(file); 00386 } 00387 00388 } else if (sf.get_wants_cvs() && !sf.get_in_cvs()) { 00389 _cvs_add.push_back(file); 00390 } 00391 } 00392 } 00393 00394 00395 //////////////////////////////////////////////////////////////////// 00396 // Function: SoftCVS::rename_file 00397 // Access: Private 00398 // Description: Renames the first file in the indicated list to a 00399 // version 1-0 filename, superceding all the other files 00400 // in the list. Returns true if the file is renamed, 00401 // false otherwise. 00402 //////////////////////////////////////////////////////////////////// 00403 bool SoftCVS:: 00404 rename_file(SoftCVS::SceneFiles::iterator begin, 00405 SoftCVS::SceneFiles::iterator end) { 00406 int length = end - begin; 00407 nassertr(length > 0, false); 00408 00409 SoftFilename &orig = (*begin); 00410 00411 string dirname = orig.get_dirname(); 00412 string source_filename = orig.get_filename(); 00413 string dest_filename = orig.get_1_0_filename(); 00414 00415 if (length > 2) { 00416 nout << source_filename << " supercedes:\n"; 00417 SceneFiles::const_iterator p; 00418 for (p = begin + 1; p != end; ++p) { 00419 nout << " " << (*p).get_filename() << "\n"; 00420 } 00421 00422 } else if (length == 2) { 00423 nout << source_filename << " supercedes " 00424 << (*(begin + 1)).get_filename() << ".\n"; 00425 00426 } else { 00427 nout << source_filename << " renamed.\n"; 00428 } 00429 00430 // Now remove all of the "wrong" files. 00431 00432 SceneFiles::const_iterator p; 00433 for (p = begin + 1; p != end; ++p) { 00434 Filename file((*p).get_dirname(), (*p).get_filename()); 00435 if (!file.unlink()) { 00436 nout << "Unable to remove " << file << ".\n"; 00437 } 00438 } 00439 00440 // And rename the good one. 00441 Filename source(dirname, source_filename); 00442 Filename dest(dirname, dest_filename); 00443 00444 if (!source.rename_to(dest)) { 00445 nout << "Unable to rename " << source << " to " << dest_filename << ".\n"; 00446 exit(1); 00447 } 00448 00449 return true; 00450 } 00451 00452 //////////////////////////////////////////////////////////////////// 00453 // Function: SoftCVS::scan_cvs 00454 // Access: Private 00455 // Description: Scans the CVS repository in the indicated directory 00456 // to determine which files are already versioned 00457 // elements. Returns true if the directory is 00458 // CVS-controlled, false otherwise. 00459 //////////////////////////////////////////////////////////////////// 00460 bool SoftCVS:: 00461 scan_cvs(const string &dirname, pset<string> &cvs_elements) { 00462 Filename cvs_entries = dirname + "/CVS/Entries"; 00463 if (!cvs_entries.exists()) { 00464 return false; 00465 } 00466 00467 ifstream in; 00468 cvs_entries.set_text(); 00469 if (!cvs_entries.open_read(in)) { 00470 nout << "Unable to read CVS directory.\n"; 00471 return true; 00472 } 00473 00474 string line; 00475 getline(in, line); 00476 while (!in.fail() && !in.eof()) { 00477 if (!line.empty() && line[0] == '/') { 00478 size_t slash = line.find('/', 1); 00479 if (slash != string::npos) { 00480 string filename = line.substr(1, slash - 1); 00481 00482 if (line.substr(slash + 1, 2) == "-1") { 00483 // If the first number after the slash is -1, the file used 00484 // to be here but was recently cvs removed. It counts as no 00485 // longer being an element. 00486 } else { 00487 cvs_elements.insert(filename); 00488 } 00489 } 00490 } 00491 00492 getline(in, line); 00493 } 00494 00495 return true; 00496 } 00497 00498 //////////////////////////////////////////////////////////////////// 00499 // Function: SoftCVS::scan_scene_file 00500 // Access: Private 00501 // Description: Reads a scene file, looking for references to element 00502 // files. For each reference found, increments the 00503 // appropriate element file's reference count. 00504 //////////////////////////////////////////////////////////////////// 00505 bool SoftCVS:: 00506 scan_scene_file(istream &in, Multifile &multifile) { 00507 bool okflag = true; 00508 00509 int c = in.get(); 00510 while (!in.eof() && !in.fail()) { 00511 // Skip whitespace. 00512 while (isspace(c) && !in.eof() && !in.fail()) { 00513 c = in.get(); 00514 } 00515 00516 // Now begin a word. 00517 string word; 00518 while (!isspace(c) && !in.eof() && !in.fail()) { 00519 word += c; 00520 c = in.get(); 00521 } 00522 00523 if (!word.empty()) { 00524 SoftFilename v("", word); 00525 00526 // Increment the use count on all matching elements of the multiset. 00527 pair<ElementFiles::iterator, ElementFiles::iterator> range; 00528 range = _element_files.equal_range(v); 00529 00530 ElementFiles::iterator ei; 00531 for (ei = range.first; ei != range.second; ++ei) { 00532 // We cheat and get a non-const reference to the filename out 00533 // of the set. We can safely do this because incrementing the 00534 // use count won't change its position in the set. 00535 SoftFilename &sf = (SoftFilename &)(*ei); 00536 sf.increment_use_count(); 00537 00538 Filename file(sf.get_dirname(), sf.get_filename()); 00539 if (multifile.update_subfile(file, file, 6).empty()) { 00540 nout << "Unable to add " << file << "\n"; 00541 okflag = false; 00542 } 00543 } 00544 } 00545 } 00546 00547 return okflag; 00548 } 00549 00550 //////////////////////////////////////////////////////////////////// 00551 // Function: SoftCVS::cvs_add 00552 // Access: Private 00553 // Description: Invokes CVS to add just the named file to the 00554 // repository. Returns true on success, false on 00555 // failure. 00556 //////////////////////////////////////////////////////////////////// 00557 bool SoftCVS:: 00558 cvs_add(const string &path) { 00559 string command = _cvs_binary + " add -kb " + path; 00560 nout << command << "\n"; 00561 int result = system(command.c_str()); 00562 00563 if (result != 0) { 00564 nout << "Failure invoking cvs.\n"; 00565 return false; 00566 } 00567 return true; 00568 } 00569 00570 //////////////////////////////////////////////////////////////////// 00571 // Function: SoftCVS::cvs_add_or_remove 00572 // Access: Private 00573 // Description: Invokes CVS to add (or remove) all of the files in 00574 // the indicated vector. Returns true on success, false 00575 // on failure. 00576 //////////////////////////////////////////////////////////////////// 00577 bool SoftCVS:: 00578 cvs_add_or_remove(const string &cvs_command, const vector_string &paths) { 00579 static const int max_command = 4096; 00580 00581 if (!paths.empty()) { 00582 string command = _cvs_binary + " " + cvs_command; 00583 vector_string::const_iterator pi; 00584 pi = paths.begin(); 00585 while (pi != paths.end()) { 00586 const string &path = (*pi); 00587 00588 if ((int)command.length() + 1 + (int)path.length() >= max_command) { 00589 // Fire off the command now. 00590 nout << command << "\n"; 00591 int result = system(command.c_str()); 00592 00593 if (result != 0) { 00594 nout << "Failure invoking cvs.\n"; 00595 return false; 00596 } 00597 00598 command = _cvs_binary + " " + cvs_command; 00599 } 00600 00601 command += ' '; 00602 command += path; 00603 00604 ++pi; 00605 } 00606 nout << command << "\n"; 00607 int result = system(command.c_str()); 00608 00609 if (result != 0) { 00610 nout << "Failure invoking cvs.\n"; 00611 return false; 00612 } 00613 } 00614 return true; 00615 } 00616 00617 00618 int main(int argc, char *argv[]) { 00619 // A call to pystub() to force libpystub.so to be linked in. 00620 pystub(); 00621 00622 SoftCVS prog; 00623 prog.parse_command_line(argc, argv); 00624 prog.run(); 00625 return 0; 00626 }