]> git.tdb.fi Git - builder.git/blob - source/package.cpp
Refactor package configuration
[builder.git] / source / package.cpp
1 #include <msp/strconv.h>
2 #include <msp/strutils.h>
3 #include "builder.h"
4 #include "misc.h"
5 #include "package.h"
6
7 using namespace std;
8 using namespace Msp;
9
10 #include <iostream>
11
12 /**
13 Creates a buildable package.
14 */
15 Package::Package(Builder &b, const string &n, const Path::Path &s):
16         builder(b),
17         name(n),
18         buildable(true),
19         source(s),
20         conf_done(false),
21         need_path(false)
22 { }
23
24 /**
25 Sets the path where the package files were installed.  This is only useful for
26 non-buildable packages that don't use pkg-config.
27 */
28 void Package::set_path(const Msp::Path::Path &p)
29 {
30         path=builder.get_cwd()/p;
31 }
32
33 /**
34 Tries to resolve all references to dependency packages.
35 */
36 void Package::resolve_refs()
37 {
38         for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
39         {
40                 Package *pkg=i->resolve();
41                 if(pkg) all_reqs.push_back(pkg);
42         }
43         for(ComponentList::iterator i=components.begin(); i!=components.end(); ++i)
44         {
45                 i->resolve_refs();
46                 const PkgRefList &creqs=i->get_requires();
47                 for(PkgRefList::const_iterator j=creqs.begin(); j!=creqs.end(); ++j)
48                         if(j->get_package())
49                                 all_reqs.push_back(j->get_package());
50         }
51 }
52
53 /**
54 Processes configuration options that were most likely obtained from the command
55 line.
56 */
57 void Package::configure(const RawOptionMap &opts, unsigned flag)
58 {
59         if(conf_done || !buildable)
60                 return;
61         
62         if(builder.get_verbose()>=3)
63                 cout<<"Configuring "<<name<<'\n';
64         
65         init_config();
66
67         if(flag && config.process(opts) && !builder.get_dry_run())
68         {
69                 if(builder.get_verbose()>=2)
70                         cout<<"Configuration of "<<name<<" changed.\n";
71                 config.save(source/".options.cache");
72         }
73         
74         for(PackageList::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
75         {
76                 (*i)->configure(opts, flag&2);
77                 if((*i)->get_need_path())
78                         (*i)->set_path(config.get_option((*i)->get_name()+"_path").value);
79         }
80         
81         create_build_info();
82
83         conf_done=true;
84 }
85
86 /**
87 Creates a non-buildable package with the given name.  Pkg-config is tried first
88 to get build information.  If it fails, a built-in list of known packages is
89 consulted.
90 */
91 Package *Package::create(Builder &b, const string &name)
92 {
93         list<string> argv;
94         argv.push_back("pkg-config");
95         argv.push_back("--silence-errors");
96         argv.push_back("--cflags");
97         argv.push_back("--libs");
98         argv.push_back(name);
99         vector<string> info=split(run_command(argv));
100         
101         bool need_path=false;
102         if(info.empty())
103         {
104                 //XXX Put these in an external file
105                 if(name=="opengl")
106                         info.push_back("-lGL");
107                 else if(name=="pthread")
108                         info.push_back("-lpthread");
109                 else if(name=="gmpxx")
110                         info.push_back("-lgmpxx");
111                 else if(name=="fmod4")
112                         need_path=true;
113                 else
114                         return 0;
115         }
116         
117         Package *pkg=new Package(b, name, info);
118         pkg->need_path=need_path;
119         return pkg;
120 }
121
122 /*** private ***/
123
124 Package::Package(Builder &b, const string &n, const vector<string> &info):
125         builder(b),
126         name(n),
127         buildable(false)
128 {
129         for(vector<string>::const_iterator i=info.begin(); i!=info.end(); ++i)
130         {
131                 if(!i->compare(0, 2, "-I"))
132                         export_binfo.incpath.push_back(i->substr(2));
133                 else if(!i->compare(0, 2, "-D"))
134                         export_binfo.defines.push_back(i->substr(2));
135                 else if(!i->compare(0, 2, "-L"))
136                         export_binfo.libpath.push_back(i->substr(2));
137                 else if(!i->compare(0, 2, "-l"))
138                         export_binfo.libs.push_back(i->substr(2));
139         }
140 }
141
142 /**
143 Initializes configuration options and loads cached values.
144 */
145 void Package::init_config()
146 {
147         config.add_option("tempdir",    "temp",   "Directory for storing temporary files");
148         config.add_option("optimize",   "0",      "Apply compiler optimizations");
149         config.add_option("debug",      "0",      "Produce debugging symbols");
150         config.add_option("cpu",        "auto",   "CPU type to optimize for");
151         config.add_option("arch",       "native", "Architecture for cross-compiling");
152         config.add_option("staticlibs", "local",  "Use static libraries");
153
154         const char *home=getenv("HOME");
155         unsigned flags=get_install_flags();
156         if(flags)
157                 config.add_option("prefix",     string(home)+"/local"/*"/usr"*/,            "Installation prefix");
158         /*if(flags&INCLUDE)
159                 config.add_option("includedir", "$prefix/include", "Header installation directory");
160         if(flags&BIN)
161                 config.add_option("includedir", "$prefix/bin",     "Binary installation directory");
162         if(flags&LIB)
163                 config.add_option("includedir", "$prefix/lib",     "Library installation directory");
164         if(flags&DATA)
165                 config.add_option("includedir", "$prefix/share",   "Data installation directory");*/
166
167         for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
168                 if(i->get_package() && i->get_package()->get_need_path())
169                         config.add_option(i->get_name()+"_path", "", "Path for "+i->get_name());
170
171         config.load(source/".options.cache");
172 }
173
174 /**
175 Fills in build info based on configuration.  All required packages must be
176 configured when this is called.
177 */
178 void Package::create_build_info()
179 {
180         if(buildable)
181         {
182                 for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
183                 {
184                         Package *pkg=i->get_package();
185                         if(!pkg)
186                                 continue;
187                         build_info.add(pkg->get_exported_binfo());
188                         //XXX We probably really only want to pass cflags and defines through
189                         export_binfo.add(pkg->get_exported_binfo());
190                 }
191         
192                 build_info.cflags.push_back("-Wall");
193                 build_info.cflags.push_back("-Wshadow");
194                 build_info.cflags.push_back("-Wextra");
195                 build_info.cflags.push_back("-Wpointer-arith");
196                 build_info.cflags.push_back("-Wconversion");
197                 build_info.cflags.push_back("-Werror");
198
199                 unsigned flags=get_install_flags();
200
201                 if(flags&INCLUDE)
202                         export_binfo.incpath.push_back((Path::Path(config.get_option("prefix").value)/"include").str());
203                 if(flags&LIB)
204                         export_binfo.libpath.push_back((Path::Path(config.get_option("prefix").value)/"lib").str());
205
206                 string optimize=config.get_option("optimize").value;
207                 if(strtol(optimize))
208                 {
209                         build_info.cflags.push_back("-O"+optimize);
210                         string cpu=config.get_option("cpu").value;
211                         if(cpu!="auto")
212                                 build_info.cflags.push_back("-march="+cpu);
213                 }
214
215                 if(strtobool(config.get_option("debug").value))
216                 {
217                         build_info.cflags.push_back("-ggdb");
218                         build_info.defines.push_back("DEBUG");
219                 }
220
221                 build_info.unique();
222
223                 for(list<Component>::iterator i=components.begin(); i!=components.end(); ++i)
224                 {
225                         i->create_build_info();
226                         if(i->get_type()==Component::LIBRARY)
227                                 export_binfo.libs.push_back(i->get_name());
228                 }
229         }
230         else if(name=="fmod4")
231         {
232                 export_binfo.libs.push_back("fmodex");
233                 if(!path.empty())
234                 {
235                         export_binfo.libpath.push_back((path/"api"/"lib").str());
236                         export_binfo.incpath.push_back((path/"api"/"inc").str());
237                 }
238         }
239         export_binfo.unique();
240 }
241
242 /**
243 Checks which kinds of things the components of this package install.
244
245 @return  A bitmask of installed things
246 */
247 unsigned Package::get_install_flags()
248 {
249         unsigned flags=0;
250         for(ComponentList::iterator i=components.begin(); i!=components.end(); ++i)
251         {
252                 if(i->get_install())
253                 {
254                         if(i->get_type()==Component::PROGRAM)
255                                 flags|=BIN;
256                         else if(i->get_type()==Component::LIBRARY || i->get_type()==Component::MODULE)
257                                 flags|=LIB;
258                 }
259                 if(!i->get_install_headers().empty())
260                         flags|=INCLUDE;
261         }
262
263         return flags;
264 }
265
266 Package::Loader::Loader(Package &p):
267         pkg(p)
268 {
269         add("version",     &Package::version);
270         add("description", &Package::description);
271         add("require",     &Loader::require);
272         add("program",     &Loader::program);
273         add("library",     &Loader::library);
274         add("headers",     &Loader::headers);
275         add("build_info",  &Loader::build_info);
276 }
277
278 void Package::Loader::require(const string &n)
279 {
280         pkg.requires.push_back(PackageRef(pkg.builder, n));
281 }
282
283 void Package::Loader::program(const string &n)
284 {
285         Component prog(pkg, Component::PROGRAM, n);
286         load_sub(prog);
287         pkg.components.push_back(prog);
288 }
289
290 void Package::Loader::library(const string &n)
291 {
292         Component prog(pkg, Component::LIBRARY, n);
293         load_sub(prog);
294         pkg.components.push_back(prog);
295 }
296
297 void Package::Loader::headers(const string &n)
298 {
299         Component prog(pkg, Component::HEADERS, n);
300         load_sub(prog);
301         pkg.components.push_back(prog);
302 }
303
304 void Package::Loader::build_info()
305 {
306         load_sub(pkg.build_info);
307 }