Chaste Release::3.1
OutputFileHandler.cpp
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 }