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