Chaste Release::3.1
|
00001 /* 00002 00003 Copyright (c) 2005-2012, University of Oxford. 00004 All rights reserved. 00005 00006 University of Oxford means the Chancellor, Masters and Scholars of the 00007 University of Oxford, having an administrative office at Wellington 00008 Square, Oxford OX1 2JD, UK. 00009 00010 This file is part of Chaste. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 * Redistributions of source code must retain the above copyright notice, 00015 this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright notice, 00017 this list of conditions and the following disclaimer in the documentation 00018 and/or other materials provided with the distribution. 00019 * Neither the name of the University of Oxford nor the names of its 00020 contributors may be used to endorse or promote products derived from this 00021 software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 */ 00035 00036 #include "OutputFileHandler.hpp" 00037 00038 #include <cstdlib> 00039 00040 #include "ArchiveLocationInfo.hpp" 00041 #include "BoostFilesystem.hpp" 00042 #include "Exception.hpp" 00043 #include "FileFinder.hpp" 00044 #include "GetCurrentWorkingDirectory.hpp" 00045 #include "PetscTools.hpp" 00046 00047 00048 const std::string OutputFileHandler::SIG_FILE_NAME(".chaste_deletable_folder"); 00049 00057 void CleanFolder(const fs::path& rPath, bool isTop=true) 00058 { 00059 assert(fs::is_directory(rPath)); 00060 fs::directory_iterator end_iter; 00061 // First recursively remove the children 00062 for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter) 00063 { 00064 if (fs::is_directory(dir_iter->status())) 00065 { 00066 CleanFolder(dir_iter->path(), false); 00067 } 00068 else 00069 { 00070 const fs::path& r_item_path(dir_iter->path()); 00071 if (!isTop || r_item_path.leaf()[0] != '.') 00072 { 00073 fs::remove(r_item_path); 00074 } 00075 } 00076 } 00077 // Now remove the folder itself, if not top 00078 if (!isTop) 00079 { 00080 fs::remove(rPath); 00081 } 00082 } 00083 00084 00085 OutputFileHandler::OutputFileHandler(const std::string& rDirectory, 00086 bool cleanOutputDirectory) 00087 { 00088 CommonConstructor(rDirectory, cleanOutputDirectory); 00089 } 00090 00091 00092 OutputFileHandler::OutputFileHandler(const FileFinder& rDirectory, 00093 bool cleanOutputDirectory) 00094 { 00095 FileFinder output_root("", RelativeTo::ChasteTestOutput); 00096 std::string relative_path; 00097 try 00098 { 00099 relative_path = rDirectory.GetRelativePath(output_root); 00100 } 00101 catch (const Exception& e) 00102 { 00103 EXCEPTION("The location provided to OutputFileHandler must be inside CHASTE_TEST_OUTPUT; '" 00104 << rDirectory.GetAbsolutePath() << "' is not under '" 00105 << output_root.GetAbsolutePath() << "'."); 00106 } 00107 CommonConstructor(relative_path, cleanOutputDirectory); 00108 } 00109 00110 00111 void OutputFileHandler::CommonConstructor(const std::string &rDirectory, 00112 bool cleanOutputDirectory) 00113 { 00114 // Is it a valid request for a directory? 00115 if (rDirectory.find("..") != std::string::npos) 00116 { 00117 EXCEPTION("Will not create directory: " + rDirectory + 00118 " due to it potentially being above, and cleaning, CHASTE_TEST_OUTPUT."); 00119 // Note: in Boost 1.48 and above we could use 'canonical' to check this better 00120 } 00121 if (FileFinder::IsAbsolutePath(rDirectory)) 00122 { 00123 EXCEPTION("The constructor argument to OutputFileHandler must be a relative path; '" 00124 << rDirectory << "' is absolute."); 00125 } 00126 00127 mDirectory = MakeFoldersAndReturnFullPath(rDirectory); 00128 00129 // Clean the directory (default) 00130 if (rDirectory != "" && cleanOutputDirectory) // Don't clean CHASTE_TEST_OUTPUT 00131 { 00132 FileFinder signature_file(mDirectory + SIG_FILE_NAME, RelativeTo::Absolute); 00133 if (!signature_file.Exists()) 00134 { 00135 EXCEPTION("Cannot delete " + mDirectory + " because signature file \"" + SIG_FILE_NAME + "\" is not present."); 00136 } 00137 00138 // Are we the master process? Only the master should delete files 00139 if (PetscTools::AmMaster()) 00140 { 00141 CleanFolder(mDirectory); 00142 } 00143 // Wait for master to finish before going on to use the directory. 00144 PetscTools::Barrier("OutputFileHandler"); 00145 } 00146 } 00147 00148 std::string OutputFileHandler::GetChasteTestOutputDirectory() 00149 { 00150 char *chaste_test_output = getenv("CHASTE_TEST_OUTPUT"); 00151 FileFinder directory_root; 00152 if (chaste_test_output == NULL || *chaste_test_output == 0) 00153 { 00154 // Default to 'testoutput' folder within the current directory 00155 directory_root.SetPath("testoutput", RelativeTo::CWD); 00156 } 00157 else 00158 { 00159 directory_root.SetPath(chaste_test_output, RelativeTo::AbsoluteOrCwd); 00160 } 00161 00162 return directory_root.GetAbsolutePath(); 00163 } 00164 00165 std::string OutputFileHandler::MakeFoldersAndReturnFullPath(const std::string& rDirectory) const 00166 { 00167 fs::path output_root(GetChasteTestOutputDirectory()); 00168 fs::path rel_path(rDirectory); 00169 00170 if (!rel_path.empty() && (*(--rel_path.end())) == ".") 00171 { 00172 // rDirectory has a trailing slash, which gives an unhelpful last component 00173 rel_path.remove_leaf(); 00174 } 00175 00176 // Are we the master process? Only the master should make any new directories 00177 if (PetscTools::AmMaster()) 00178 { 00179 try 00180 { 00181 // If necessary make the ChasteTestOutputDirectory - don't make it deleteable by Chaste 00182 if (!fs::exists(output_root)) 00183 { 00184 fs::create_directories(output_root); 00185 } 00186 00187 // Now make all the sub-folders requested one-by-one and add the .chaste_deletable_folder file to them 00188 fs::path next_folder(output_root); 00189 for (fs::path::iterator path_iter = rel_path.begin(); path_iter != rel_path.end(); ++path_iter) 00190 { 00191 next_folder /= *path_iter; 00192 if (!fs::is_directory(next_folder)) 00193 { 00194 fs::create_directory(next_folder); 00195 // Add the Chaste signature file 00196 fs::ofstream sig_file(next_folder / SIG_FILE_NAME); 00197 } 00198 } 00199 } 00200 catch (const fs::filesystem_error& e) 00201 { 00202 TERMINATE("Error making test output folder: " << e.what()); 00203 } 00204 } 00205 00206 // Wait for master to finish before going on to use the directory. 00207 PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPath"); 00208 00209 std::string path_with_slash = (output_root / rel_path).string(); 00210 AddTrailingSlash(path_with_slash); 00211 return path_with_slash; 00212 } 00213 00214 std::string OutputFileHandler::GetOutputDirectoryFullPath() const 00215 { 00216 return mDirectory; 00217 } 00218 00219 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName, 00220 std::ios_base::openmode mode) const 00221 { 00222 out_stream p_output_file(new std::ofstream((mDirectory+rFileName).c_str(), mode)); 00223 if (!p_output_file->is_open()) 00224 { 00225 EXCEPTION("Could not open file \"" + rFileName + "\" in " + mDirectory); 00226 } 00227 return p_output_file; 00228 } 00229 00230 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName, 00231 unsigned number, 00232 const std::string& rFileFormat, 00233 std::ios_base::openmode mode) const 00234 { 00235 std::stringstream string_stream; 00236 string_stream << rFileName << number << rFileFormat; 00237 return OpenOutputFile(string_stream.str(), mode); 00238 } 00239 00240 void OutputFileHandler::SetArchiveDirectory() const 00241 { 00242 FileFinder dir(GetOutputDirectoryFullPath(), RelativeTo::Absolute); 00243 ArchiveLocationInfo::SetArchiveDirectory(dir); 00244 } 00245 00246 void OutputFileHandler::AddTrailingSlash(std::string& rDirectory) 00247 { 00248 // Add a trailing slash if not already there 00249 if (rDirectory!="" && !( *(rDirectory.end()-1) == '/')) 00250 { 00251 rDirectory = rDirectory + "/"; 00252 } 00253 } 00254 00255 FileFinder OutputFileHandler::CopyFileTo(const FileFinder& rSourceFile) const 00256 { 00257 if (!rSourceFile.IsFile()) 00258 { 00259 EXCEPTION("Can only copy single files:\n" << rSourceFile.GetAbsolutePath() << " is not a file."); 00260 } 00261 fs::path from_path(rSourceFile.GetAbsolutePath()); 00262 fs::path to_path(GetOutputDirectoryFullPath()); 00263 to_path /= from_path.leaf(); 00264 if (PetscTools::AmMaster()) 00265 { 00266 try 00267 { 00268 fs::copy_file(from_path, to_path); 00269 } 00270 catch (const fs::filesystem_error& e) 00271 { 00272 TERMINATE("Error copying file '" << rSourceFile.GetAbsolutePath() << "': " << e.what()); 00273 } 00274 } 00275 PetscTools::Barrier("OutputFileHandler::CopyFileTo"); 00276 return FileFinder(to_path.string(), RelativeTo::Absolute); 00277 } 00278 00279 FileFinder OutputFileHandler::FindFile(std::string leafName) const 00280 { 00281 return FileFinder(GetOutputDirectoryFullPath() + leafName, RelativeTo::Absolute); 00282 }