]> git.tdb.fi Git - builder.git/blob - source/packagemanager.cpp
Move package management to a separate class
[builder.git] / source / packagemanager.cpp
1 #include <msp/fs/dir.h>
2 #include <msp/fs/stat.h>
3 #include <msp/fs/utils.h>
4 #include <msp/io/print.h>
5 #include <msp/strings/format.h>
6 #include <msp/strings/utils.h>
7 #include <msp/time/units.h>
8 #include <msp/time/utils.h>
9 #include "binarypackage.h"
10 #include "builder.h"
11 #include "externaltask.h"
12 #include "package.h"
13 #include "packagemanager.h"
14
15 using namespace std;
16 using namespace Msp;
17
18 PackageManager::PackageManager(Builder &b):
19         builder(b),
20         no_externals(false)
21 {
22         pkg_path.push_back(builder.get_cwd()/".");
23         pkg_path.push_back(builder.get_cwd()/"..");
24 }
25
26 PackageManager::~PackageManager()
27 {
28         for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
29                 delete i->second;
30 }
31
32 void PackageManager::set_no_externals(bool x)
33 {
34         no_externals = x;
35 }
36
37 void PackageManager::add_package(Package *pkg)
38 {
39         PackageMap::iterator i = packages.find(pkg->get_name());
40         if(i!=packages.end())
41         {
42                 if(i->second!=pkg)
43                         throw invalid_argument("Package name conflict");
44                 else
45                         throw logic_error("Package is already managed");
46         }
47
48         packages.insert(PackageMap::value_type(pkg->get_name(), pkg));
49 }
50
51 Package *PackageManager::find_package(const string &name)
52 {
53         PackageMap::iterator i = packages.find(format("%s/%s", name, builder.get_current_arch().get_system()));
54         if(i==packages.end())
55                 i = packages.find(name);
56         if(i!=packages.end())
57                 return i->second;
58
59         if(!no_externals)
60         {
61                 FS::Path path = get_package_location(name);
62                 if(!path.empty() && !builder.load_build_file(path/"Build"))
63                 {
64                         i = packages.find(name);
65                         if(i!=packages.end())
66                                 return i->second;
67                 }
68         }
69
70         Package *pkg = 0;
71         try
72         {
73                 // Package source not found - create a binary package
74                 string flags_str = run_pkgconfig(name, "flags");
75                 vector<string> flags = split(flags_str);
76                 pkg = BinaryPackage::from_flags(builder, name, flags);
77         }
78         catch(...)
79         {
80                 builder.problem(name, "not found");
81         }
82
83         packages.insert(PackageMap::value_type(name, pkg));
84
85         return pkg;
86 }
87
88 string PackageManager::run_pkgconfig(const string &pkg, const string &what)
89 {
90         ExternalTask::Arguments argv;
91         argv.push_back("pkg-config");
92         if(what=="cflags" || what=="libs")
93                 argv.push_back("--"+what);
94         else if(what=="flags")
95         {
96                 argv.push_back("--cflags");
97                 argv.push_back("--libs");
98         }
99         else
100                 argv.push_back("--variable="+what);
101         argv.push_back(pkg);
102
103         if(builder.get_verbose()>=4)
104                 IO::print("Running %s\n", join(argv.begin(), argv.end()));
105
106         ExternalTask task(argv);
107         task.set_stdout(ExternalTask::CAPTURE);
108         task.set_stderr(ExternalTask::IGNORE);
109         task.start();
110         Task::Status status;
111         while((status=task.check())==Task::RUNNING)
112                 Time::sleep(10*Time::msec);
113         if(status==Task::ERROR)
114                 throw runtime_error(format("pkg-config for package %s failed", pkg));
115
116         return task.get_output();
117 }
118
119 FS::Path PackageManager::get_package_location(const string &name)
120 {
121         if(builder.get_verbose()>=3)
122                 IO::print("Looking for package %s\n", name);
123
124         try
125         {
126                 // Try to get source directory with pkgconfig
127                 string srcdir = strip(run_pkgconfig(name, "source"));
128                 if(!srcdir.empty())
129                         return srcdir;
130         }
131         catch(...)
132         { }
133
134         if(pkg_dirs.empty())
135         {
136                 for(list<FS::Path>::const_iterator i=pkg_path.begin(); i!=pkg_path.end(); ++i)
137                 {
138                         list<string> files = list_files(*i);
139                         for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
140                         {
141                                 FS::Path full = *i / *j;
142                                 if(FS::exists(full/"Build"))
143                                         pkg_dirs.push_back(full);
144                         }
145                 }
146                 if(builder.get_verbose()>=3)
147                         IO::print("%d packages found in path\n", pkg_dirs.size());
148         }
149
150         bool msp = !name.compare(0, 3, "msp");
151         for(list<FS::Path>::const_iterator i=pkg_dirs.begin(); i!=pkg_dirs.end(); ++i)
152         {
153                 string base = basename(*i);
154                 unsigned dash = base.rfind('-');
155
156                 if(!base.compare(0, dash, name))
157                         return *i;
158                 else if(msp && !base.compare(0, dash-3, name, 3, string::npos))
159                         return *i;
160         }
161
162         return FS::Path();
163 }