OutputFileHandler.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 void CleanFolder(const fs::path& rPath, bool isTop)
00060 {
00061     assert(fs::is_directory(rPath));
00062     fs::directory_iterator end_iter;
00063     // First recursively remove the children
00064     for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
00065     {
00066         if (fs::is_directory(dir_iter->status()))
00067         {
00068             CleanFolder(dir_iter->path(), false);
00069         }
00070         else
00071         {
00072             const fs::path& r_item_path(dir_iter->path());
00073             if (!isTop || PATH_LEAF_NAME(r_item_path)[0] != '.')
00074             {
00075                 fs::remove(r_item_path);
00076             }
00077         }
00078     }
00079     // Now remove the folder itself, if not top
00080     if (!isTop)
00081     {
00082         fs::remove(rPath);
00083     }
00084 }
00085 
00086 
00087 OutputFileHandler::OutputFileHandler(const std::string& rDirectory,
00088                                      bool cleanOutputDirectory)
00089 {
00090     CommonConstructor(rDirectory, cleanOutputDirectory);
00091 }
00092 
00093 
00094 OutputFileHandler::OutputFileHandler(const FileFinder& rDirectory,
00095                                      bool cleanOutputDirectory)
00096 {
00097     FileFinder output_root("", RelativeTo::ChasteTestOutput);
00098     std::string relative_path;
00099     try
00100     {
00101         relative_path = rDirectory.GetRelativePath(output_root);
00102     }
00103     catch (const Exception&)
00104     {
00105         EXCEPTION("The location provided to OutputFileHandler must be inside CHASTE_TEST_OUTPUT; '"
00106                   << rDirectory.GetAbsolutePath() << "' is not under '"
00107                   << output_root.GetAbsolutePath() << "'.");
00108     }
00109     if (*output_root.GetAbsolutePath().rbegin() != '/' && !relative_path.empty())
00110     {
00111         assert(*relative_path.begin() == '/');
00112         relative_path.erase(0, 1); // Remove leading slash
00113     }
00114     CommonConstructor(relative_path, cleanOutputDirectory);
00115 }
00116 
00117 
00118 void OutputFileHandler::CommonConstructor(const std::string &rDirectory,
00119                                           bool cleanOutputDirectory)
00120 {
00121     // Is it a valid request for a directory?
00122     if (rDirectory.find("..") != std::string::npos)
00123     {
00124         EXCEPTION("Will not create directory: " + rDirectory +
00125                   " due to it potentially being above, and cleaning, CHASTE_TEST_OUTPUT.");
00126         // Note: in Boost 1.48 and above we could use 'canonical' to check this better
00127     }
00128     //The notion of absolute path on Windows is rather different.
00129     //For example, / and /foo are not absolute paths.
00130     //However, fs::path.has_root_path() captures the intended semantics here as follows
00131 
00132     if (fs::path(rDirectory).has_root_path())
00133     {
00134         EXCEPTION("The constructor argument to OutputFileHandler must be a relative path; '"
00135                   << rDirectory << "' is absolute.");
00136     }
00137 
00138     mDirectory = MakeFoldersAndReturnFullPath(rDirectory);
00139 
00140     // Clean the directory (default)
00141     if (rDirectory != "" && cleanOutputDirectory) // Clean directory but don't ever clean CHASTE_TEST_OUTPUT at the top level
00142     {
00143         FileFinder signature_file(mDirectory + SIG_FILE_NAME, RelativeTo::Absolute);
00144         if (!signature_file.Exists())
00145         {
00146             EXCEPTION("Cannot delete " + mDirectory + " because signature file \"" + SIG_FILE_NAME + "\" is not present.");
00147         }
00148 
00149         // Are we the master process? Only the master should delete files
00150         if (PetscTools::AmMaster())
00151         {
00152             CleanFolder(mDirectory);
00153         }
00154         // Wait for master to finish before going on to use the directory.
00155         PetscTools::Barrier("OutputFileHandler");
00156     }
00157 }
00158 
00159 std::string OutputFileHandler::GetChasteTestOutputDirectory()
00160 {
00161     char *chaste_test_output = getenv("CHASTE_TEST_OUTPUT");
00162     FileFinder directory_root;
00163     if (chaste_test_output == NULL || *chaste_test_output == 0)
00164     {
00165         // Default to 'testoutput' folder within the current directory
00166         directory_root.SetPath("testoutput", RelativeTo::CWD);
00167     }
00168     else
00169     {
00170         directory_root.SetPath(chaste_test_output, RelativeTo::AbsoluteOrCwd);
00171     }
00172     // Note that FileFinder::GetAbsolutePath adds a trailing slash, but only
00173     // if the directory exists at the time of the call
00174     std::string chaste_test_output_directory = directory_root.GetAbsolutePath();
00175     AddTrailingSlash(chaste_test_output_directory);
00176     return chaste_test_output_directory;
00177 }
00178 
00179 std::string OutputFileHandler::MakeFoldersAndReturnFullPath(const std::string& rDirectory) const
00180 {
00181     fs::path output_root(GetChasteTestOutputDirectory());
00182     fs::path rel_path(rDirectory);
00183 
00184     if (!rel_path.empty() && (*(--rel_path.end())) == ".")
00185     {
00186         // rDirectory has a trailing slash, which gives an unhelpful last component
00187         rel_path.remove_leaf();
00188     }
00189 
00190     // Are we the master process? Only the master should make any new directories
00191     if (PetscTools::AmMaster())
00192     {
00193         try
00194         {
00195             // If necessary make the ChasteTestOutputDirectory - don't make it deleteable by Chaste
00196             fs::create_directories(output_root); // Note that this is a no-op if the folder exists already
00197 
00198             // Now make all the sub-folders requested one-by-one and add the .chaste_deletable_folder file to them
00199             fs::path next_folder(output_root);
00200             for (fs::path::iterator path_iter = rel_path.begin(); path_iter != rel_path.end(); ++path_iter)
00201             {
00202                 next_folder /= *path_iter;
00203                 bool created_dir = fs::create_directory(next_folder);
00204                 if (created_dir)
00205                 {
00206                     // Add the Chaste signature file
00207                     fs::ofstream sig_file(next_folder / SIG_FILE_NAME);
00208                     sig_file.close();
00209                 }
00210             }
00211         }
00212         catch (const fs::filesystem_error& e)
00213         {
00214             TERMINATE("Error making test output folder: " << e.what());
00215         }
00216     }
00217 
00218     // Wait for master to finish before going on to use the directory.
00219     PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPath");
00220 
00221     std::string path_with_slash = (output_root / rel_path).string();
00222     AddTrailingSlash(path_with_slash);
00223     return path_with_slash;
00224 }
00225 
00226 std::string OutputFileHandler::GetOutputDirectoryFullPath() const
00227 {
00228     return mDirectory;
00229 }
00230 
00231 std::string OutputFileHandler::GetRelativePath() const
00232 {
00233     FileFinder output_root("", RelativeTo::ChasteTestOutput);
00234     std::string relative_path = FindFile("").GetRelativePath(output_root);
00235     if (!relative_path.empty() && *relative_path.rbegin() == '/')
00236     {
00237         relative_path.erase(--relative_path.end()); // Remove trailing slash
00238     }
00239     return relative_path;
00240 }
00241 
00242 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
00243                                              std::ios_base::openmode mode) const
00244 {
00245     out_stream p_output_file(new std::ofstream((mDirectory+rFileName).c_str(), mode));
00246     if (!p_output_file->is_open())
00247     {
00248         EXCEPTION("Could not open file \"" + rFileName + "\" in " + mDirectory);
00249     }
00250     return p_output_file;
00251 }
00252 
00253 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
00254                                              unsigned number,
00255                                              const std::string& rFileFormat,
00256                                              std::ios_base::openmode mode) const
00257 {
00258     std::stringstream string_stream;
00259     string_stream << rFileName << number << rFileFormat;
00260     return OpenOutputFile(string_stream.str(), mode);
00261 }
00262 
00263 void OutputFileHandler::SetArchiveDirectory() const
00264 {
00265     FileFinder dir(GetOutputDirectoryFullPath(), RelativeTo::Absolute);
00266     ArchiveLocationInfo::SetArchiveDirectory(dir);
00267 }
00268 
00269 void OutputFileHandler::AddTrailingSlash(std::string& rDirectory)
00270 {
00271     // Add a trailing slash if not already there
00272     if (rDirectory!="" && !( *(rDirectory.end()-1) == '/'))
00273     {
00274         rDirectory = rDirectory + "/";
00275     }
00276 }
00277 
00278 FileFinder OutputFileHandler::CopyFileTo(const FileFinder& rSourceFile) const
00279 {
00280     if (!rSourceFile.IsFile())
00281     {
00282         EXCEPTION("Can only copy single files:\n" << rSourceFile.GetAbsolutePath() << " is not a file.");
00283     }
00284     fs::path from_path(rSourceFile.GetAbsolutePath());
00285     fs::path to_path(GetOutputDirectoryFullPath());
00286     to_path /= from_path.leaf();
00287     if (PetscTools::AmMaster())
00288     {
00289         try
00290         {
00291             fs::copy_file(from_path, to_path);
00292         }
00293         catch (const fs::filesystem_error& e)
00294         {
00295             TERMINATE("Error copying file '" << rSourceFile.GetAbsolutePath() << "': " << e.what());
00296         }
00297     }
00298     PetscTools::Barrier("OutputFileHandler::CopyFileTo");
00299     return FileFinder(to_path.string(), RelativeTo::Absolute);
00300 }
00301 
00302 FileFinder OutputFileHandler::FindFile(std::string leafName) const
00303 {
00304     return FileFinder(GetOutputDirectoryFullPath() + leafName, RelativeTo::Absolute);
00305 }

Generated by  doxygen 1.6.2