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