Fabrique

A build language for complex systems

 All Classes Namespaces Files Functions Variables Enumerations Enumerator Macros Pages
driver.cc
Go to the documentation of this file.
1 
2 /*
3  * Copyright (c) 2013-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 "AST/ASTDump.h"
33 #include "AST/EvalContext.h"
34 
35 #include "Backend/Backend.h"
36 
37 #include "DAG/DAG.h"
38 
39 #include "Parsing/Lexer.h"
40 #include "Parsing/Parser.h"
41 
42 #include "Plugin/Loader.h"
43 #include "Plugin/Registry.h"
44 
45 #include "Support/Arguments.h"
46 #include "Support/Bytestream.h"
47 #include "Support/exceptions.h"
48 #include "Support/os.h"
49 
50 #include "Types/TypeContext.h"
51 
52 #include <cassert>
53 #include <cstdio>
54 #include <fstream>
55 #include <iostream>
56 #include <memory>
57 
58 using namespace fabrique;
59 using namespace std;
62 
63 
64 static Bytestream& err();
65 static void reportError(string message, SourceRange, ErrorReport::Severity);
66 static unique_ptr<ast::Scope> Parse(UniqPtr<Parser>& parser, const string& filename,
67  const vector<string>& definitions,
68  string srcroot, string buildroot, bool printAST);
69 
70 int main(int argc, char *argv[]) {
71  //
72  // Parse command-line arguments.
73  //
74  unique_ptr<Arguments> args(Arguments::Parse(argc, argv));
75  if (not args or args->help)
76  {
77  Arguments::PrintUsage(cerr);
78  return (args ? 0 : 1);
79  }
80 
81  //
82  // Set up debug streams.
83  //
84  Bytestream::SetDebugPattern(args->debugPattern);
85  Bytestream::SetDebugStream(Bytestream::Stdout());
86 
87  Bytestream& argDebug = Bytestream::Debug("cli.args");
88  Arguments::Print(*args, argDebug);
89  argDebug << Bytestream::Reset << "\n";
90 
91  //
92  // Parse the file, build the DAG and pass it to the backend.
93  // These operations can report errors with exceptions, so put them in
94  // a `try` block.
95  //
96  try
97  {
98  TypeContext types;
99 
100  const string fabfile =
101  PathIsDirectory(args->input)
102  ? JoinPath(args->input, "fabfile")
103  : args->input
104  ;
105 
106  if (not PathIsFile(fabfile))
107  throw UserError("no such file: '" + fabfile + "'");
108 
109  const string srcroot = AbsoluteDirectory(DirectoryOf(fabfile));
110  const string buildroot = AbsoluteDirectory(args->output);
111 
112  plugin::Registry& pluginRegistry = plugin::Registry::get();
113  plugin::Loader pluginLoader(PluginSearchPaths(args->executable));
114 
115  //
116  // Parse the file, optionally pretty-printing it.
117  //
118  unique_ptr<ast::Parser> parser(
119  new ast::Parser(types, pluginRegistry, pluginLoader, srcroot));
120 
121  unique_ptr<ast::Scope> ast(
122  Parse(parser, fabfile, args->definitions,
123  srcroot, buildroot, args->printAST));
124 
125  if (not ast)
126  return -1;
127 
128  if (args->parseOnly)
129  return 0;
130 
131 
132  //
133  // Prepare backends to receive the build graph.
134  //
135  UniqPtrVec<Backend> backends;
136  vector<string> outputFiles;
137  for (const string& format : args->outputFormats)
138  {
139  backends.emplace_back(Backend::Create(format));
140 
141  const string filename = backends.back()->DefaultFilename();
142  if (not filename.empty())
143  outputFiles.push_back(filename);
144  }
145 
146 
147  //
148  // Convert the AST into a build graph.
149  //
150  ast::EvalContext ctx(types, buildroot, srcroot);
151  auto topScope(ctx.Evaluate(*ast));
152 
153  // Get the names of the top-level targets:
154  vector<string> targets;
155  transform(topScope.begin(), topScope.end(), back_inserter(targets),
156  [](const pair<string,dag::ValuePtr>& i)
157  {
158  return i.first;
159  });
160 
161  // Add regeneration (if Fabrique files change):
162  if (not outputFiles.empty())
163  ctx.builder().AddRegeneration(
164  *args, parser->files(), outputFiles);
165 
166  unique_ptr<dag::DAG> dag = ctx.builder().dag(targets);
167  assert(dag);
168 
169  if (args->printDAG)
170  {
171  Bytestream::Stdout()
172  << Bytestream::Comment
173  << "#\n"
174  << "# DAG pretty-printed from '"
175  << fabfile << "'\n"
176  << "#\n"
177  << Bytestream::Reset
178  << *dag
179  ;
180  }
181 
182 
183  //
184  // Finally, feed the build graph into the backend(s).
185  //
186  for (UniqPtr<Backend>& backend : backends)
187  {
188  std::ofstream outfile;
189  unique_ptr<Bytestream> outfileStream;
190 
191  const string filename =
192  JoinPath(buildroot, backend->DefaultFilename());
193 
194  if (not args->printOutput and filename != buildroot)
195  {
196  outfile.open(filename.c_str());
197  outfileStream.reset(Bytestream::Plain(outfile));
198  }
199 
200  Bytestream& out = outfileStream
201  ? *outfileStream
202  : Bytestream::Stdout();
203 
204  backend->Process(*dag, out, reportError);
205 
206  outfile.flush();
207  outfile.close();
208  }
209 
210  return 0;
211  }
212  catch (const UserError& e)
213  {
214  err()
215  << Bytestream::Error << "Error"
216  << Bytestream::Reset << ": " << e
217  ;
218  }
219  catch (const OSError& e)
220  {
221  err()
222  << Bytestream::Error << e.message()
223  << Bytestream::Reset << ": "
224  << Bytestream::ErrorMessage << e.description()
225  ;
226  }
227  catch (const SemanticException& e)
228  {
229  err() << e;
230  }
231  catch (const SourceCodeException& e)
232  {
233  err()
234  << Bytestream::Error << "Parse error"
235  << Bytestream::Reset << ": "
236  << Bytestream::ErrorMessage << e
237  ;
238  }
239 #ifdef NDEBUG
240  // In debug mode, let uncaught exceptions propagate to ease debugging.
241  catch (const std::exception& e)
242  {
243  err()
244  << Bytestream::Error << "Uncaught exception"
245  << Bytestream::Reset << ": "
246  << Bytestream::ErrorMessage << e.what()
247  ;
248  }
249 #endif
250 
251  err() << Bytestream::Reset << "\n";
252  return 1;
253 }
254 
255 
256 unique_ptr<ast::Scope> Parse(UniqPtr<Parser>& parser, const string& filename,
257  const vector<string>& definitions,
258  string srcroot, string buildroot, bool printAST)
259 {
260 
261  // Parse command-line arguments.
262  const Type& args = parser->ParseDefinitions(definitions);
263 
264  // Open and parse the top-level build description.
265  std::ifstream infile(filename.c_str());
266  assert(infile);
267 
268  map<string,string> builtins {
269  std::make_pair("srcroot", srcroot),
270  std::make_pair("buildroot", buildroot),
271  };
272 
273  const string absolute =
274  PathIsAbsolute(filename) ? filename : AbsolutePath(filename);
275 
276  unique_ptr<ast::Scope> ast(
277  parser->ParseFile(infile, args, absolute, builtins));
278 
279  if (not ast)
280  {
281  for (auto& error : parser->errors())
282  err() << *error << "\n";
283 
284  return ast;
285  }
286  assert(parser->errors().empty());
287 
288  if (printAST)
289  {
290  Bytestream::Stdout()
291  << Bytestream::Comment
292  << "#\n"
293  << "# AST pretty-printed from '" << filename << "'\n"
294  << "#\n"
295  << Bytestream::Reset
296  ;
297 
298  for (auto& val : ast->values())
299  Bytestream::Stdout() << *val << "\n";
300  }
301 
302  return ast;
303 }
304 
305 
306 static Bytestream& err()
307 {
308  static Bytestream& err = Bytestream::Stderr();
309  return err;
310 }
311 
312 
313 static void reportError(string message, SourceRange src, ErrorReport::Severity severity)
314 {
315  UniqPtr<ErrorReport> warning { ErrorReport::Create(message, src, severity) };
316  err() << *warning << Bytestream::Reset << "\n";
317 }
Interface for backend classes that convert a DAG to something else (e.g., a Ninja file)...
Definition: Backend.h:51
A context object that holds state for a compilation (e.g., type objects).
Definition: TypeContext.h:54
Declaration of fabrique::Arguments.
Declaration of fabrique::ast::EvalContext.
Declaration of fabrique::ast::Parser.
Base class for exceptions related to invalid source code.
Definition: exceptions.h:111
An error in user input.
Definition: exceptions.h:90
Declaration of fabrique::plugin::Loader.
const UniqPtrVec< ErrorReport > & errors() const
Errors encountered during parsing.
Definition: Parser.h:76
const Type & ParseDefinitions(const std::vector< std::string > &defs)
Parse Fabrique fragments defined at, e.g., the command line.
Definition: Parser.cc:75
STL namespace.
std::unique_ptr< Scope > ParseFile(std::istream &input, const Type &arguments, std::string name="", StringMap< std::string > builtins=StringMap< std::string >(), SourceRange openedFrom=SourceRange::None())
Parse Fabrique input (usually a file) into a Scope.
Definition: Parser.cc:119
Declaration of fabrique::ast::ASTDump.
Declaration of fabrique::TypeContext.
Declaration of fabrique::backend::Backend.
Parses Fabrique files as driven by flex/byacc.
Definition: Parser.h:61
Declaration of fabrique::dag::DAG.
An error that has an OS-specific description.
Definition: exceptions.h:68
Declaration of basic Fabrique exceptions.
Declarations of OS-abstraction functions.
Support for loading plugins from shared libraries.
Definition: Loader.h:51
A ostream-like class that may support formatting.
Definition: Bytestream.h:43
Definition of fabrique::Lexer.
A range of characters in source code.
Declaration of fabrique::plugin::Registry.
A context for evaluating AST Expression objects.
Definition: EvalContext.h:69
static Bytestream & Debug(const std::string &name)
Retrieve the debug output stream or a do-nothing stream, based on the (hierarchical) debug naming sch...
Definition: Bytestream.cc:181
Declaration of fabrique::Bytestream.
The name of a value, function, parameter or argument.
Definition: Type.h:53
A registry for naming Fabrique Plugin objects.
Definition: Registry.h:43
A semantic error is present in the Fabrique description.
Definition: exceptions.h:141
static Bytestream * Plain(std::ostream &)
Construct a plain fabrique::Bytestream to wrap an std::ostream.
Definition: Bytestream.cc:207