]> git.tdb.fi Git - builder.git/blob - source/lib/packagemanager.cpp
Make PackageManager::run_pkgconfig fail on Windows
[builder.git] / source / lib / packagemanager.cpp
1 #include <cstdlib>
2 #include <msp/core/algorithm.h>
3 #include <msp/core/except.h>
4 #include <msp/fs/dir.h>
5 #include <msp/fs/stat.h>
6 #include <msp/fs/utils.h>
7 #include <msp/io/print.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()
21 {
22         for(const auto &kvp: packages)
23                 delete kvp.second;
24 }
25
26 void PackageManager::append_package_path(const FS::Path &p)
27 {
28         pkg_path.push_back(p);
29 }
30
31 void PackageManager::append_binary_package_path(const FS::Path &p)
32 {
33         binpkg_path.push_back(p);
34 }
35
36 void PackageManager::set_no_externals(bool x)
37 {
38         no_externals = x;
39 }
40
41 void PackageManager::add_package(Package *pkg)
42 {
43         auto i = packages.find(pkg->get_name());
44         if(i!=packages.end())
45         {
46                 if(i->second!=pkg)
47                         throw invalid_argument("Package name conflict");
48                 else
49                         throw logic_error("Package is already managed");
50         }
51
52         if(packages.empty())
53                 main_pkg = pkg;
54
55         packages.insert({ pkg->get_name(), pkg });
56 }
57
58 Package *PackageManager::get_package(const string &name) const
59 {
60         auto i = packages.find(name);
61         if(i!=packages.end())
62                 return i->second;
63
64         return 0;
65 }
66
67 Package &PackageManager::get_main_package() const
68 {
69         if(!main_pkg)
70                 throw logic_error("No packages");
71         return *main_pkg;
72 }
73
74 Package *PackageManager::find_package(const string &name)
75 {
76         if(not_found.count(name))
77                 return 0;
78
79         if(Package *pkg = get_package(name))
80                 return pkg;
81
82         if(!no_externals)
83         {
84                 FS::Path path = get_package_location(name);
85                 if(!path.empty())
86                 {
87                         builder.load_build_file(path/"Build");
88                         auto i = packages.find(name);
89                         if(i!=packages.end())
90                                 return i->second;
91                 }
92         }
93
94         FS::Path path = get_binary_package_file(name);
95         if(!path.empty())
96         {
97                 builder.load_build_file(path);
98                 if(Package *pkg = get_package(name))
99                         return pkg;
100         }
101
102         try
103         {
104                 // Package source not found - create a binary package
105                 string flags_str = run_pkgconfig(name, "flags");
106                 BinaryPackage::Flags flags = split(flags_str);
107                 flags_str = run_pkgconfig(name, "staticflags");
108                 BinaryPackage::Flags static_flags = split(flags_str);
109                 Package *pkg = BinaryPackage::from_flags(builder, name, flags, static_flags);
110                 packages.insert({ name, pkg });
111                 return pkg;
112         }
113         catch(...)
114         {
115                 not_found.insert(name);
116                 return 0;
117         }
118 }
119
120 string PackageManager::run_pkgconfig(const string &pkg, const string &what)
121 {
122 #ifndef _WIN32
123         if(!env_set)
124         {
125                 const FS::Path &prefix = builder.get_prefix();
126                 if(prefix.str()!="/usr")
127                 {
128                         FS::Path pcdir = prefix/"lib/pkgconfig";
129                         if(const char *pcp = getenv("PKG_CONFIG_PATH"))
130                         {
131                                 vector<string> path = split(pcp, ':');
132                                 if(!any_equals(path, pcdir.str()))
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" || what=="staticflags")
148         {
149                 argv.push_back("--cflags");
150                 argv.push_back("--libs");
151                 if(what=="staticflags")
152                         argv.push_back("--static");
153         }
154         else
155                 argv.push_back("--variable="+what);
156         argv.push_back(pkg);
157
158         builder.get_logger().log("auxcommands", "Running %s", join(argv.begin(), argv.end()));
159
160         return ExternalTask::run_and_capture_output(argv);
161 #else
162         (void)pkg;
163         (void)what;
164         throw unsupported("run_pkgconfig");
165 #endif
166 }
167
168 FS::Path PackageManager::get_package_location(const string &name)
169 {
170         builder.get_logger().log("packagemgr", "Looking for source package %s", name);
171
172         try
173         {
174                 // Try to get source directory with pkgconfig
175                 string srcdir = strip(run_pkgconfig(name, "source"));
176                 if(!srcdir.empty() && FS::exists(FS::Path(srcdir)/"Build"))
177                         return srcdir;
178         }
179         catch(...)
180         { }
181
182         if(pkg_dirs.empty())
183         {
184                 for(const FS::Path &p: pkg_path)
185                 {
186                         if(!FS::exists(p))
187                                 continue;
188
189                         builder.get_logger().log("files", "Traversing %s", p);
190                         unsigned count = 0;
191                         for(const string &f: list_files(p))
192                         {
193                                 FS::Path full = p/f;
194                                 if(FS::exists(full/"Build"))
195                                 {
196                                         pkg_dirs.push_back(full);
197                                         ++count;
198                                 }
199                         }
200
201                         builder.get_logger().log("packagemgr", "%d source packages found in %s", count, p);
202                 }
203
204                 builder.get_logger().log("packagemgr", "%d source packages found", pkg_dirs.size());
205         }
206
207         bool msp = !name.compare(0, 3, "msp");
208         for(const FS::Path &p: pkg_dirs)
209         {
210                 string base = FS::basename(p);
211                 unsigned dash = base.rfind('-');
212
213                 if(!base.compare(0, dash, name))
214                         return p;
215                 else if(msp && !base.compare(0, dash, name, 3, string::npos))
216                         return p;
217         }
218
219         return FS::Path();
220 }
221
222 FS::Path PackageManager::get_binary_package_file(const string &name)
223 {
224         builder.get_logger().log("packagemgr", "Looking for binary package %s", name);
225
226         if(binpkg_files.empty())
227         {
228                 for(const FS::Path &p: binpkg_path)
229                 {
230                         if(!FS::exists(p))
231                                 continue;
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 }