]> git.tdb.fi Git - builder.git/blob - source/component.cpp
4e7f547f36f7d05b9212a09f957907e1c79c75a8
[builder.git] / source / component.cpp
1 #include <algorithm>
2 #include <msp/fs/dir.h>
3 #include <msp/fs/stat.h>
4 #include <msp/fs/utils.h>
5 #include <msp/io/print.h>
6 #include <msp/strings/lexicalcast.h>
7 #include "booleanevaluator.h"
8 #include "builder.h"
9 #include "component.h"
10 #include "csourcefile.h"
11 #include "datapack.h"
12 #include "executable.h"
13 #include "file.h"
14 #include "objectfile.h"
15 #include "sharedlibrary.h"
16 #include "sourcepackage.h"
17 #include "staticlibrary.h"
18 #include "tarball.h"
19 #include "target.h"
20 #include "tool.h"
21 #include "toolchain.h"
22
23 using namespace std;
24 using namespace Msp;
25
26 Component::Component(SourcePackage &p, Type t, const string &n):
27         package(p),
28         type(t),
29         name(n),
30         install(false),
31         deflt(true)
32 { }
33
34 void Component::prepare()
35 {
36         for(Package::Requirements::const_iterator i=requires.begin(); i!=requires.end(); ++i)
37                 (*i)->prepare();
38 }
39
40 void Component::create_build_info()
41 {
42         BuildInfo final_build_info;
43
44         const Package::Requirements &pkg_reqs = package.get_required_packages();
45         Package::Requirements direct_reqs = requires;
46         direct_reqs.insert(direct_reqs.end(), pkg_reqs.begin(), pkg_reqs.end());
47
48         Package::Requirements all_reqs = direct_reqs;
49         for(Package::Requirements::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
50         {
51                 BuildInfo::UpdateLevel level = BuildInfo::CHAINED;
52                 if(find(direct_reqs.begin(), direct_reqs.end(), *i)!=direct_reqs.end())
53                         level = BuildInfo::DEPENDENCY;
54                 final_build_info.update_from((*i)->get_exported_build_info(), level);
55
56                 const Package::Requirements &reqs = (*i)->get_required_packages();
57                 for(Package::Requirements::const_iterator j=reqs.begin(); j!=reqs.end(); ++j)
58                         if(find(all_reqs.begin(), all_reqs.end(), *j)==all_reqs.end())
59                                 all_reqs.push_back(*j);
60         }
61
62         final_build_info.update_from(package.get_build_info());
63         final_build_info.update_from(build_info);
64         build_info = final_build_info;
65
66         for(BuildInfo::PathList::iterator i=build_info.incpath.begin(); i!=build_info.incpath.end(); ++i)
67                 *i = (package.get_source_directory() / *i).str();
68         for(BuildInfo::PathList::iterator i=build_info.libpath.begin(); i!=build_info.libpath.end(); ++i)
69                 *i = (package.get_source_directory() / *i).str();
70
71         for(UseList::const_iterator i=uses.begin(); i!=uses.end(); ++i)
72         {
73                 /* Select an include path that contains all the sources for this and the
74                 used component.  This should produce a sensible result in most cases. */
75                 FS::Path base;
76                 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
77                         base = base.empty() ? *j : FS::common_ancestor(base, *j);
78                 const SourceList &use_sources = (*i)->get_sources();
79                 for(SourceList::const_iterator j=use_sources.begin(); j!=use_sources.end(); ++j)
80                         base = FS::common_ancestor(base, *j);
81                 build_info.incpath.push_back(base);
82                 build_info.libs.push_back((*i)->get_name());
83                 if(!(*i)->get_install())
84                 {
85                         build_info.libmodes[(*i)->get_name()] = BuildInfo::STATIC;
86                         build_info.libpath.push_back((*i)->get_package().get_source_directory());
87                 }
88         }
89
90         if(type==LIBRARY || type==MODULE)
91                 if(build_info.libmode<BuildInfo::DYNAMIC)
92                         build_info.libmode = BuildInfo::DYNAMIC;
93 }
94
95 BuildInfo Component::get_build_info_for_path(const FS::Path &path) const
96 {
97         // XXX Cache these and check that the directories actually exist before adding them
98         BuildInfo binfo = build_info;
99         if(!overlays.empty())
100         {
101                 FS::Path dir = FS::dirname(path);
102                 string last = FS::basename(dir);
103                 for(OverlayList::const_iterator i=overlays.begin(); i!=overlays.end(); ++i)
104                         if(last==*i)
105                         {
106                                 dir = FS::dirname(dir);
107                                 break;
108                         }
109
110                 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
111                         if(dir==*i)
112                         {
113                                 binfo.local_incpath.push_back(dir);
114                                 for(OverlayList::const_iterator j=overlays.begin(); j!=overlays.end(); ++j)
115                                         binfo.local_incpath.push_back(*i/ *j);
116                         }
117         }
118         return binfo;
119 }
120
121 void Component::create_targets() const
122 {
123         Builder &builder = package.get_builder();
124         BuildGraph &build_graph = builder.get_build_graph();
125         const Toolchain &toolchain = builder.get_toolchain();
126
127         SourceList source_filenames = collect_source_files();
128
129         string inst_loc;
130         if(type==TARBALL)
131         {
132                 Tool &tar = toolchain.get_tool("TAR");
133
134                 list<Target *> files;
135                 for(SourceList::const_iterator i=source_filenames.begin(); i!=source_filenames.end(); ++i)
136                 {
137                         FileTarget *file = builder.get_vfs().get_target(*i);
138                         if(!file)
139                                 file = new File(builder, package, *i);
140                         files.push_back(file);
141                 }
142
143                 string tarname = name;
144                 if(name=="@src")
145                 {
146                         tarname = package.get_name()+"-"+package.get_version();
147                         files.insert(files.begin(), &package.get_build_file());
148
149                         const BuildGraph::TargetMap &targets = build_graph.get_targets();
150                         for(BuildGraph::TargetMap::const_iterator i=targets.begin(); i!=targets.end(); ++i)
151                                 if(i->second->get_package()==&package && !i->second->is_buildable())
152                                         if(find(files.begin(), files.end(), i->second)==files.end())
153                                                 files.push_back(i->second);
154                 }
155
156                 Target *result = tar.create_target(files, tarname);
157
158                 build_graph.get_target("tarballs")->add_dependency(*result);
159
160                 return;
161         }
162         else if(type==INSTALL)
163         {
164                 Target *inst = build_graph.get_target("install");
165                 Tool &copy = toolchain.get_tool("CP");
166                 for(SourceList::const_iterator i=source_filenames.begin(); i!=source_filenames.end(); ++i)
167                 {
168                         FileTarget *ft;
169                         if(Target *tgt = builder.get_vfs().get_target(*i))
170                                 ft = dynamic_cast<FileTarget *>(tgt);
171                         else
172                                 ft = new File(builder, package, *i);
173                         inst->add_dependency(*copy.create_target(*ft, name));
174                 }
175         }
176         else if(type==DATAPACK)
177         {
178                 Tool &dcomp = toolchain.get_tool("DATA");
179
180                 list<Target *> files;
181                 for(SourceList::const_iterator i=source_filenames.begin(); i!=source_filenames.end(); ++i)
182                 {
183                         string ext = FS::extpart(FS::basename(*i));
184                         if(ext==".mdt")
185                         {
186                                 Target *src = dcomp.create_source(*this, *i);
187                                 files.push_back(dcomp.create_target(*src, "collection"));
188                         }
189                         else if(Target *tgt = builder.get_vfs().get_target(*i))
190                                 files.push_back(tgt);
191                         else
192                                 files.push_back(new File(builder, package, *i));
193                 }
194
195                 Target *result = dcomp.create_target(files, "pack");
196
197                 build_graph.add_primary_target(*result);
198                 if(install)
199                         build_graph.add_installed_target(*result);
200         }
201
202         if(type==PROGRAM || type==LIBRARY || type==MODULE)
203         {
204                 list<Target *> objs;
205                 const Toolchain &pkg_tools = package.get_toolchain();
206                 for(SourceList::const_iterator i=source_filenames.begin(); i!=source_filenames.end(); ++i)
207                 {
208                         string ext = FS::extpart(FS::basename(*i));
209                         Target *src = 0;
210
211                         Tool *gen = pkg_tools.get_tool_for_suffix(ext);
212                         if(gen)
213                         {
214                                 Target *tmpl = gen->create_source(*this, *i);
215                                 if(tmpl)
216                                 {
217                                         src = gen->create_target(*tmpl);
218                                         ext = FS::extpart(FS::basename(dynamic_cast<FileTarget &>(*src).get_path()));
219                                 }
220                         }
221
222                         Tool *tool = toolchain.get_tool_for_suffix(ext, true);
223                         if(tool)
224                         {
225                                 if(!src)
226                                         src = tool->create_source(*this, *i);
227                                 if(!src)
228                                         continue;
229
230                                 if(tool->accepts_suffix(ext))
231                                 {
232                                         Target *obj = tool->create_target(*src);
233                                         objs.push_back(obj);
234                                 }
235
236                                 if(type==LIBRARY && install)
237                                 {
238                                         if(dynamic_cast<FileTarget *>(src)->is_installable())
239                                                 build_graph.add_installed_target(*src);
240
241                                         const Target::Dependencies &side_effects = src->get_side_effects();
242                                         for(Target::Dependencies::const_iterator j=side_effects.begin(); j!=side_effects.end(); ++j)
243                                                 if(dynamic_cast<FileTarget *>(*j)->is_installable())
244                                                         build_graph.add_installed_target(**j);
245                                 }
246                         }
247                 }
248
249                 Tool &linker = toolchain.get_tool("LINK");
250
251                 list<Target *> results;
252                 if(type==LIBRARY)
253                 {
254                         Tool &archiver = toolchain.get_tool("AR");
255                         results.push_back(linker.create_target(objs, "shared"));
256                         results.push_back(archiver.create_target(objs));
257                 }
258                 else if(type==MODULE)
259                         results.push_back(linker.create_target(objs, "shared"));
260                 else
261                         results.push_back(linker.create_target(objs));
262
263                 for(list<Target *>::const_iterator i=results.begin(); i!=results.end(); ++i)
264                 {
265                         build_graph.add_primary_target(**i);
266                         if(install)
267                                 build_graph.add_installed_target(**i);
268                 }
269         }
270 }
271
272 Component::SourceList Component::collect_source_files() const
273 {
274         SourceList files;
275         for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
276         {
277                 FS::Path path(*i);
278                 if(FS::is_dir(path))
279                 {
280                         SourceList dirs;
281                         dirs.push_back(path);
282                         for(OverlayList::const_iterator j=overlays.begin(); j!=overlays.end(); ++j)
283                         {
284                                 FS::Path opath = path / *j;
285                                 if(FS::is_dir(opath))
286                                         dirs.push_back(opath);
287                         }
288                         for(SourceList::const_iterator j=dirs.begin(); j!=dirs.end(); ++j)
289                         {
290                                 package.get_builder().get_logger().log("files", format("Traversing %s", *j));
291                                 list<string> sfiles = list_files(*j);
292                                 for(list<string>::iterator k=sfiles.begin(); k!=sfiles.end(); ++k)
293                                         files.push_back(*j / *k);
294                         }
295                 }
296                 else
297                 {
298                         files.push_back(path);
299                         for(OverlayList::const_iterator j=overlays.begin(); j!=overlays.end(); ++j)
300                         {
301                                 FS::Path opath = FS::dirname(path)/ *j/FS::basename(path);
302                                 if(FS::is_reg(opath))
303                                         files.push_back(opath);
304                         }
305                 }
306         }
307
308         return files;
309 }
310
311
312 Component::Loader::Loader(Component &c):
313         DataFile::ObjectLoader<Component>(c)
314 {
315         add("if_arch",         &Loader::if_arch);
316         add("if_feature",      &Loader::if_feature);
317         add("overlay",         &Loader::overlay);
318         add("source",          &Loader::source);
319         add("install",         &Component::install);
320         add("install_map",     &Loader::install_map);
321         add("build_info",      &Loader::build_info);
322         add("require",         &Loader::require);
323         add("default",         &Component::deflt);
324         add("use",             &Loader::use);
325 }
326
327 void Component::Loader::build_info()
328 {
329         load_sub(obj.build_info);
330 }
331
332 void Component::Loader::if_arch(const string &cond)
333 {
334         BooleanEvaluator eval(sigc::hide<1>(sigc::mem_fun(&obj.package.get_builder().get_current_arch(), &Architecture::match_name)), false);
335         bool match = eval.evaluate(cond);
336         obj.package.get_builder().get_logger().log("configure",
337                 format("%s/%s: arch %s %smatched", obj.package.get_name(), obj.name, cond, (match ? "" : "not ")));
338         if(match)
339                 load_sub_with(*this);
340 }
341
342 void Component::Loader::if_feature(const string &cond)
343 {
344         BooleanEvaluator eval(sigc::mem_fun(&obj.package, &SourcePackage::match_feature));
345         bool match = eval.evaluate(cond);
346         obj.package.get_builder().get_logger().log("configure",
347                 format("%s/%s: feature %s %smatched", obj.package.get_name(), obj.name, cond, (match ? "" : "not ")));
348         if(match)
349                 load_sub_with(*this);
350 }
351
352 void Component::Loader::install_map()
353 {
354         load_sub(obj.install_map, obj.package.get_source_directory());
355 }
356
357 void Component::Loader::overlay(const string &o)
358 {
359         obj.overlays.push_back(o);
360 }
361
362 void Component::Loader::require(const string &n)
363 {
364         Package *req = obj.package.get_builder().get_package_manager().find_package(n);
365         if(req)
366                 obj.requires.push_back(req);
367         else
368                 obj.problems.push_back(format("Required package %s not found", n));
369 }
370
371 void Component::Loader::source(const string &s)
372 {
373         obj.sources.push_back((obj.package.get_source_directory()/s).str());
374 }
375
376 void Component::Loader::use(const string &n)
377 {
378         const SourcePackage::ComponentList &components = obj.package.get_components();
379         for(SourcePackage::ComponentList::const_iterator i=components.begin(); i!=components.end(); ++i)
380                 if(i->get_name()==n && i->get_type()==LIBRARY)
381                 {
382                         obj.uses.push_back(&*i);
383                         return;
384                 }
385         throw invalid_argument("Component::Loader::use");
386 }