]> git.tdb.fi Git - builder.git/blob - source/packagemanager.cpp
Feed paths to PackageManager from the outside
[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::set_no_externals(bool x)
37 {
38         no_externals = x;
39 }
40
41 void PackageManager::add_package(Package *pkg)
42 {
43         PackageMap::iterator 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         packages.insert(PackageMap::value_type(pkg->get_name(), pkg));
53 }
54
55 Package *PackageManager::find_package(const string &name)
56 {
57         PackageMap::iterator i = packages.find(format("%s/%s", name, builder.get_current_arch().get_system()));
58         if(i==packages.end())
59                 i = packages.find(name);
60         if(i!=packages.end())
61                 return i->second;
62
63         if(!no_externals)
64         {
65                 FS::Path path = get_package_location(name);
66                 if(!path.empty())
67                 {
68                         builder.load_build_file(path/"Build");
69                         i = packages.find(name);
70                         if(i!=packages.end())
71                                 return i->second;
72                 }
73         }
74
75         Package *pkg = 0;
76         try
77         {
78                 // Package source not found - create a binary package
79                 string flags_str = run_pkgconfig(name, "flags");
80                 vector<string> flags = split(flags_str);
81                 pkg = BinaryPackage::from_flags(builder, name, flags);
82         }
83         catch(...)
84         {
85                 builder.problem(name, "not found");
86         }
87
88         packages.insert(PackageMap::value_type(name, pkg));
89
90         return pkg;
91 }
92
93 string PackageManager::run_pkgconfig(const string &pkg, const string &what)
94 {
95         if(!env_set)
96         {
97                 const FS::Path &prefix = builder.get_prefix();
98                 if(prefix.str()!="/usr")
99                 {
100                         FS::Path pcdir = prefix/"lib/pkgconfig";
101                         if(const char *pcp = getenv("PKG_CONFIG_PATH"))
102                         {
103                                 vector<string> path = split(pcp, ':');
104                                 bool found = false;
105                                 for(vector<string>::const_iterator i=path.begin(); (!found && i!=path.end()); ++i)
106                                         found = (*i==pcdir.str());
107                                 if(!found)
108                                 {
109                                         path.push_back(pcdir.str());
110                                         setenv("PKG_CONFIG_PATH", join(path.begin(), path.end(), ":").c_str(), true);
111                                 }
112                         }
113                         else
114                                 setenv("PKG_CONFIG_PATH", pcdir.str().c_str(), true);
115                 }
116         }
117
118         ExternalTask::Arguments argv;
119         argv.push_back("pkg-config");
120         if(what=="cflags" || what=="libs")
121                 argv.push_back("--"+what);
122         else if(what=="flags")
123         {
124                 argv.push_back("--cflags");
125                 argv.push_back("--libs");
126         }
127         else
128                 argv.push_back("--variable="+what);
129         argv.push_back(pkg);
130
131         builder.get_logger().log("auxcommands", format("Running %s", join(argv.begin(), argv.end())));
132
133         return ExternalTask::run_and_capture_output(argv);
134 }
135
136 FS::Path PackageManager::get_package_location(const string &name)
137 {
138         builder.get_logger().log("packagemgr", format("Looking for package %s", name));
139
140         try
141         {
142                 // Try to get source directory with pkgconfig
143                 string srcdir = strip(run_pkgconfig(name, "source"));
144                 if(!srcdir.empty())
145                         return srcdir;
146         }
147         catch(...)
148         { }
149
150         if(pkg_dirs.empty())
151         {
152                 for(list<FS::Path>::const_iterator i=pkg_path.begin(); i!=pkg_path.end(); ++i)
153                 {
154                         builder.get_logger().log("files", format("Traversing %s", *i));
155                         list<string> files = list_files(*i);
156                         for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
157                         {
158                                 FS::Path full = *i / *j;
159                                 if(FS::exists(full/"Build"))
160                                         pkg_dirs.push_back(full);
161                         }
162                 }
163                 builder.get_logger().log("packagemgr", format("%d packages found in path", pkg_dirs.size()));
164         }
165
166         bool msp = !name.compare(0, 3, "msp");
167         for(list<FS::Path>::const_iterator i=pkg_dirs.begin(); i!=pkg_dirs.end(); ++i)
168         {
169                 string base = basename(*i);
170                 unsigned dash = base.rfind('-');
171
172                 if(!base.compare(0, dash, name))
173                         return *i;
174                 else if(msp && !base.compare(0, dash-3, name, 3, string::npos))
175                         return *i;
176         }
177
178         return FS::Path();
179 }