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