]> git.tdb.fi Git - builder.git/blob - source/component.cpp
Fix rebuild graph walking over symlinks
[builder.git] / source / component.cpp
1 /* $Id$
2
3 This file is part of builder
4 Copyright © 2006-2010  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <algorithm>
9 #include <msp/core/except.h>
10 #include <msp/fs/dir.h>
11 #include <msp/fs/stat.h>
12 #include <msp/fs/utils.h>
13 #include <msp/io/print.h>
14 #include <msp/strings/lexicalcast.h>
15 #include "builder.h"
16 #include "component.h"
17 #include "datafile.h"
18 #include "executable.h"
19 #include "file.h"
20 #include "header.h"
21 #include "install.h"
22 #include "objectfile.h"
23 #include "sharedlibrary.h"
24 #include "sourcepackage.h"
25 #include "staticlibrary.h"
26 #include "symlink.h"
27 #include "tarball.h"
28 #include "target.h"
29
30 using namespace std;
31 using namespace Msp;
32
33 Component::Component(SourcePackage &p, Type t, const string &n):
34         pkg(p),
35         type(t),
36         name(n),
37         install(false),
38         module_host(0),
39         modular(false),
40         deflt(true)
41 { }
42
43 void Component::create_build_info()
44 {
45         const PackageList &pkg_reqs = pkg.get_requires();
46         PackageList direct_reqs = requires;
47         direct_reqs.insert(direct_reqs.end(), pkg_reqs.begin(), pkg_reqs.end());
48
49         PackageList all_reqs = direct_reqs;
50         for(PackageList::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
51         {
52                 if(find(direct_reqs.begin(), direct_reqs.end(), *i)!=direct_reqs.end())
53                         build_info.add((*i)->get_exported_binfo());
54                 else
55                 {
56                         const BuildInfo &ebi = (*i)->get_exported_binfo();
57                         build_info.cflags.insert(build_info.cflags.end(), ebi.cflags.begin(), ebi.cflags.end());
58                         build_info.incpath.insert(build_info.incpath.end(), ebi.incpath.begin(), ebi.incpath.end());
59                         build_info.defines.insert(build_info.defines.end(), ebi.defines.begin(), ebi.defines.end());
60                 }
61
62                 const PackageList &reqs = (*i)->get_requires();
63                 for(PackageList::const_iterator j=reqs.begin(); j!=reqs.end(); ++j)
64                         if(find(all_reqs.begin(), all_reqs.end(), *j)==all_reqs.end())
65                                 all_reqs.push_back(*j);
66         }
67
68         build_info.add(pkg.get_build_info());
69
70         for(StringList::iterator i=build_info.incpath.begin(); i!=build_info.incpath.end(); ++i)
71                 *i = (pkg.get_source() / *i).str();
72         for(StringList::iterator i=build_info.libpath.begin(); i!=build_info.libpath.end(); ++i)
73                 *i = (pkg.get_source() / *i).str();
74
75         if(pkg.get_library_mode()!=DYNAMIC)
76         {
77                 for(PackageList::iterator i=all_reqs.begin(); i!=all_reqs.end(); ++i)
78                 {
79                         const BuildInfo &ebi = (*i)->get_exported_binfo();
80                         build_info.libpath.insert(build_info.libpath.end(), ebi.libpath.begin(), ebi.libpath.end());
81                 }
82         }
83
84         if(type==PROGRAM)
85         {
86                 string strip = pkg.get_config().get_option("strip").value;
87                 if(lexical_cast<bool>(strip))
88                         build_info.ldflags.push_back("-s");
89         }
90         else if(type==LIBRARY)
91         {
92                 build_info.cflags.push_back("-fPIC");
93         }
94
95         if(modular)
96         {
97                 build_info.ldflags.push_back("-rdynamic");
98                 build_info.libs.push_back("dl");
99         }
100         else if(module_host)
101         {
102                 const PathList &host_src = module_host->get_sources();
103                 for(PathList::const_iterator i=host_src.begin(); i!=host_src.end(); ++i)
104                         build_info.incpath.push_back(i->str());
105         }
106
107         build_info.unique();
108 }
109
110 void Component::create_targets() const
111 {
112         Builder &builder = pkg.get_builder();
113         Target *world = builder.get_target("world");
114         Target *def_tgt = builder.get_target("default");
115
116         PathList files = collect_source_files();
117         list<FileTarget *> inst_list;
118
119         string inst_loc;
120         if(type==TARBALL)
121         {
122                 string tarname = name;
123                 if(name=="@src")
124                         tarname = pkg.get_name()+"-"+pkg.get_version();
125                 TarBall *result = new TarBall(builder, pkg, tarname);
126
127                 if(name=="@src")
128                 {
129                         const TargetMap &targets = builder.get_targets();
130                         for(TargetMap::const_iterator i=targets.begin(); i!=targets.end(); ++i)
131                                 if(i->second->get_package()==&pkg && !i->second->is_buildable())
132                                         result->add_depend(i->second);
133                         files.push_back(pkg.get_source()/"Build");
134                 }
135
136                 for(PathList::const_iterator i=files.begin(); i!=files.end(); ++i)
137                 {
138                         FileTarget *ft;
139                         if(Target *tgt = builder.get_target(i->str()))
140                                 ft = dynamic_cast<FileTarget *>(tgt);
141                         else
142                                 ft = new File(builder, *i);
143                         result->add_depend(ft);
144                 }
145
146                 Target *tarbls_tgt = builder.get_target("tarballs");
147                 tarbls_tgt->add_depend(result);
148
149                 return;
150         }
151         else if(type==INSTALL)
152         {
153                 inst_loc = name;
154                 for(PathList::const_iterator i=files.begin(); i!=files.end(); ++i)
155                 {
156                         FileTarget *ft;
157                         if(Target *tgt = builder.get_target(i->str()))
158                                 ft = dynamic_cast<FileTarget *>(tgt);
159                         else
160                                 ft = new File(builder, pkg, *i);
161                         inst_list.push_back(ft);
162                 }
163         }
164         else if(type==DATAFILE)
165         {
166                 File *source;
167                 if(Target *tgt = builder.get_target(files.front().str()))
168                         source = dynamic_cast<File *>(tgt);
169                 else
170                         source = new File(builder, pkg, files.front());
171                 ::DataFile *result = new ::DataFile(builder, *this, *source);
172
173                 if(&pkg==builder.get_main_package() && deflt)
174                         def_tgt->add_depend(result);
175                 else
176                         world->add_depend(result);
177                 if(install)
178                         inst_list.push_back(result);
179         }
180         else
181         {
182                 for(PathList::const_iterator i=files.begin(); i!=files.end(); ++i)
183                 {
184                         string ext = FS::extpart(FS::basename(*i));
185                         if(ext==".h")
186                         {
187                                 FileTarget *hdr = dynamic_cast<FileTarget *>(builder.get_target(i->str()));
188                                 if(!hdr)
189                                         hdr = new Header(builder, *this, i->str());
190
191                                 // Install headers if requested
192                                 if(type==HEADERS && install)
193                                         inst_list.push_back(hdr);
194                         }
195                 }
196         }
197
198         if(type==PROGRAM || type==LIBRARY || type==MODULE)
199         {
200                 list<ObjectFile *> objs;
201                 for(PathList::const_iterator i=files.begin(); i!=files.end(); ++i)
202                 {
203                         string ext = FS::extpart(FS::basename(*i));
204                         if((ext==".cpp" || ext==".cc" || ext==".c"))
205                         {
206                                 SourceFile *src = new SourceFile(builder, *this, i->str());
207                                 ObjectFile *obj = new ObjectFile(builder, *this, *src);
208                                 objs.push_back(obj);
209                         }
210                 }
211
212                 list<FileTarget *> results;
213                 if(type==LIBRARY)
214                 {
215                         results.push_back(new SharedLibrary(builder, *this, objs));
216                         results.push_back(new StaticLibrary(builder, *this, objs));
217                 }
218                 else
219                         results.push_back(new Executable(builder, *this, objs));
220
221                 for(list<FileTarget *>::const_iterator i=results.begin(); i!=results.end(); ++i)
222                 {
223                         if(&pkg==builder.get_main_package() && deflt)
224                                 def_tgt->add_depend(*i);
225                         else
226                                 world->add_depend(*i);
227                         if(install)
228                                 inst_list.push_back(*i);
229                 }
230         }
231
232         Target *inst_tgt = builder.get_target("install");
233         for(list<FileTarget *>::const_iterator i=inst_list.begin(); i!=inst_list.end(); ++i)
234         {
235                 Install *inst = new Install(builder, pkg, **i, inst_loc);
236                 inst_tgt->add_depend(inst);
237
238                 if(type==LIBRARY)
239                         if(SharedLibrary *shlib = dynamic_cast<SharedLibrary *>(*i))
240                                 if(!shlib->get_soname().empty())
241                                         inst_tgt->add_depend(new Symlink(builder, pkg, *inst, shlib->get_name()));
242         }
243 }
244
245 PathList Component::collect_source_files() const
246 {
247         PathList files;
248         for(PathList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
249         {
250                 if(FS::is_dir(*i))
251                 {
252                         list<string> sfiles = list_files(*i);
253                         for(list<string>::iterator j=sfiles.begin(); j!=sfiles.end(); ++j)
254                                 files.push_back(*i / *j);
255                 }
256                 else
257                         files.push_back(*i);
258         }
259
260         return files;
261 }
262
263
264 Component::Loader::Loader(Component &c):
265         comp(c)
266 {
267         add("source",          &Loader::source);
268         add("install",         &Component::install);
269         add("install_headers", &Loader::install_headers);
270         add("build_info",      &Loader::build_info);
271         add("require",         &Loader::require);
272         add("modular",         &Loader::modular);
273         add("host",            &Loader::host);
274         add("default",         &Component::deflt);
275 }
276
277 void Component::Loader::finish()
278 {
279         if(!inst_hdr.empty())
280         {
281                 Component hdrcomp(comp.pkg, HEADERS, inst_hdr);
282                 hdrcomp.sources = comp.sources;
283                 hdrcomp.install = true;
284                 const_cast<ComponentList &>(comp.pkg.get_components()).push_back(hdrcomp);
285         }
286 }
287
288 void Component::Loader::source(const string &s)
289 {
290         comp.sources.push_back(comp.pkg.get_source()/s);
291 }
292
293 void Component::Loader::require(const string &n)
294 {
295         Package *req = comp.pkg.get_builder().get_package(n);
296         if(req)
297                 comp.requires.push_back(req);
298 }
299
300 void Component::Loader::modular()
301 {
302         if(comp.type!=PROGRAM)
303                 throw Exception("Only programs can be modular");
304         comp.modular = true;
305 }
306
307 void Component::Loader::host(const string &n)
308 {
309         const ComponentList &comps = comp.pkg.get_components();
310         for(ComponentList::const_iterator i=comps.begin(); i!=comps.end(); ++i)
311                 if(i->get_name()==n)
312                 {
313                         if(i->get_type()!=PROGRAM || !i->is_modular())
314                                 throw Exception("Module host must be a modular program");
315                         comp.module_host = &*i;
316                         return;
317                 }
318
319         throw KeyError("Unknown component", n);
320 }
321
322 void Component::Loader::install_headers(const string &p)
323 {
324         IO::print("%s: Note: install_headers is deprecated\n", get_source());
325         if(comp.type==HEADERS)
326         {
327                 comp.name = p;
328                 comp.install = true;
329         }
330         else
331                 inst_hdr = p;
332 }
333
334 void Component::Loader::build_info()
335 {
336         load_sub(comp.build_info);
337 }