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