]> git.tdb.fi Git - builder.git/blob - source/package.cpp
Add basic support for tools (not configurable yet)
[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         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         conf_done(false)
193 {
194         for(vector<string>::const_iterator i=info.begin(); i!=info.end(); ++i)
195         {
196                 if(!i->compare(0, 2, "-I"))
197                         export_binfo.incpath.push_back(i->substr(2));
198                 else if(!i->compare(0, 2, "-D"))
199                         export_binfo.defines.push_back(i->substr(2));
200                 else if(!i->compare(0, 2, "-L"))
201                         export_binfo.libpath.push_back(i->substr(2));
202                 else if(!i->compare(0, 2, "-l"))
203                         export_binfo.libs.push_back(i->substr(2));
204         }
205 }
206
207 /**
208 Initializes configuration options and loads cached values.
209 */
210 void Package::init_config()
211 {
212         config.add_option("profile",    "default", "Configuration profile");
213         config.add_option("tempdir",    "temp",    "Directory for storing temporary files");
214         config.add_option("outdir",     ".",       "Directory to put build results in");
215         config.add_option("optimize",   "0",       "Apply compiler optimizations");
216         config.add_option("debug",      "0",       "Produce debugging symbols");
217         config.add_option("cpu",        "auto",    "CPU type to optimize for");
218         config.add_option("arch",       "native",  "Architecture for cross-compiling");
219         config.add_option("staticlibs", "local",   "Use static libraries");
220
221         const char *home=getenv("HOME");
222         unsigned flags=get_install_flags();
223         if(flags)
224                 config.add_option("prefix",     string(home)+"/local"/*"/usr"*/,            "Installation prefix");
225         /*if(flags&INCLUDE)
226                 config.add_option("includedir", "$prefix/include", "Header installation directory");
227         if(flags&BIN)
228                 config.add_option("includedir", "$prefix/bin",     "Binary installation directory");
229         if(flags&LIB)
230                 config.add_option("includedir", "$prefix/lib",     "Library installation directory");
231         if(flags&DATA)
232                 config.add_option("includedir", "$prefix/share",   "Data installation directory");*/
233
234         for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
235                 if(i->get_package() && i->get_package()->get_need_path())
236                         config.add_option(i->get_name()+"_path", "", "Path for "+i->get_name());
237
238         config.set_source(source);
239 }
240
241 /**
242 Fills in build info based on configuration.  All required packages must be
243 configured when this is called.
244 */
245 void Package::create_build_info()
246 {
247         if(buildable)
248         {
249                 for(PkgRefList::iterator i=requires.begin(); i!=requires.end(); ++i)
250                 {
251                         Package *pkg=i->get_package();
252                         if(!pkg)
253                                 continue;
254                         build_info.add(pkg->get_exported_binfo());
255                         //XXX We probably really only want to pass cflags and defines through
256                         export_binfo.add(pkg->get_exported_binfo());
257                 }
258         
259                 build_info.cflags.push_back("-Wall");
260                 build_info.cflags.push_back("-Wshadow");
261                 build_info.cflags.push_back("-Wextra");
262                 build_info.cflags.push_back("-Wpointer-arith");
263                 build_info.cflags.push_back("-Wconversion");
264                 build_info.cflags.push_back("-Werror");
265
266                 unsigned flags=get_install_flags();
267
268                 if(flags&INCLUDE)
269                         export_binfo.incpath.push_back((Path::Path(config.get_option("prefix").value)/"include").str());
270                 if(flags&LIB)
271                         export_binfo.libpath.push_back((Path::Path(config.get_option("prefix").value)/"lib").str());
272
273                 string optimize=config.get_option("optimize").value;
274                 if(lexical_cast<unsigned>(optimize))
275                 {
276                         build_info.cflags.push_back("-O"+optimize);
277                         build_info.ldflags.push_back("-O"+optimize);
278                         string cpu=config.get_option("cpu").value;
279                         if(cpu!="auto")
280                                 build_info.cflags.push_back("-march="+cpu);
281                 }
282
283                 if(lexical_cast<bool>(config.get_option("debug").value))
284                 {
285                         build_info.cflags.push_back("-ggdb");
286                         build_info.defines.push_back("DEBUG");
287                 }
288
289                 build_info.unique();
290
291                 for(list<Component>::iterator i=components.begin(); i!=components.end(); ++i)
292                 {
293                         i->create_build_info();
294                         if(i->get_type()==Component::LIBRARY)
295                                 export_binfo.libs.push_back(i->get_name());
296                 }
297         }
298         else if(name=="fmod4")
299         {
300                 export_binfo.libs.push_back("fmodex");
301                 if(!path.empty())
302                 {
303                         export_binfo.libpath.push_back((path/"api"/"lib").str());
304                         export_binfo.incpath.push_back((path/"api"/"inc").str());
305                 }
306         }
307         export_binfo.unique();
308 }
309
310 Package::Loader::Loader(Package &p):
311         pkg(p)
312 {
313         add("version",     &Package::version);
314         add("description", &Package::description);
315         add("require",     &Loader::require);
316         add("program",     &Loader::program);
317         add("library",     &Loader::library);
318         add("module",      &Loader::module);
319         add("headers",     &Loader::headers);
320         add("build_info",  &Loader::build_info);
321 }
322
323 void Package::Loader::require(const string &n)
324 {
325         pkg.requires.push_back(PackageRef(pkg.builder, n));
326 }
327
328 void Package::Loader::program(const string &n)
329 {
330         Component prog(pkg, Component::PROGRAM, n);
331         load_sub(prog);
332         pkg.components.push_back(prog);
333 }
334
335 void Package::Loader::library(const string &n)
336 {
337         Component prog(pkg, Component::LIBRARY, n);
338         load_sub(prog);
339         pkg.components.push_back(prog);
340 }
341
342 void Package::Loader::module(const string &n)
343 {
344         Component prog(pkg, Component::MODULE, n);
345         load_sub(prog);
346         pkg.components.push_back(prog);
347 }
348
349 void Package::Loader::headers(const string &n)
350 {
351         Component prog(pkg, Component::HEADERS, n);
352         load_sub(prog);
353         pkg.components.push_back(prog);
354 }
355
356 void Package::Loader::build_info()
357 {
358         load_sub(pkg.build_info);
359 }