]> git.tdb.fi Git - builder.git/blob - source/config.cpp
Add missing includes
[builder.git] / source / config.cpp
1 /* $Id$
2
3 This file is part of builder
4 Copyright © 2006-2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <cstdlib>
9 #include <msp/core/except.h>
10 #include <msp/io/except.h>
11 #include <msp/io/file.h>
12 #include <msp/io/print.h>
13 #include <msp/path/utils.h>
14 #include <msp/time/utils.h>
15 #include "builder.h"
16 #include "config.h"
17 #include "sourcepackage.h"
18
19 using namespace std;
20 using namespace Msp;
21
22 Config::Config(SourcePackage &p):
23         package(p),
24         freeze_mtime(false)
25 { }
26
27 /**
28 Adds a configuration option.
29
30 @param   n  Option name
31 @param   v  Default value
32 @param   d  Description
33 */
34 void Config::add_option(const string &n, const string &v, const string &d)
35 {
36         options.insert(OptionMap::value_type(n, Option(n, v, d)));
37 }
38
39 /**
40 Gets the given option from the configuration.  If the option doesn't exist,
41 an Exception is thrown.
42 */
43 const Config::Option &Config::get_option(const string &name) const
44 {
45         OptionMap::const_iterator i=options.find(name);
46         if(i==options.end())
47                 throw Exception("Tried to access nonexistent option "+name);
48
49         return i->second;
50 }
51
52 /**
53 Checks whether an option with the given name exists.
54 */
55 bool Config::is_option(const string &name) const
56 {
57         return options.count(name);
58 }
59
60 /**
61 Selects the last profile used.  If the profile cache file is not present, the
62 default profile is assumed.
63 */
64 void Config::select_last_profile()
65 {
66         try
67         {
68                 IO::File in((package.get_source()/".profile.cache").str());
69                 string profile;
70                 in.getline(profile);
71                 set_option("profile", profile);
72         }
73         catch(const IO::FileNotFound &)
74         { }
75
76         freeze_mtime=true;
77         package.get_builder().apply_profile_template(*this, get_option("profile").value);
78         freeze_mtime=false;
79
80         load();
81 }
82
83 /**
84 Selects the given profile.  The profile cache file is updated as well, unless
85 --dry-run was given.
86 */
87 void Config::select_profile(const string &profile)
88 {
89         set_option("profile", profile);
90
91         if(!package.get_builder().get_dry_run())
92         {
93                 IO::File out((package.get_source()/".profile.cache").str(), IO::M_WRITE);
94                 IO::print(out, "%s\n", profile);
95         }
96
97         freeze_mtime=true;
98         package.get_builder().apply_profile_template(*this, profile);
99         freeze_mtime=false;
100
101         load();
102 }
103
104 /**
105 Processes options from the given raw option map.  Nonexistent options are
106 ignored.  If any options were changed, the mtime of the configuration is updated
107 to the current time.
108
109 @param   opts  A map to process options from
110
111 @return  Whether any option values were changed
112 */
113 bool Config::update(const StringMap &opts)
114 {
115         bool changed=false;
116         for(StringMap::const_iterator i=opts.begin(); i!=opts.end(); ++i)
117         {
118                 if(set_option(i->first, i->second) && i->first!="profile")
119                         changed=true;
120         }
121
122         if(changed && !freeze_mtime)
123                 mtime=Time::now();
124
125         return changed;
126 }
127
128 /**
129 Expands any variable references in options.
130 */
131 void Config::finish()
132 {
133         for(unsigned n=0; n<20; ++n)
134         {
135                 bool changed=false;
136                 for(OptionMap::iterator i=options.begin(); i!=options.end(); ++i)
137                 {
138                         Option &opt=i->second;
139                         unsigned dollar=0;
140                         while((dollar=opt.value.find('$', dollar))!=string::npos)
141                         {
142                                 unsigned end;
143                                 string var;
144                                 if(opt.value[dollar+1]=='{')
145                                 {
146                                         end=opt.value.find('}', dollar+2);
147                                         if(end==string::npos)
148                                                 throw Exception("Unterminated variable reference");
149                                         var=opt.value.substr(dollar+2, end-dollar-2);
150                                         ++end;
151                                 }
152                                 else
153                                 {
154                                         for(end=dollar+1; (isalnum(opt.value[end]) && opt.value[end]!='_'); ++end) ;
155                                         var=opt.value.substr(dollar+1, end-dollar-1);
156                                 }
157
158                                 string value;
159                                 if(is_option(var))
160                                         value=get_option(var).value;
161                                 else if(var=="arch")
162                                         value=package.get_builder().get_current_arch().get_name();
163                                 else if(const char *ptr=getenv(var.c_str()))
164                                         value=ptr;
165
166                                 opt.value.replace(dollar, end-dollar, value);
167
168                                 dollar+=value.size();
169                                 changed=true;
170                         }
171                 }
172
173                 if(!changed)
174                         break;
175         }
176 }
177
178 void Config::save() const
179 {
180         Path fn=package.get_source()/".options.cache";
181
182         OptionMap::const_iterator i=options.find("profile");
183         if(i!=options.end())
184                 fn=package.get_source()/(".options."+i->second.value+".cache");
185
186         IO::File out(fn.str(), IO::M_WRITE);
187
188         for(i=options.begin(); i!=options.end(); ++i)
189                 IO::print(out, "option \"%s\" \"%s\";\n", i->second.name, i->second.value);
190 }
191
192 bool Config::set_option(const string &opt, const string &val)
193 {
194         bool result=false;
195
196         OptionMap::iterator i=options.find(opt);
197         if(i!=options.end())
198         {
199                 if(i->second.value!=val)
200                         result=true;
201                 i->second.value=val;
202         }
203
204         return result;
205 }
206
207 void Config::load()
208 {
209         Path fn=package.get_source()/".options.cache";
210
211         OptionMap::iterator i=options.find("profile");
212         if(i!=options.end())
213                 fn=package.get_source()/(".options."+i->second.value+".cache");
214
215         try
216         {
217                 IO::File inf(fn.str());
218                 IO::Buffered in(inf);
219
220                 mtime=Time::TimeStamp::from_unixtime(stat(fn).st_mtime);
221
222                 DataFile::Parser parser(in, fn.str());
223                 Loader loader(*this);
224                 loader.load(parser);
225         }
226         catch(const IO::FileNotFound &)
227         { }
228 }
229
230
231 Config::Option::Option(const string &n, const string &v, const string &d):
232         name(n),
233         defv(v),
234         descr(d),
235         value(v)
236 { }
237
238
239 Config::Loader::Loader(Config &c):
240         conf(c)
241 {
242         add("option", &Loader::option);
243 }
244
245 void Config::Loader::option(const string &n, const string &v)
246 {
247         conf.set_option(n, v);
248 }