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