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