Fabrique

A build language for complex systems

 All Classes Namespaces Files Functions Variables Enumerations Enumerator Macros Pages
os-posix.cc
Go to the documentation of this file.
1 
2 /*
3  * Copyright (c) 2014 Jonathan Anderson
4  * All rights reserved.
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8  * ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "Support/Bytestream.h"
33 #include "Support/Join.h"
34 #include "Support/PosixError.h"
35 #include "Support/String.h"
36 #include "Support/exceptions.h"
37 #include "Support/os.h"
38 
39 #include <fstream>
40 
41 #include <sys/stat.h>
42 
43 #include <libgen.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 
47 using std::string;
48 using std::vector;
49 
50 
51 namespace {
52 
53 bool FileExists(const string& filename, bool directory = false)
54 {
55  struct stat s;
56  if (stat(filename.c_str(), &s) == 0)
57  return directory ? S_ISDIR(s.st_mode) : S_ISREG(s.st_mode);
58 
59  if (errno == ENOENT)
60  return false;
61 
62  throw fabrique::PosixError("error examining " + filename);
63 }
64 
65 
66 #if defined(__DARWIN_UNIX03)
67 
72 static const char* dirname(const char *filename)
73 {
74  return ::dirname(const_cast<char*>(filename));
75 }
76 #endif
77 
78 static const char PathDelimiter = ':';
79 
80 }
81 
82 
83 bool fabrique::PathIsAbsolute(const string& path)
84 {
85  return (not path.empty() and path[0] == '/');
86 }
87 
88 
89 string fabrique::AbsoluteDirectory(string name, bool createIfMissing)
90 {
91  const char *cname = name.c_str();
92 
93  struct stat s;
94  if (stat(cname, &s) != 0)
95  {
96  if (errno == ENOENT and createIfMissing)
97  {
98  if (mkdir(cname, 0777) != 0)
99  throw PosixError("creating directory " + name);
100  }
101  else
102  throw PosixError("reading directory " + name);
103  }
104 
105  return AbsolutePath(cname);
106 }
107 
108 
109 string fabrique::AbsolutePath(string name)
110 {
111  char *absolutePath = realpath(name.c_str(), nullptr);
112  if (not absolutePath)
113  throw PosixError("error in realpath('" + name + "')");
114 
115  string path(absolutePath);
116  free(absolutePath);
117 
118  if (path == ".")
119  return "";
120 
121  return path;
122 }
123 
124 
125 string fabrique::BaseName(string path)
126 {
127  const string filename = FilenameComponent(path);
128  return filename.substr(0, filename.rfind('.'));
129 }
130 
131 
132 string fabrique::CreateDirCommand(string dir)
133 {
134  return "if [ ! -e \"" + dir + "\" ]; then mkdir -p \"" + dir + "\"; fi";
135 }
136 
137 
138 fabrique::MissingFileReporter fabrique::DefaultFilename(std::string name)
139 {
140  return [name](string, const vector<string>&) { return name; };
141 }
142 
143 
144 string fabrique::DirectoryOf(string filename, bool absolute)
145 {
146  const char *dir = dirname(filename.c_str());
147 
148  if (not dir)
149  throw PosixError("error looking for parent of " + filename);
150 
151  const string relative(dir);
152  if (not absolute)
153  return (relative == ".") ? "" : relative;
154 
155  const string absoluteDir(AbsoluteDirectory(dir));
156 
157  struct stat s;
158  if (stat(absoluteDir.c_str(), &s) != 0)
159  throw PosixError("error querying " + absoluteDir);
160 
161  if (not S_ISDIR(s.st_mode))
162  throw PosixError(filename + " is not a directory");
163 
164  return absoluteDir;
165 }
166 
167 
168 string fabrique::FileExtension(string path)
169 {
170  const string filename = FilenameComponent(path);
171 
172  size_t i = filename.rfind('.');
173  if (i == string::npos)
174  return "";
175 
176  else
177  return filename.substr(i + 1);
178 }
179 
180 
181 bool fabrique::FileIsExecutable(string path)
182 {
183  struct stat s;
184  if (stat(path.c_str(), &s) != 0)
185  throw PosixError("error querying '" + path + "'");
186 
187  if (not S_ISREG(s.st_mode))
188  return false;
189 
190  return (s.st_mode & S_IXUSR);
191 }
192 
193 
194 bool fabrique::FileIsSharedLibrary(string path)
195 {
196  //
197  // For now, just check that a file exists and is executable.
198  // We can refine this logic later.
199  //
200  struct stat s;
201  if (stat(path.c_str(), &s) != 0)
202  throw PosixError("error querying '" + path + "'");
203 
204  if (not S_ISREG(s.st_mode))
205  return false;
206 
207  return (s.st_mode & S_IXUSR);
208 }
209 
210 
211 string fabrique::FilenameComponent(string pathIncludingDirectory)
212 {
213  if (pathIncludingDirectory.empty())
214  return "";
215 
216  const char *cname = pathIncludingDirectory.c_str();
217  return basename(const_cast<char*>(cname));
218 }
219 
220 
221 string fabrique::FileNotFound(string name, const vector<string>& searchPaths)
222 {
223  std::ostringstream oss;
224  oss << "no file '" << name << "' in directories [";
225 
226  for (const string& directory : searchPaths)
227  oss << " '" << directory << "'";
228 
229  oss << " ]";
230 
231  throw UserError(oss.str());
232 }
233 
234 
235 string fabrique::FindExecutable(string name, MissingFileReporter report)
236 {
237  const char *path = getenv("PATH");
238  if (not path)
239  throw PosixError("error in getenv('PATH')");
240 
241  return FindFile(name, Split(path, PathDelimiter), FileIsExecutable, report);
242 }
243 
244 
245 string fabrique::FindFile(string filename, const vector<string>& directories,
246  std::function<bool (const string&)> test,
247  MissingFileReporter fileNotFound)
248 {
249  for (const string& directory : directories)
250  {
251  const string absolute = JoinPath(directory, filename);
252  if (PathIsFile(absolute) and test(absolute))
253  return absolute;
254  }
255 
256  return fileNotFound(filename, directories);
257 }
258 
259 
260 string fabrique::FindModule(string srcroot, string subdir, string name)
261 {
262  const string relativeName = JoinPath(subdir, name);
263 
264  //
265  // Have we been passed an absolute module path?
266  //
267  if (PathIsAbsolute(relativeName) and FileExists(relativeName))
268  return relativeName;
269 
270  //
271  // If we can find the module relative to the srcroot, we don't want to
272  // return an absolute path: it will go into 'subdir' and try to generate
273  // files by absolute name. That is not allowed: files must be generated
274  // relative to the buildroot.
275  //
276  if (FileExists(JoinPath(srcroot, relativeName)))
277  return relativeName;
278 
279  //
280  // Look for the file within platform-specific search paths.
281  //
282  const vector<string> searchPaths = {
283  "/usr/local/share/fabrique",
284  };
285 
286  const string found = FindFile(relativeName, searchPaths,
287  PathIsFile, DefaultFilename(""));
288  if (not found.empty())
289  return found;
290 
291  //
292  // If we were passed a directory, look for 'fabfile' within it.
293  //
294  const string dirname = JoinPath(srcroot, relativeName);
295  if (FileExists(dirname, true))
296  {
297  const string fabfile = JoinPath(dirname, "fabfile");
298  if (FileExists(fabfile))
299  return JoinPath(relativeName, "fabfile");
300  }
301 
302  throw UserError("unable to find module '" + name + "'");
303 }
304 
305 
306 string fabrique::JoinPath(const string& x, const string& y)
307 {
308  if (x.empty() or x == ".")
309  return y;
310 
311  if (y.empty() or y == ".")
312  return x;
313 
314  return x + "/" + y;
315 }
316 
317 string fabrique::JoinPath(const vector<string>& components)
318 {
319  return join(components, "/");
320 }
321 
322 
323 string fabrique::LibraryFilename(string name)
324 {
325  static constexpr char Extension[] =
326 #if defined(OS_MAC)
327  "dylib"
328 #else
329  "so"
330 #endif
331  ;
332 
333  return "lib" + name + "." + Extension;
334 }
335 
336 
337 vector<string> fabrique::PluginSearchPaths(string binary)
338 {
339  const string prefix = DirectoryOf(DirectoryOf(binary));
340  return {
341  prefix + "/lib/fabrique",
342  "/usr/lib/fabrique",
343  "/usr/local/lib/fabrique",
344  };
345 }
346 
347 
348 bool fabrique::PathIsDirectory(string path)
349 {
350  return FileExists(path, true);
351 }
352 
353 bool fabrique::PathIsFile(string path)
354 {
355  return FileExists(path, false);
356 }
Declaration of fabrique::PosixError.
An OS error that has an errno or equivalent output.
Definition: PosixError.h:40
Declaration of basic Fabrique exceptions.
Declarations of OS-abstraction functions.
Declaration of the fabrique::Join ostream helper and some helper functions for joining strings (or th...
Declaration of string utility functions.
Declaration of fabrique::Bytestream.