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