blob: a381d56e1cde2a0b4f112fc19c315ed0ccbbcb50 [file] [log] [blame]
Reid Spencercbabe7f2004-08-19 21:17:53 +00001//===- Configuration.cpp - Configuration Data Mgmt --------------*- C++ -*-===//
Reid Spencer2594c9a2004-08-13 20:21:22 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Reid Spencer and is distributed under the
6// University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the parsing of configuration files for the LLVM Compiler
11// Driver (llvmc).
12//
13//===------------------------------------------------------------------------===
14
Reid Spencerf51a87c2004-08-19 21:52:49 +000015#include "Configuration.h"
Reid Spencer68fb37a2004-08-14 09:37:15 +000016#include "ConfigLexer.h"
Reid Spencer2594c9a2004-08-13 20:21:22 +000017#include "CompilerDriver.h"
Reid Spencerb38e4052004-08-20 09:24:07 +000018#include "Config/config.h"
Reid Spencerbae68252004-08-19 04:49:47 +000019#include "Support/CommandLine.h"
Reid Spencer2594c9a2004-08-13 20:21:22 +000020#include "Support/StringExtras.h"
21#include <iostream>
Reid Spencer68fb37a2004-08-14 09:37:15 +000022#include <fstream>
Reid Spencer2594c9a2004-08-13 20:21:22 +000023
24using namespace llvm;
25
Reid Spencerb38e4052004-08-20 09:24:07 +000026namespace sys {
27 // From CompilerDriver.cpp (for now)
28 extern bool FileReadable(const std::string& fname);
29}
30
Reid Spencer68fb37a2004-08-14 09:37:15 +000031namespace llvm {
Reid Spencerbae68252004-08-19 04:49:47 +000032 ConfigLexerInfo ConfigLexerState;
Reid Spencer68fb37a2004-08-14 09:37:15 +000033 InputProvider* ConfigLexerInput = 0;
Reid Spencer68fb37a2004-08-14 09:37:15 +000034
35 InputProvider::~InputProvider() {}
36 void InputProvider::error(const std::string& msg) {
Reid Spencerbae68252004-08-19 04:49:47 +000037 std::cerr << name << ":" << ConfigLexerState.lineNum << ": Error: " <<
38 msg << "\n";
Reid Spencer68fb37a2004-08-14 09:37:15 +000039 errCount++;
40 }
41
42 void InputProvider::checkErrors() {
43 if (errCount > 0) {
44 std::cerr << name << " had " << errCount << " errors. Terminating.\n";
45 exit(errCount);
46 }
47 }
48
49}
50
Reid Spencer2594c9a2004-08-13 20:21:22 +000051namespace {
52
Reid Spencer68fb37a2004-08-14 09:37:15 +000053 class FileInputProvider : public InputProvider {
54 public:
55 FileInputProvider(const std::string & fname)
56 : InputProvider(fname)
57 , F(fname.c_str()) {
58 ConfigLexerInput = this;
59 }
60 virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; }
61 virtual unsigned read(char *buffer, unsigned max_size) {
62 if (F.good()) {
63 F.read(buffer,max_size);
64 if ( F.gcount() ) return F.gcount() - 1;
65 }
66 return 0;
67 }
Reid Spencer2594c9a2004-08-13 20:21:22 +000068
Reid Spencer68fb37a2004-08-14 09:37:15 +000069 bool okay() { return F.good(); }
70 private:
71 std::ifstream F;
72 };
Reid Spencer2594c9a2004-08-13 20:21:22 +000073
Reid Spencerbae68252004-08-19 04:49:47 +000074 cl::opt<bool> DumpTokens("dump-tokens", cl::Optional, cl::Hidden, cl::init(false),
75 cl::desc("Dump lexical tokens (debug use only)."));
76
Reid Spencerbf437722004-08-15 08:19:46 +000077 struct Parser
Reid Spencer68fb37a2004-08-14 09:37:15 +000078 {
Reid Spencerbae68252004-08-19 04:49:47 +000079 Parser() {
80 token = EOFTOK;
81 provider = 0;
82 confDat = 0;
83 ConfigLexerState.lineNum = 1;
84 ConfigLexerState.in_value = false;
85 ConfigLexerState.StringVal.clear();
86 ConfigLexerState.IntegerVal = 0;
87 };
88
Reid Spencerbf437722004-08-15 08:19:46 +000089 ConfigLexerTokens token;
Reid Spencer68fb37a2004-08-14 09:37:15 +000090 InputProvider* provider;
91 CompilerDriver::ConfigData* confDat;
Reid Spencer2594c9a2004-08-13 20:21:22 +000092
Reid Spencerbae68252004-08-19 04:49:47 +000093 int next() {
94 token = Configlex();
95 if (DumpTokens)
96 std::cerr << token << "\n";
97 return token;
98 }
Reid Spencer68fb37a2004-08-14 09:37:15 +000099
100 bool next_is_real() {
Reid Spencerbae68252004-08-19 04:49:47 +0000101 next();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000102 return (token != EOLTOK) && (token != ERRORTOK) && (token != 0);
Reid Spencer2594c9a2004-08-13 20:21:22 +0000103 }
104
Reid Spencer68fb37a2004-08-14 09:37:15 +0000105 void eatLineRemnant() {
106 while (next_is_real()) ;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000107 }
108
Reid Spencer68fb37a2004-08-14 09:37:15 +0000109 void error(const std::string& msg, bool skip = true) {
110 provider->error(msg);
111 if (skip)
112 eatLineRemnant();
113 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000114
Reid Spencer68fb37a2004-08-14 09:37:15 +0000115 std::string parseName() {
116 std::string result;
117 if (next() == EQUALS) {
118 while (next_is_real()) {
119 switch (token ) {
120 case STRING :
121 case OPTION :
Reid Spencerbae68252004-08-19 04:49:47 +0000122 result += ConfigLexerState.StringVal + " ";
Reid Spencer68fb37a2004-08-14 09:37:15 +0000123 break;
124 default:
125 error("Invalid name");
126 break;
127 }
128 }
129 if (result.empty())
130 error("Name exepected");
131 else
132 result.erase(result.size()-1,1);
133 } else
134 error("= expected");
135 return result;
136 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000137
Reid Spencer68fb37a2004-08-14 09:37:15 +0000138 bool parseBoolean() {
139 bool result = true;
140 if (next() == EQUALS) {
141 if (next() == FALSETOK) {
142 result = false;
143 } else if (token != TRUETOK) {
144 error("Expecting boolean value");
145 return false;
146 }
147 if (next() != EOLTOK && token != 0) {
148 error("Extraneous tokens after boolean");
149 }
150 }
151 else
152 error("Expecting '='");
153 return result;
154 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000155
Reid Spencerbae68252004-08-19 04:49:47 +0000156 bool parseSubstitution(CompilerDriver::StringVector& optList) {
157 switch (token) {
Reid Spencer53aa7932004-08-20 22:53:11 +0000158 case IN_SUBST: optList.push_back("%in%"); break;
159 case OUT_SUBST: optList.push_back("%out%"); break;
160 case TIME_SUBST: optList.push_back("%time%"); break;
161 case STATS_SUBST: optList.push_back("%stats%"); break;
162 case OPT_SUBST: optList.push_back("%opt%"); break;
163 case TARGET_SUBST: optList.push_back("%target%"); break;
Reid Spencerbae68252004-08-19 04:49:47 +0000164 default:
165 return false;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000166 }
Reid Spencerbae68252004-08-19 04:49:47 +0000167 return true;
168 }
169
170 void parseOptionList(CompilerDriver::StringVector& optList ) {
171 if (next() == EQUALS) {
172 while (next_is_real()) {
173 if (token == STRING || token == OPTION)
174 optList.push_back(ConfigLexerState.StringVal);
175 else if (!parseSubstitution(optList)) {
176 error("Expecting a program argument or substitution", false);
177 break;
178 }
179 }
180 } else
181 error("Expecting '='");
Reid Spencerbf437722004-08-15 08:19:46 +0000182 }
183
184 void parseLang() {
185 switch (next() ) {
186 case NAME:
187 confDat->langName = parseName();
188 break;
189 case OPT1:
190 parseOptionList(confDat->opts[CompilerDriver::OPT_FAST_COMPILE]);
191 break;
192 case OPT2:
193 parseOptionList(confDat->opts[CompilerDriver::OPT_SIMPLE]);
194 break;
195 case OPT3:
196 parseOptionList(confDat->opts[CompilerDriver::OPT_AGGRESSIVE]);
197 break;
198 case OPT4:
199 parseOptionList(confDat->opts[CompilerDriver::OPT_LINK_TIME]);
200 break;
201 case OPT5:
202 parseOptionList(
203 confDat->opts[CompilerDriver::OPT_AGGRESSIVE_LINK_TIME]);
204 break;
205 default:
206 error("Expecting 'name' or 'optN' after 'lang.'");
207 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000208 }
209 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000210
Reid Spencer68fb37a2004-08-14 09:37:15 +0000211 void parseCommand(CompilerDriver::Action& action) {
212 if (next() == EQUALS) {
Reid Spencerbf437722004-08-15 08:19:46 +0000213 if (next() == EOLTOK) {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000214 // no value (valid)
215 action.program.clear();
216 action.args.clear();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000217 } else {
218 if (token == STRING || token == OPTION) {
Reid Spencerbae68252004-08-19 04:49:47 +0000219 action.program = ConfigLexerState.StringVal;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000220 } else {
Reid Spencer68fb37a2004-08-14 09:37:15 +0000221 error("Expecting a program name");
222 }
223 while (next_is_real()) {
Reid Spencerbf437722004-08-15 08:19:46 +0000224 if (token == STRING || token == OPTION) {
Reid Spencerbae68252004-08-19 04:49:47 +0000225 action.args.push_back(ConfigLexerState.StringVal);
226 } else if (!parseSubstitution(action.args)) {
227 error("Expecting a program argument or substitution", false);
Reid Spencerbf437722004-08-15 08:19:46 +0000228 break;
229 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000230 }
231 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000232 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000233 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000234
Reid Spencerbf437722004-08-15 08:19:46 +0000235 void parsePreprocessor() {
236 switch (next()) {
237 case COMMAND:
238 parseCommand(confDat->PreProcessor);
239 break;
240 case REQUIRED:
241 if (parseBoolean())
242 confDat->PreProcessor.set(CompilerDriver::REQUIRED_FLAG);
243 else
244 confDat->PreProcessor.clear(CompilerDriver::REQUIRED_FLAG);
245 break;
246 default:
247 error("Expecting 'command' or 'required'");
248 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000249 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000250 }
251
252 void parseTranslator() {
Reid Spencerbf437722004-08-15 08:19:46 +0000253 switch (next()) {
254 case COMMAND:
255 parseCommand(confDat->Translator);
256 break;
257 case REQUIRED:
258 if (parseBoolean())
259 confDat->Translator.set(CompilerDriver::REQUIRED_FLAG);
260 else
261 confDat->Translator.clear(CompilerDriver::REQUIRED_FLAG);
262 break;
263 case PREPROCESSES:
264 if (parseBoolean())
265 confDat->Translator.set(CompilerDriver::PREPROCESSES_FLAG);
266 else
267 confDat->Translator.clear(CompilerDriver::PREPROCESSES_FLAG);
268 break;
269 case OPTIMIZES:
270 if (parseBoolean())
271 confDat->Translator.set(CompilerDriver::OPTIMIZES_FLAG);
272 else
273 confDat->Translator.clear(CompilerDriver::OPTIMIZES_FLAG);
274 break;
275 case GROKS_DASH_O:
276 if (parseBoolean())
277 confDat->Translator.set(CompilerDriver::GROKS_DASH_O_FLAG);
278 else
279 confDat->Translator.clear(CompilerDriver::GROKS_DASH_O_FLAG);
280 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000281 case OUTPUT_IS_ASM:
Reid Spencerbf437722004-08-15 08:19:46 +0000282 if (parseBoolean())
Reid Spencerbae68252004-08-19 04:49:47 +0000283 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000284 else
Reid Spencerbae68252004-08-19 04:49:47 +0000285 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000286 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000287
Reid Spencerbf437722004-08-15 08:19:46 +0000288 default:
289 error("Expecting 'command', 'required', 'preprocesses', "
290 "'groks_dash_O' or 'optimizes'");
291 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000292 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000293 }
294
295 void parseOptimizer() {
Reid Spencerbf437722004-08-15 08:19:46 +0000296 switch (next()) {
297 case COMMAND:
298 parseCommand(confDat->Optimizer);
299 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000300 case PREPROCESSES:
301 if (parseBoolean())
302 confDat->Optimizer.set(CompilerDriver::PREPROCESSES_FLAG);
303 else
304 confDat->Optimizer.clear(CompilerDriver::PREPROCESSES_FLAG);
305 break;
306 case TRANSLATES:
307 if (parseBoolean())
308 confDat->Optimizer.set(CompilerDriver::TRANSLATES_FLAG);
309 else
310 confDat->Optimizer.clear(CompilerDriver::TRANSLATES_FLAG);
311 break;
Reid Spencerbf437722004-08-15 08:19:46 +0000312 case GROKS_DASH_O:
313 if (parseBoolean())
314 confDat->Optimizer.set(CompilerDriver::GROKS_DASH_O_FLAG);
315 else
316 confDat->Optimizer.clear(CompilerDriver::GROKS_DASH_O_FLAG);
317 break;
Reid Spencerbae68252004-08-19 04:49:47 +0000318 case OUTPUT_IS_ASM:
Reid Spencerbf437722004-08-15 08:19:46 +0000319 if (parseBoolean())
Reid Spencerbae68252004-08-19 04:49:47 +0000320 confDat->Translator.set(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000321 else
Reid Spencerbae68252004-08-19 04:49:47 +0000322 confDat->Translator.clear(CompilerDriver::OUTPUT_IS_ASM_FLAG);
Reid Spencerbf437722004-08-15 08:19:46 +0000323 break;
324 default:
325 error("Expecting 'command' or 'groks_dash_O'");
326 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000327 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000328 }
329
330 void parseAssembler() {
Reid Spencerbf437722004-08-15 08:19:46 +0000331 switch(next()) {
332 case COMMAND:
333 parseCommand(confDat->Assembler);
334 break;
335 default:
336 error("Expecting 'command'");
337 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000338 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000339 }
340
341 void parseLinker() {
Reid Spencerbf437722004-08-15 08:19:46 +0000342 switch(next()) {
343 case COMMAND:
344 parseCommand(confDat->Linker);
345 break;
346 case GROKS_DASH_O:
347 if (parseBoolean())
348 confDat->Linker.set(CompilerDriver::GROKS_DASH_O_FLAG);
349 else
350 confDat->Linker.clear(CompilerDriver::GROKS_DASH_O_FLAG);
351 break;
Reid Spencerbf437722004-08-15 08:19:46 +0000352 default:
353 error("Expecting 'command'");
354 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000355 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000356 }
357
358 void parseAssignment() {
359 switch (token) {
Reid Spencerbf437722004-08-15 08:19:46 +0000360 case LANG: parseLang(); break;
361 case PREPROCESSOR: parsePreprocessor(); break;
362 case TRANSLATOR: parseTranslator(); break;
363 case OPTIMIZER: parseOptimizer(); break;
364 case ASSEMBLER: parseAssembler(); break;
365 case LINKER: parseLinker(); break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000366 case EOLTOK: break; // just ignore
367 case ERRORTOK:
368 default:
Reid Spencerbf437722004-08-15 08:19:46 +0000369 error("Invalid top level configuration item");
370 break;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000371 }
372 }
373
374 void parseFile() {
Reid Spencerbf437722004-08-15 08:19:46 +0000375 while ( next() != EOFTOK ) {
376 if (token == ERRORTOK)
377 error("Invalid token");
378 else if (token != EOLTOK)
379 parseAssignment();
Reid Spencer68fb37a2004-08-14 09:37:15 +0000380 }
381 provider->checkErrors();
382 }
383 };
384
385 void
386 ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) {
Reid Spencerbf437722004-08-15 08:19:46 +0000387 Parser p;
388 p.token = EOFTOK;
389 p.provider = &provider;
390 p.confDat = &confDat;
Reid Spencerbf437722004-08-15 08:19:46 +0000391 p.parseFile();
Reid Spencer2594c9a2004-08-13 20:21:22 +0000392 }
393}
394
395CompilerDriver::ConfigData*
Reid Spencer68fb37a2004-08-14 09:37:15 +0000396LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) {
397 CompilerDriver::ConfigData* result = 0;
Reid Spencerb38e4052004-08-20 09:24:07 +0000398 std::string dir_name;
Reid Spencer68fb37a2004-08-14 09:37:15 +0000399 if (configDir.empty()) {
Reid Spencerb38e4052004-08-20 09:24:07 +0000400 // Try the environment variable
401 const char* conf = getenv("LLVM_CONFIG_DIR");
402 if (conf) {
403 dir_name = conf;
404 dir_name += "/";
405 if (!::sys::FileReadable(dir_name + ftype))
406 throw "Configuration file for '" + ftype + "' is not available.";
407 } else {
408 // Try the user's home directory
409 const char* home = getenv("HOME");
410 if (home) {
411 dir_name = home;
412 dir_name += "/.llvm/etc/";
413 if (!::sys::FileReadable(dir_name + ftype)) {
414 // Okay, try the LLVM installation directory
415 dir_name = LLVM_ETCDIR;
416 dir_name += "/";
417 if (!::sys::FileReadable(dir_name + ftype)) {
418 // Okay, try the "standard" place
419 dir_name = "/etc/llvm/";
420 if (!::sys::FileReadable(dir_name + ftype)) {
421 throw "Configuration file for '" + ftype + "' is not available.";
422 }
423 }
424 }
425 }
Reid Spencer68fb37a2004-08-14 09:37:15 +0000426 }
427 } else {
Reid Spencerb38e4052004-08-20 09:24:07 +0000428 dir_name = configDir + "/";
429 if (!::sys::FileReadable(dir_name + ftype)) {
430 throw "Configuration file for '" + ftype + "' is not available.";
Reid Spencer68fb37a2004-08-14 09:37:15 +0000431 }
Reid Spencer2594c9a2004-08-13 20:21:22 +0000432 }
Reid Spencerb38e4052004-08-20 09:24:07 +0000433 FileInputProvider fip( dir_name + ftype );
434 if (!fip.okay()) {
435 throw "Configuration file for '" + ftype + "' is not available.";
436 }
437 result = new CompilerDriver::ConfigData();
438 ParseConfigData(fip,*result);
Reid Spencer68fb37a2004-08-14 09:37:15 +0000439 return result;
Reid Spencer2594c9a2004-08-13 20:21:22 +0000440}
441
442LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider()
443 : Configurations()
444 , configDir()
445{
446 Configurations.clear();
447}
448
449LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider()
450{
451 ConfigDataMap::iterator cIt = Configurations.begin();
452 while (cIt != Configurations.end()) {
453 CompilerDriver::ConfigData* cd = cIt->second;
454 ++cIt;
455 delete cd;
456 }
457 Configurations.clear();
458}
459
460CompilerDriver::ConfigData*
461LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) {
462 CompilerDriver::ConfigData* result = 0;
463 if (!Configurations.empty()) {
464 ConfigDataMap::iterator cIt = Configurations.find(filetype);
465 if ( cIt != Configurations.end() ) {
466 // We found one in the case, return it.
467 result = cIt->second;
468 }
469 }
470 if (result == 0) {
471 // The configuration data doesn't exist, we have to go read it.
472 result = ReadConfigData(filetype);
473 // If we got one, cache it
474 if ( result != 0 )
475 Configurations.insert(std::make_pair(filetype,result));
476 }
477 return result; // Might return 0
478}
479
480// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab