]> git.tdb.fi Git - builder.git/blob - source/packagemanager.cpp
Move main package tracking to PackageManager
[builder.git] / source / packagemanager.cpp
1 #include <cstdlib>
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/format.h>
7 #include <msp/strings/utils.h>
8 #include <msp/time/units.h>
9 #include <msp/time/utils.h>
10 #include "binarypackage.h"
11 #include "builder.h"
12 #include "externaltask.h"
13 #include "package.h"
14 #include "packagemanager.h"
15
16 using namespace std;
17 using namespace Msp;
18
19 PackageManager::PackageManager(Builder &b):
20         builder(b),
21         no_externals(false),
22         main_pkg(0),
23         env_set(false)
24 { }
25
26 PackageManager::~PackageManager()
27 {
28         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
29                 delete i->second;
30 }
31
32 void PackageManager::append_package_path(const FS::Path &p)
33 {
34         pkg_path.push_back(p);
35 }
36
37 void PackageManager::append_binary_package_path(const FS::Path &p)
38 {
39         binpkg_path.push_back(p);
40 }
41
42 void PackageManager::set_no_externals(bool x)
43 {
44         no_externals = x;
45 }
46
47 void PackageManager::add_package(Package *pkg)
48 {
49         PackageMap::iterator i = packages.find(pkg->get_name());
50         if(i!=packages.end())
51         {
52                 if(i->second!=pkg)
53                         throw invalid_argument("Package name conflict");
54                 else
55                         throw logic_error("Package is already managed");
56         }
57
58         if(packages.empty())
59                 main_pkg = pkg;
60
61         packages.insert(PackageMap::value_type(pkg->get_name(), pkg));
62 }
63
64 Package &PackageManager::get_main_package() const
65 {
66         if(!main_pkg)
67                 throw logic_error("No packages");
68         return *main_pkg;
69 }
70
71 Package *PackageManager::find_package(const string &name)
72 {
73         if(not_found.count(name))
74                 return 0;
75
76         PackageMap::iterator i = packages.find(name);
77         if(i!=packages.end())
78                 return i->second;
79
80         if(!no_externals)
81         {
82                 FS::Path path = get_package_location(name);
83                 if(!path.empty())
84                 {
85                         builder.load_build_file(path/"Build");
86                         i = packages.find(name);
87                         if(i!=packages.end())
88                                 return i->second;
89                 }
90         }
91
92         FS::Path path = get_binary_package_file(name);
93         if(!path.empty())
94         {
95                 builder.load_build_file(path);
96                 i = packages.find(name);
97                 if(i!=packages.end())
98                         return i->second;
99         }
100
101         try
102         {
103                 // Package source not found - create a binary package
104                 string flags_str = run_pkgconfig(name, "flags");
105                 vector<string> flags = split(flags_str);
106                 Package *pkg = BinaryPackage::from_flags(builder, name, flags);
107                 packages.insert(PackageMap::value_type(name, pkg));
108                 return pkg;
109         }
110         catch(...)
111         {
112                 builder.problem(name, "not found");
113                 not_found.insert(name);
114                 return 0;
115         }
116 }
117
118 string PackageManager::run_pkgconfig(const string &pkg, const string &what)
119 {
120         if(!env_set)
121         {
122                 const FS::Path &prefix = builder.get_prefix();
123                 if(prefix.str()!="/usr")
124                 {
125                         FS::Path pcdir = prefix/"lib/pkgconfig";
126                         if(const char *pcp = getenv("PKG_CONFIG_PATH"))
127                         {
128                                 vector<string> path = split(pcp, ':');
129                                 bool found = false;
130                                 for(vector<string>::const_iterator i=path.begin(); (!found && i!=path.end()); ++i)
131                                         found = (*i==pcdir.str());
132                                 if(!found)
133                                 {
134                                         path.push_back(pcdir.str());
135                                         setenv("PKG_CONFIG_PATH", join(path.begin(), path.end(), ":").c_str(), true);
136                                 }
137                         }
138                         else
139                                 setenv("PKG_CONFIG_PATH", pcdir.str().c_str(), true);
140                 }
141         }
142
143         ExternalTask::Arguments argv;
144         argv.push_back("pkg-config");
145         if(what=="cflags" || what=="libs")
146                 argv.push_back("--"+what);
147         else if(what=="flags")
148         {
149                 argv.push_back("--cflags");
150                 argv.push_back("--libs");
151         }
152         else
153                 argv.push_back("--variable="+what);
154         argv.push_back(pkg);
155
156         builder.get_logger().log("auxcommands", format("Running %s", join(argv.begin(), argv.end())));
157
158         return ExternalTask::run_and_capture_output(argv);
159 }
160
161 FS::Path PackageManager::get_package_location(const string &name)
162 {
163         builder.get_logger().log("packagemgr", format("Looking for source package %s", name));
164
165         try
166         {
167                 // Try to get source directory with pkgconfig
168                 string srcdir = strip(run_pkgconfig(name, "source"));
169                 if(!srcdir.empty() && FS::exists(FS::Path(srcdir)/"Build"))
170                         return srcdir;
171         }
172         catch(...)
173         { }
174
175         if(pkg_dirs.empty())
176         {
177                 for(SearchPath::const_iterator i=pkg_path.begin(); i!=pkg_path.end(); ++i)
178                 {
179                         builder.get_logger().log("files", format("Traversing %s", *i));
180                         list<string> files = list_files(*i);
181                         unsigned count = 0;
182                         for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
183                         {
184                                 FS::Path full = *i / *j;
185                                 if(FS::exists(full/"Build"))
186                                 {
187                                         pkg_dirs.push_back(full);
188                                         ++count;
189                                 }
190                         }
191
192                         builder.get_logger().log("packagemgr", format("%d source packages found in %s", count, *i));
193                 }
194
195                 builder.get_logger().log("packagemgr", format("%d source packages found", pkg_dirs.size()));
196         }
197
198         bool msp = !name.compare(0, 3, "msp");
199         for(SearchPath::const_iterator i=pkg_dirs.begin(); i!=pkg_dirs.end(); ++i)
200         {
201                 string base = FS::basename(*i);
202                 unsigned dash = base.rfind('-');
203
204                 if(!base.compare(0, dash, name))
205                         return *i;
206                 else if(msp && !base.compare(0, dash, name, 3, string::npos))
207                         return *i;
208         }
209
210         return FS::Path();
211 }
212
213 FS::Path PackageManager::get_binary_package_file(const string &name)
214 {
215         builder.get_logger().log("packagemgr", format("Looking for binary package %s", name));
216
217         if(binpkg_files.empty())
218         {
219                 for(list<FS::Path>::const_iterator i=binpkg_path.begin(); i!=binpkg_path.end(); ++i)
220                 {
221                         builder.get_logger().log("files", format("Traversing %s", *i));
222                         list<string> files = list_filtered(*i, "\\.bpk$");
223                         for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
224                                 binpkg_files.push_back(*i / *j);
225                         builder.get_logger().log("packagemgr", format("%d binary packages found in %s", files.size(), *i));
226                 }
227
228                 builder.get_logger().log("packagemgr", format("%d binary packages found", binpkg_files.size()));
229         }
230
231         for(SearchPath::const_iterator i=binpkg_files.begin(); i!=binpkg_files.end(); ++i)
232         {
233                 string base = FS::basepart(FS::basename(*i));
234                 if(base==name)
235                         return *i;
236         }
237
238         return FS::Path();
239 }