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