]> git.tdb.fi Git - builder.git/blob - source/package.cpp
Add Id tag to all files
[builder.git] / source / package.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/strings/lexicalcast.h>
9 #include <msp/strings/utils.h>
10 #include "builder.h"
11 #include "misc.h"
12 #include "package.h"
13
14 using namespace std;
15 using namespace Msp;
16
17 #include <iostream>
18
19 /**
20 Creates a buildable package.
21 */
22 Package::Package(Builder &b, const string &n, const Path::Path &s):
23         builder(b),
24         name(n),
25         buildable(true),
26         source(s),
27         config(*this),
28         conf_done(false),
29         use_pkgconfig(true),
30         need_path(false)
31 {
32         if(builder.get_verbose()>=4)
33                 cout<<"Created buildable package "<<n<<" at "<<s<<'\n';
34 }
35
36 /**
37 Sets the path where the package files were installed.  This is only useful for
38 non-buildable packages that don't use pkg-config.
39 */
40 void Package::set_path(const Msp::Path::Path &p)
41 {
42         path=builder.get_cwd()/p;
43 }
44
45 Msp::Path::Path Package::get_temp_dir() const
46 {
47         return source/config.get_option("tempdir").value/config.get_option("profile").value;
48 }
49
50 Msp::Path::Path Package::get_out_dir() const
51 {
52         return source/config.get_option("outdir").value;
53 }
54
55 /**
56 Checks which kinds of things the components of this package install.
57
58 @return  A bitmask of installed things
59 */
60 unsigned Package::get_install_flags()
61 {
62         unsigned flags=0;
63         for(ComponentList::iterator i=components.begin(); i!=components.end(); ++i)
64         {
65                 if(i->get_install())
66                 {
67                         if(i->get_type()==Component::PROGRAM)
68                                 flags|=BIN;
69                         else if(i->get_type()==Component::LIBRARY || i->get_type()==Component::MODULE)
70                                 flags|=LIB;
71                 }
72                 if(!i->get_install_headers().empty())
73                         flags|=INCLUDE;
74         }
75
76         return flags;
77 }
78
79 LibMode Package::get_library_mode() const
80 {
81         const string &mode=config.get_option("staticlibs").value;
82         if(mode=="all")
83                 return ALL_STATIC;
84         else if(mode=="local")
85                 return LOCAL_STATIC;
86         else if(mode=="none")
87                 return DYNAMIC;
88         else
89                 throw Exception("Unknown library mode");
90 }
91
92 /**
93 Tries to resolve all references to dependency packages.
94 */
95 void Package::resolve_refs()
96 {
97         for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
98         {
99                 Package *pkg=i->resolve();
100                 if(pkg) all_reqs.push_back(pkg);
101         }
102
103         for(ComponentList::iterator i=components.begin(); i!=components.end(); ++i)
104         {
105                 i->resolve_refs();
106                 const PkgRefList &creqs=i->get_requires();
107                 for(PkgRefList::const_iterator j=creqs.begin(); j!=creqs.end(); ++j)
108                         if(j->get_package())
109                                 all_reqs.push_back(j->get_package());
110         }
111
112         for(ConditionList::iterator i=conditions.begin(); i!=conditions.end(); ++i)
113         {
114                 i->resolve_refs();
115                 const PkgRefList &creqs=i->get_requires();
116                 for(PkgRefList::const_iterator j=creqs.begin(); j!=creqs.end(); ++j)
117                         if(j->get_package())
118                                 all_reqs.push_back(j->get_package());
119         }
120 }
121
122 /**
123 Processes configuration options that were most likely obtained from the command
124 line.
125 */
126 void Package::configure(const StringMap &opts, unsigned flag)
127 {
128         if(conf_done)
129                 return;
130
131         if(builder.get_verbose()>=3)
132                 cout<<"Configuring "<<name<<'\n';
133
134         if(buildable)
135         {
136                 init_config();
137
138                 StringMap::const_iterator prof=opts.find("profile");
139                 if(prof!=opts.end() && flag)
140                         config.select_profile(prof->second);
141                 else
142                         config.select_last_profile();
143
144                 if(flag && config.update(opts))
145                 {
146                         if(builder.get_verbose()>=2)
147                                 cout<<"Configuration of "<<name<<" changed\n";
148                         if(!builder.get_dry_run())
149                                 config.save();
150                 }
151
152                 config.finish();
153
154                 for(ConditionList::iterator i=conditions.begin(); i!=conditions.end(); ++i)
155                         if(i->eval())
156                         {
157                                 const PkgRefList &reqs=i->get_requires();
158                                 requires.insert(requires.end(), reqs.begin(), reqs.end());
159                                 build_info.add(i->get_build_info());
160                         }
161
162                 for(PackageList::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
163                 {
164                         if((*i)->get_need_path())
165                                 (*i)->set_path(config.get_option((*i)->get_name()+"_path").value);
166                         (*i)->configure(opts, flag&2);
167                 }
168         }
169
170         create_build_info();
171
172         conf_done=true;
173 }
174
175 /**
176 Creates a non-buildable package with the given name.  Pkg-config is tried first
177 to get build information.  If it fails, a built-in list of known packages is
178 consulted.
179 */
180 Package *Package::create(Builder &b, const string &name)
181 {
182         list<string> argv;
183         argv.push_back("pkg-config");
184         argv.push_back("--silence-errors");
185         argv.push_back("--cflags");
186         argv.push_back("--libs");
187         argv.push_back(name);
188         vector<string> info=split(run_command(argv));
189
190         bool need_path=false;
191         bool use_pkgconfig=true;
192         if(info.empty())
193         {
194                 use_pkgconfig=false;
195
196                 //XXX Put these in an external file
197                 if(name=="opengl")
198                         info.push_back("-lGL");
199                 else if(name=="pthread")
200                         info.push_back("-lpthread");
201                 else if(name=="gmpxx")
202                         info.push_back("-lgmpxx");
203                 else if(name=="fmod4")
204                         need_path=true;
205                 else if(name=="devil")
206                         info.push_back("-lIL");
207                 else if(name=="Xlib")
208                         info.push_back("-lX11");
209                 else
210                         return 0;
211         }
212
213         Package *pkg=new Package(b, name, info);
214         pkg->need_path=need_path;
215         pkg->use_pkgconfig=use_pkgconfig;
216         return pkg;
217 }
218
219 /*** private ***/
220
221 Package::Package(Builder &b, const string &n, const vector<string> &info):
222         builder(b),
223         name(n),
224         buildable(false),
225         config(*this),
226         conf_done(false)
227 {
228         for(vector<string>::const_iterator i=info.begin(); i!=info.end(); ++i)
229         {
230                 if(!i->compare(0, 2, "-I"))
231                         export_binfo.incpath.push_back(i->substr(2));
232                 else if(!i->compare(0, 2, "-D"))
233                         export_binfo.defines.push_back(i->substr(2));
234                 else if(!i->compare(0, 2, "-L"))
235                         export_binfo.libpath.push_back(i->substr(2));
236                 else if(!i->compare(0, 2, "-l"))
237                         export_binfo.libs.push_back(i->substr(2));
238         }
239
240         if(builder.get_verbose()>=4)
241         {
242                 cout<<"Created non-buildable package "<<name<<" with";
243                 for(vector<string>::const_iterator i=info.begin(); i!=info.end(); ++i)
244                         cout<<' '<<*i;
245                 cout<<'\n';
246         }
247 }
248
249 /**
250 Initializes configuration options and loads cached values.
251 */
252 void Package::init_config()
253 {
254         config.add_option("profile",    "default", "Configuration profile");
255         config.add_option("tempdir",    "temp",    "Directory for storing temporary files");
256         config.add_option("outdir",     ".",       "Directory to put build results in");
257         config.add_option("optimize",   "0",       "Apply compiler optimizations");
258         config.add_option("debug",      "0",       "Produce debugging symbols");
259         config.add_option("cpu",        "auto",    "CPU type to optimize for");
260         config.add_option("arch",       "native",  "Architecture for cross-compiling");
261         config.add_option("staticlibs", "local",   "Use static libraries");
262
263         unsigned flags=get_install_flags();
264         if(flags)
265                 config.add_option("prefix",     "$HOME/local",     "Installation prefix");
266         /*if(flags&INCLUDE)
267                 config.add_option("includedir", "$prefix/include", "Header installation directory");
268         if(flags&BIN)
269                 config.add_option("includedir", "$prefix/bin",     "Binary installation directory");
270         if(flags&LIB)
271                 config.add_option("includedir", "$prefix/lib",     "Library installation directory");
272         if(flags&DATA)
273                 config.add_option("includedir", "$prefix/share",   "Data installation directory");*/
274
275         for(FeatureList::iterator i=features.begin(); i!=features.end(); ++i)
276                 config.add_option("with_"+i->name, "0", i->descr);
277
278         for(PackageList::const_iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
279                 if((*i)->get_need_path())
280                         config.add_option((*i)->get_name()+"_path", "", "Path for "+(*i)->get_name());
281 }
282
283 /**
284 Fills in build info based on configuration.  All required packages must be
285 configured when this is called.
286 */
287 void Package::create_build_info()
288 {
289         if(buildable)
290         {
291                 for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
292                 {
293                         Package *pkg=i->get_package();
294                         if(!pkg)
295                                 continue;
296                         const BuildInfo &ebi=pkg->get_exported_binfo();
297                         build_info.add(ebi);
298
299                         export_binfo.cflags.insert(export_binfo.cflags.end(), ebi.cflags.begin(), ebi.cflags.end());
300                         export_binfo.incpath.insert(export_binfo.incpath.end(), ebi.incpath.begin(), ebi.incpath.end());
301                         export_binfo.defines.insert(export_binfo.defines.end(), ebi.defines.begin(), ebi.defines.end());
302                 }
303
304                 build_info.cflags.push_back("-Wall");
305                 build_info.cflags.push_back("-Wshadow");
306                 build_info.cflags.push_back("-Wextra");
307                 build_info.cflags.push_back("-Wpointer-arith");
308                 build_info.cflags.push_back("-Wconversion");
309                 build_info.cflags.push_back("-Werror");
310
311                 unsigned flags=get_install_flags();
312
313                 if(flags&INCLUDE)
314                         export_binfo.incpath.push_back((Path::Path(config.get_option("prefix").value)/"include").str());
315                 if(flags&LIB)
316                         export_binfo.libpath.push_back((Path::Path(config.get_option("prefix").value)/"lib").str());
317
318                 string optimize=config.get_option("optimize").value;
319                 if(lexical_cast<unsigned>(optimize))
320                 {
321                         build_info.cflags.push_back("-O"+optimize);
322                         build_info.ldflags.push_back("-O"+optimize);
323                         string cpu=config.get_option("cpu").value;
324                         if(cpu!="auto")
325                                 build_info.cflags.push_back("-march="+cpu);
326                 }
327
328                 if(lexical_cast<bool>(config.get_option("debug").value))
329                 {
330                         build_info.cflags.push_back("-ggdb");
331                         build_info.defines.push_back("DEBUG");
332                 }
333
334                 for(FeatureList::iterator i=features.begin(); i!=features.end(); ++i)
335                         if(lexical_cast<bool>(config.get_option("with_"+i->name).value))
336                                 build_info.cflags.push_back("-DWITH_"+toupper(i->name));
337
338                 build_info.unique();
339
340                 for(list<Component>::iterator i=components.begin(); i!=components.end(); ++i)
341                 {
342                         i->create_build_info();
343                         if(i->get_type()==Component::LIBRARY)
344                                 export_binfo.libs.push_back(i->get_name());
345                 }
346         }
347         else if(name=="fmod4")
348         {
349                 export_binfo.libs.push_back("fmodex");
350                 if(!path.empty())
351                 {
352                         export_binfo.libpath.push_back((path/"api"/"lib").str());
353                         export_binfo.incpath.push_back((path/"api"/"inc").str());
354                 }
355         }
356         export_binfo.unique();
357 }
358
359 Package::Loader::Loader(Package &p):
360         pkg(p)
361 {
362         add("version",     &Package::version);
363         add("description", &Package::description);
364         add("require",     &Loader::require);
365         add("feature",     &Loader::feature);
366         add("if",          &Loader::condition);
367         add("program",     &Loader::program);
368         add("library",     &Loader::library);
369         add("module",      &Loader::module);
370         add("headers",     &Loader::headers);
371         add("build_info",  &Loader::build_info);
372 }
373
374 void Package::Loader::require(const string &n)
375 {
376         pkg.requires.push_back(PackageRef(pkg.builder, n));
377 }
378
379 void Package::Loader::feature(const string &n, const string &d)
380 {
381         pkg.features.push_back(Feature(n, d));
382 }
383
384 void Package::Loader::condition(const string &c)
385 {
386         Condition cond(pkg, c);
387         load_sub(cond);
388         pkg.conditions.push_back(cond);
389 }
390
391 void Package::Loader::program(const string &n)
392 {
393         Component prog(pkg, Component::PROGRAM, n);
394         load_sub(prog);
395         pkg.components.push_back(prog);
396 }
397
398 void Package::Loader::library(const string &n)
399 {
400         Component prog(pkg, Component::LIBRARY, n);
401         load_sub(prog);
402         pkg.components.push_back(prog);
403 }
404
405 void Package::Loader::module(const string &n)
406 {
407         Component prog(pkg, Component::MODULE, n);
408         load_sub(prog);
409         pkg.components.push_back(prog);
410 }
411
412 void Package::Loader::headers(const string &n)
413 {
414         Component prog(pkg, Component::HEADERS, n);
415         load_sub(prog);
416         pkg.components.push_back(prog);
417 }
418
419 void Package::Loader::build_info()
420 {
421         load_sub(pkg.build_info);
422 }