]> git.tdb.fi Git - builder.git/blob - source/packagemanager.cpp
Refactor logger to do message formatting internally
[builder.git] / source / packagemanager.cpp
1 #include <cstdlib>
2 #include <msp/core/algorithm.h>
3 #include <msp/fs/dir.h>
4 #include <msp/fs/stat.h>
5 #include <msp/fs/utils.h>
6 #include <msp/io/print.h>
7 #include <msp/strings/utils.h>
8 #include <msp/time/timedelta.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(const auto &kvp: packages)
29                 delete kvp.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         auto 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({ pkg->get_name(), pkg });
62 }
63
64 Package *PackageManager::get_package(const string &name) const
65 {
66         auto i = packages.find(name);
67         if(i!=packages.end())
68                 return i->second;
69
70         return 0;
71 }
72
73 Package &PackageManager::get_main_package() const
74 {
75         if(!main_pkg)
76                 throw logic_error("No packages");
77         return *main_pkg;
78 }
79
80 Package *PackageManager::find_package(const string &name)
81 {
82         if(not_found.count(name))
83                 return 0;
84
85         if(Package *pkg = get_package(name))
86                 return pkg;
87
88         if(!no_externals)
89         {
90                 FS::Path path = get_package_location(name);
91                 if(!path.empty())
92                 {
93                         builder.load_build_file(path/"Build");
94                         auto i = packages.find(name);
95                         if(i!=packages.end())
96                                 return i->second;
97                 }
98         }
99
100         FS::Path path = get_binary_package_file(name);
101         if(!path.empty())
102         {
103                 builder.load_build_file(path);
104                 if(Package *pkg = get_package(name))
105                         return pkg;
106         }
107
108         try
109         {
110                 // Package source not found - create a binary package
111                 string flags_str = run_pkgconfig(name, "flags");
112                 BinaryPackage::Flags flags = split(flags_str);
113                 flags_str = run_pkgconfig(name, "staticflags");
114                 BinaryPackage::Flags static_flags = split(flags_str);
115                 Package *pkg = BinaryPackage::from_flags(builder, name, flags, static_flags);
116                 packages.insert({ name, pkg });
117                 return pkg;
118         }
119         catch(...)
120         {
121                 not_found.insert(name);
122                 return 0;
123         }
124 }
125
126 string PackageManager::run_pkgconfig(const string &pkg, const string &what)
127 {
128 #ifndef _WIN32
129         if(!env_set)
130         {
131                 const FS::Path &prefix = builder.get_prefix();
132                 if(prefix.str()!="/usr")
133                 {
134                         FS::Path pcdir = prefix/"lib/pkgconfig";
135                         if(const char *pcp = getenv("PKG_CONFIG_PATH"))
136                         {
137                                 vector<string> path = split(pcp, ':');
138                                 if(!any_equals(path, pcdir.str()))
139                                 {
140                                         path.push_back(pcdir.str());
141                                         setenv("PKG_CONFIG_PATH", join(path.begin(), path.end(), ":").c_str(), true);
142                                 }
143                         }
144                         else
145                                 setenv("PKG_CONFIG_PATH", pcdir.str().c_str(), true);
146                 }
147         }
148
149         ExternalTask::Arguments argv;
150         argv.push_back("pkg-config");
151         if(what=="cflags" || what=="libs")
152                 argv.push_back("--"+what);
153         else if(what=="flags" || what=="staticflags")
154         {
155                 argv.push_back("--cflags");
156                 argv.push_back("--libs");
157                 if(what=="staticflags")
158                         argv.push_back("--static");
159         }
160         else
161                 argv.push_back("--variable="+what);
162         argv.push_back(pkg);
163
164         builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
165
166         return ExternalTask::run_and_capture_output(argv);
167 #else
168         (void)pkg;
169         (void)what;
170         return string();
171 #endif
172 }
173
174 FS::Path PackageManager::get_package_location(const string &name)
175 {
176         builder.get_logger().log("packagemgr", "Looking for source package %s", name);
177
178         try
179         {
180                 // Try to get source directory with pkgconfig
181                 string srcdir = strip(run_pkgconfig(name, "source"));
182                 if(!srcdir.empty() && FS::exists(FS::Path(srcdir)/"Build"))
183                         return srcdir;
184         }
185         catch(...)
186         { }
187
188         if(pkg_dirs.empty())
189         {
190                 for(const FS::Path &p: pkg_path)
191                 {
192                         builder.get_logger().log("files", "Traversing %s", p);
193                         unsigned count = 0;
194                         for(const string &f: list_files(p))
195                         {
196                                 FS::Path full = p/f;
197                                 if(FS::exists(full/"Build"))
198                                 {
199                                         pkg_dirs.push_back(full);
200                                         ++count;
201                                 }
202                         }
203
204                         builder.get_logger().log("packagemgr", "%d source packages found in %s", count, p);
205                 }
206
207                 builder.get_logger().log("packagemgr", "%d source packages found", pkg_dirs.size());
208         }
209
210         bool msp = !name.compare(0, 3, "msp");
211         for(const FS::Path &p: pkg_dirs)
212         {
213                 string base = FS::basename(p);
214                 unsigned dash = base.rfind('-');
215
216                 if(!base.compare(0, dash, name))
217                         return p;
218                 else if(msp && !base.compare(0, dash, name, 3, string::npos))
219                         return p;
220         }
221
222         return FS::Path();
223 }
224
225 FS::Path PackageManager::get_binary_package_file(const string &name)
226 {
227         builder.get_logger().log("packagemgr", "Looking for binary package %s", name);
228
229         if(binpkg_files.empty())
230         {
231                 for(const FS::Path &p: binpkg_path)
232                 {
233                         builder.get_logger().log("files", "Traversing %s", p);
234                         vector<string> files = list_filtered(p, "\\.bpk$");
235                         for(const string &f: files)
236                                 binpkg_files.push_back(p/f);
237                         builder.get_logger().log("packagemgr", "%d binary packages found in %s", files.size(), p);
238                 }
239
240                 builder.get_logger().log("packagemgr", "%d binary packages found", binpkg_files.size());
241         }
242
243         auto i = find_if(binpkg_files, [&name](const FS::Path &p){ return FS::basepart(FS::basename(p))==name; });
244         if(i!=binpkg_files.end())
245                 return *i;
246
247         return FS::Path();
248 }
249
250 void PackageManager::save_all_caches() const
251 {
252         for(const auto &kvp: packages)
253                 kvp.second->save_caches();
254 }