Chaste  Release::2017.1
OutputFileHandler.cpp
1 /*
2 
3 Copyright (c) 2005-2017, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "OutputFileHandler.hpp"
37 
38 #include <cstdlib>
39 #include <fstream>
40 #include <sstream>
41 
42 #include "ArchiveLocationInfo.hpp"
43 #include "BoostFilesystem.hpp"
44 #include "Exception.hpp"
45 #include "FileFinder.hpp"
46 #include "GetCurrentWorkingDirectory.hpp"
47 #include "PetscTools.hpp"
48 
49 
50 const std::string OutputFileHandler::SIG_FILE_NAME(".chaste_deletable_folder");
51 
59 void CleanFolder(const fs::path& rPath, bool isTop=true);
60 
61 void CleanFolder(const fs::path& rPath, bool isTop)
62 {
63  assert(fs::is_directory(rPath));
64  fs::directory_iterator end_iter;
65  // First recursively remove the children
66  for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
67  {
68  if (fs::is_directory(dir_iter->status()))
69  {
70  CleanFolder(dir_iter->path(), false);
71  }
72  else
73  {
74  const fs::path& r_item_path(dir_iter->path());
75  if (!isTop || PATH_LEAF_NAME(r_item_path)[0] != '.')
76  {
77  fs::remove(r_item_path);
78  }
79  }
80  }
81  // Now remove the folder itself, if not top
82  if (!isTop)
83  {
84  fs::remove(rPath);
85  }
86 }
87 
88 
89 OutputFileHandler::OutputFileHandler(const std::string& rDirectory,
90  bool cleanOutputDirectory)
91 {
92  CommonConstructor(rDirectory, cleanOutputDirectory);
93 }
94 
95 
97  bool cleanOutputDirectory)
98 {
100  std::string relative_path;
101  try
102  {
103  relative_path = rDirectory.GetRelativePath(output_root);
104  }
105  catch (const Exception&)
106  {
107  EXCEPTION("The location provided to OutputFileHandler must be inside CHASTE_TEST_OUTPUT; '"
108  << rDirectory.GetAbsolutePath() << "' is not under '"
109  << output_root.GetAbsolutePath() << "'.");
110  }
111  if (*output_root.GetAbsolutePath().rbegin() != '/' && !relative_path.empty())
112  {
113  assert(*relative_path.begin() == '/');
114  relative_path.erase(0, 1); // Remove leading slash
115  }
116  CommonConstructor(relative_path, cleanOutputDirectory);
117 }
118 
119 
120 void OutputFileHandler::CommonConstructor(const std::string& rDirectory,
121  bool cleanOutputDirectory)
122 {
123  // Is it a valid request for a directory?
124  if (rDirectory.find("..") != std::string::npos)
125  {
126  EXCEPTION("Will not create directory: " + rDirectory +
127  " due to it potentially being above, and cleaning, CHASTE_TEST_OUTPUT.");
128  // Note: in Boost 1.48 and above we could use 'canonical' to check this better
129  }
130  //The notion of absolute path on Windows is rather different.
131  //For example, / and /foo are not absolute paths.
132  //However, fs::path.has_root_path() captures the intended semantics here as follows
133 
134  if (fs::path(rDirectory).has_root_path())
135  {
136  EXCEPTION("The constructor argument to OutputFileHandler must be a relative path; '"
137  << rDirectory << "' is absolute.");
138  }
139 
141 
142  // Clean the directory (default)
143  if (rDirectory != "" && cleanOutputDirectory) // Clean directory but don't ever clean CHASTE_TEST_OUTPUT at the top level
144  {
146  if (!signature_file.Exists())
147  {
148  EXCEPTION("Cannot delete " + mDirectory + " because signature file \"" + SIG_FILE_NAME + "\" is not present.");
149  }
150 
151  // Are we the master process? Only the master should delete files
152  if (PetscTools::AmMaster())
153  {
154  CleanFolder(mDirectory);
155  }
156  // Wait for master to finish before going on to use the directory.
157  PetscTools::Barrier("OutputFileHandler");
158  }
159 }
160 
162 {
163  char *chaste_test_output = getenv("CHASTE_TEST_OUTPUT");
164  FileFinder directory_root;
165  if (chaste_test_output == nullptr || *chaste_test_output == 0)
166  {
167  // Default to 'testoutput' folder within the current directory
168  directory_root.SetPath("testoutput", RelativeTo::CWD);
169  }
170  else
171  {
172  directory_root.SetPath(chaste_test_output, RelativeTo::AbsoluteOrCwd);
173  }
174  // Note that FileFinder::GetAbsolutePath adds a trailing slash, but only
175  // if the directory exists at the time of the call
176  std::string chaste_test_output_directory = directory_root.GetAbsolutePath();
177  AddTrailingSlash(chaste_test_output_directory);
178  return chaste_test_output_directory;
179 }
180 
181 std::string OutputFileHandler::MakeFoldersAndReturnFullPath(const std::string& rDirectory) const
182 {
183  fs::path output_root(GetChasteTestOutputDirectory());
184  fs::path rel_path(rDirectory);
185 
186  if (!rel_path.empty() && (*(--rel_path.end())) == ".")
187  {
188  // rDirectory has a trailing slash, which gives an unhelpful last component
189  rel_path.remove_leaf();
190  }
191 
192  // Make master wait (because other processes may be checking whether a directory exists)
193  PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPathBeforeCreation");
194  // Are we the master process? Only the master should make any new directories
195  if (PetscTools::AmMaster())
196  {
197  try
198  {
199  // If necessary make the ChasteTestOutputDirectory - don't make it deleteable by Chaste
200  fs::create_directories(output_root); // Note that this is a no-op if the folder exists already
201 
202  // Now make all the sub-folders requested one-by-one and add the .chaste_deletable_folder file to them
203  fs::path next_folder(output_root);
204  for (fs::path::iterator path_iter = rel_path.begin(); path_iter != rel_path.end(); ++path_iter)
205  {
206  next_folder /= *path_iter;
207  bool created_dir = fs::create_directory(next_folder);
208  if (created_dir)
209  {
210  // Add the Chaste signature file
211  fs::ofstream sig_file(next_folder / SIG_FILE_NAME);
212  sig_file.close();
213  }
214  }
215  }
216  // LCOV_EXCL_START
217  catch (const fs::filesystem_error& e)
218  {
219  TERMINATE("Error making test output folder: " << e.what());
220  }
221  // LCOV_EXCL_STOP
222  }
223 
224  // Wait for master to finish before going on to use the directory.
225  PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPath");
226 
227  std::string path_with_slash = (output_root / rel_path).string();
228  AddTrailingSlash(path_with_slash);
229  return path_with_slash;
230 }
231 
233 {
234  return mDirectory;
235 }
236 
238 {
239  FileFinder output_root("", RelativeTo::ChasteTestOutput);
240  std::string relative_path = FindFile("").GetRelativePath(output_root);
241  if (!relative_path.empty() && *relative_path.rbegin() == '/')
242  {
243  relative_path.erase(--relative_path.end()); // Remove trailing slash
244  }
245  return relative_path;
246 }
247 
248 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
249  std::ios_base::openmode mode) const
250 {
251  out_stream p_output_file(new std::ofstream((mDirectory+rFileName).c_str(), mode));
252  if (!p_output_file->is_open())
253  {
254  EXCEPTION("Could not open file \"" + rFileName + "\" in " + mDirectory);
255  }
256  return p_output_file;
257 }
258 
259 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
260  unsigned number,
261  const std::string& rFileFormat,
262  std::ios_base::openmode mode) const
263 {
264  std::stringstream string_stream;
265  string_stream << rFileName << number << rFileFormat;
266  return OpenOutputFile(string_stream.str(), mode);
267 }
268 
270 {
273 }
274 
275 void OutputFileHandler::AddTrailingSlash(std::string& rDirectory)
276 {
277  // Add a trailing slash if not already there
278  if (rDirectory!="" && !( *(rDirectory.end()-1) == '/'))
279  {
280  rDirectory = rDirectory + "/";
281  }
282 }
283 
285 {
286  if (!rSourceFile.IsFile())
287  {
288  EXCEPTION("Can only copy single files:\n" << rSourceFile.GetAbsolutePath() << " is not a file.");
289  }
290  fs::path from_path(rSourceFile.GetAbsolutePath());
291  fs::path to_path(GetOutputDirectoryFullPath());
292  to_path /= from_path.leaf();
293  if (PetscTools::AmMaster())
294  {
295  try
296  {
297  fs::copy_file(from_path, to_path);
298  }
299  // LCOV_EXCL_START
300  catch (const fs::filesystem_error& e)
301  {
302  TERMINATE("Error copying file '" << rSourceFile.GetAbsolutePath() << "': " << e.what());
303  }
304  // LCOV_EXCL_STOP
305  }
306  PetscTools::Barrier("OutputFileHandler::CopyFileTo");
307  return FileFinder(to_path.string(), RelativeTo::Absolute);
308 }
309 
310 FileFinder OutputFileHandler::FindFile(std::string leafName) const
311 {
313 }
std::string MakeFoldersAndReturnFullPath(const std::string &rDirectory) const
static const std::string SIG_FILE_NAME
static void AddTrailingSlash(std::string &rDirectory)
static void Barrier(const std::string callerId="")
Definition: PetscTools.cpp:134
bool IsFile() const
Definition: FileFinder.cpp:185
FileFinder CopyFileTo(const FileFinder &rSourceFile) const
std::string GetAbsolutePath() const
Definition: FileFinder.cpp:221
#define EXCEPTION(message)
Definition: Exception.hpp:143
static bool AmMaster()
Definition: PetscTools.cpp:120
#define TERMINATE(message)
Definition: Exception.hpp:168
std::string GetOutputDirectoryFullPath() const
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
void SetArchiveDirectory() const
FileFinder FindFile(std::string leafName) const
std::string GetRelativePath() const
#define PATH_LEAF_NAME(path)
bool Exists() const
Definition: FileFinder.cpp:180
std::string mDirectory
The directory to store output files in (always ends in "/")
virtual void SetPath(const std::string &rPath, RelativeTo::Value relativeTo)
Definition: FileFinder.cpp:99
static std::string GetChasteTestOutputDirectory()
static void SetArchiveDirectory(const FileFinder &rDirectory)
std::string GetRelativePath(const FileFinder &rBasePath) const
Definition: FileFinder.cpp:261
void CommonConstructor(const std::string &rDirectory, bool cleanOutputDirectory)
OutputFileHandler(const std::string &rDirectory, bool cleanOutputDirectory=true)