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