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