Chaste  Release::3.4
OutputFileHandler.cpp
1 /*
2 
3 Copyright (c) 2005-2016, 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 
40 #include "ArchiveLocationInfo.hpp"
41 #include "BoostFilesystem.hpp"
42 #include "Exception.hpp"
43 #include "FileFinder.hpp"
44 #include "GetCurrentWorkingDirectory.hpp"
45 #include "PetscTools.hpp"
46 
47 
48 const std::string OutputFileHandler::SIG_FILE_NAME(".chaste_deletable_folder");
49 
57 void CleanFolder(const fs::path& rPath, bool isTop=true);
58 
59 void CleanFolder(const fs::path& rPath, bool isTop)
60 {
61  assert(fs::is_directory(rPath));
62  fs::directory_iterator end_iter;
63  // First recursively remove the children
64  for (fs::directory_iterator dir_iter(rPath); dir_iter != end_iter; ++dir_iter)
65  {
66  if (fs::is_directory(dir_iter->status()))
67  {
68  CleanFolder(dir_iter->path(), false);
69  }
70  else
71  {
72  const fs::path& r_item_path(dir_iter->path());
73  if (!isTop || PATH_LEAF_NAME(r_item_path)[0] != '.')
74  {
75  fs::remove(r_item_path);
76  }
77  }
78  }
79  // Now remove the folder itself, if not top
80  if (!isTop)
81  {
82  fs::remove(rPath);
83  }
84 }
85 
86 
87 OutputFileHandler::OutputFileHandler(const std::string& rDirectory,
88  bool cleanOutputDirectory)
89 {
90  CommonConstructor(rDirectory, cleanOutputDirectory);
91 }
92 
93 
95  bool cleanOutputDirectory)
96 {
98  std::string relative_path;
99  try
100  {
101  relative_path = rDirectory.GetRelativePath(output_root);
102  }
103  catch (const Exception&)
104  {
105  EXCEPTION("The location provided to OutputFileHandler must be inside CHASTE_TEST_OUTPUT; '"
106  << rDirectory.GetAbsolutePath() << "' is not under '"
107  << output_root.GetAbsolutePath() << "'.");
108  }
109  if (*output_root.GetAbsolutePath().rbegin() != '/' && !relative_path.empty())
110  {
111  assert(*relative_path.begin() == '/');
112  relative_path.erase(0, 1); // Remove leading slash
113  }
114  CommonConstructor(relative_path, cleanOutputDirectory);
115 }
116 
117 
118 void OutputFileHandler::CommonConstructor(const std::string& rDirectory,
119  bool cleanOutputDirectory)
120 {
121  // Is it a valid request for a directory?
122  if (rDirectory.find("..") != std::string::npos)
123  {
124  EXCEPTION("Will not create directory: " + rDirectory +
125  " due to it potentially being above, and cleaning, CHASTE_TEST_OUTPUT.");
126  // Note: in Boost 1.48 and above we could use 'canonical' to check this better
127  }
128  //The notion of absolute path on Windows is rather different.
129  //For example, / and /foo are not absolute paths.
130  //However, fs::path.has_root_path() captures the intended semantics here as follows
131 
132  if (fs::path(rDirectory).has_root_path())
133  {
134  EXCEPTION("The constructor argument to OutputFileHandler must be a relative path; '"
135  << rDirectory << "' is absolute.");
136  }
137 
139 
140  // Clean the directory (default)
141  if (rDirectory != "" && cleanOutputDirectory) // Clean directory but don't ever clean CHASTE_TEST_OUTPUT at the top level
142  {
144  if (!signature_file.Exists())
145  {
146  EXCEPTION("Cannot delete " + mDirectory + " because signature file \"" + SIG_FILE_NAME + "\" is not present.");
147  }
148 
149  // Are we the master process? Only the master should delete files
150  if (PetscTools::AmMaster())
151  {
152  CleanFolder(mDirectory);
153  }
154  // Wait for master to finish before going on to use the directory.
155  PetscTools::Barrier("OutputFileHandler");
156  }
157 }
158 
160 {
161  char *chaste_test_output = getenv("CHASTE_TEST_OUTPUT");
162  FileFinder directory_root;
163  if (chaste_test_output == NULL || *chaste_test_output == 0)
164  {
165  // Default to 'testoutput' folder within the current directory
166  directory_root.SetPath("testoutput", RelativeTo::CWD);
167  }
168  else
169  {
170  directory_root.SetPath(chaste_test_output, RelativeTo::AbsoluteOrCwd);
171  }
172  // Note that FileFinder::GetAbsolutePath adds a trailing slash, but only
173  // if the directory exists at the time of the call
174  std::string chaste_test_output_directory = directory_root.GetAbsolutePath();
175  AddTrailingSlash(chaste_test_output_directory);
176  return chaste_test_output_directory;
177 }
178 
179 std::string OutputFileHandler::MakeFoldersAndReturnFullPath(const std::string& rDirectory) const
180 {
181  fs::path output_root(GetChasteTestOutputDirectory());
182  fs::path rel_path(rDirectory);
183 
184  if (!rel_path.empty() && (*(--rel_path.end())) == ".")
185  {
186  // rDirectory has a trailing slash, which gives an unhelpful last component
187  rel_path.remove_leaf();
188  }
189 
190  // Make master wait (because other processes may be checking whether a directory exists)
191  PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPathBeforeCreation");
192  // Are we the master process? Only the master should make any new directories
193  if (PetscTools::AmMaster())
194  {
195  try
196  {
197  // If necessary make the ChasteTestOutputDirectory - don't make it deleteable by Chaste
198  fs::create_directories(output_root); // Note that this is a no-op if the folder exists already
199 
200  // Now make all the sub-folders requested one-by-one and add the .chaste_deletable_folder file to them
201  fs::path next_folder(output_root);
202  for (fs::path::iterator path_iter = rel_path.begin(); path_iter != rel_path.end(); ++path_iter)
203  {
204  next_folder /= *path_iter;
205  bool created_dir = fs::create_directory(next_folder);
206  if (created_dir)
207  {
208  // Add the Chaste signature file
209  fs::ofstream sig_file(next_folder / SIG_FILE_NAME);
210  sig_file.close();
211  }
212  }
213  }
214  catch (const fs::filesystem_error& e)
215  {
216  TERMINATE("Error making test output folder: " << e.what());
217  }
218  }
219 
220  // Wait for master to finish before going on to use the directory.
221  PetscTools::Barrier("OutputFileHandler::MakeFoldersAndReturnFullPath");
222 
223  std::string path_with_slash = (output_root / rel_path).string();
224  AddTrailingSlash(path_with_slash);
225  return path_with_slash;
226 }
227 
229 {
230  return mDirectory;
231 }
232 
234 {
235  FileFinder output_root("", RelativeTo::ChasteTestOutput);
236  std::string relative_path = FindFile("").GetRelativePath(output_root);
237  if (!relative_path.empty() && *relative_path.rbegin() == '/')
238  {
239  relative_path.erase(--relative_path.end()); // Remove trailing slash
240  }
241  return relative_path;
242 }
243 
244 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
245  std::ios_base::openmode mode) const
246 {
247  out_stream p_output_file(new std::ofstream((mDirectory+rFileName).c_str(), mode));
248  if (!p_output_file->is_open())
249  {
250  EXCEPTION("Could not open file \"" + rFileName + "\" in " + mDirectory);
251  }
252  return p_output_file;
253 }
254 
255 out_stream OutputFileHandler::OpenOutputFile(const std::string& rFileName,
256  unsigned number,
257  const std::string& rFileFormat,
258  std::ios_base::openmode mode) const
259 {
260  std::stringstream string_stream;
261  string_stream << rFileName << number << rFileFormat;
262  return OpenOutputFile(string_stream.str(), mode);
263 }
264 
266 {
269 }
270 
271 void OutputFileHandler::AddTrailingSlash(std::string& rDirectory)
272 {
273  // Add a trailing slash if not already there
274  if (rDirectory!="" && !( *(rDirectory.end()-1) == '/'))
275  {
276  rDirectory = rDirectory + "/";
277  }
278 }
279 
281 {
282  if (!rSourceFile.IsFile())
283  {
284  EXCEPTION("Can only copy single files:\n" << rSourceFile.GetAbsolutePath() << " is not a file.");
285  }
286  fs::path from_path(rSourceFile.GetAbsolutePath());
287  fs::path to_path(GetOutputDirectoryFullPath());
288  to_path /= from_path.leaf();
289  if (PetscTools::AmMaster())
290  {
291  try
292  {
293  fs::copy_file(from_path, to_path);
294  }
295  catch (const fs::filesystem_error& e)
296  {
297  TERMINATE("Error copying file '" << rSourceFile.GetAbsolutePath() << "': " << e.what());
298  }
299  }
300  PetscTools::Barrier("OutputFileHandler::CopyFileTo");
301  return FileFinder(to_path.string(), RelativeTo::Absolute);
302 }
303 
304 FileFinder OutputFileHandler::FindFile(std::string leafName) const
305 {
307 }
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)