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