*i = (path/i->substr(1)).str();
}
-BinaryPackage *BinaryPackage::from_pkgconfig(Builder &builder, const string &name)
+BinaryPackage *BinaryPackage::from_flags(Builder &builder, const std::string &name, const vector<string> &flags)
{
- string info = builder.run_pkgconfig(name, "flags");
-
BinaryPackage *pkg = new BinaryPackage(builder, name);
pkg->use_pkgconfig = true;
BuildInfo &binfo = pkg->export_binfo;
- vector<string> flags = split(info);
for(vector<string>::const_iterator i=flags.begin(); i!=flags.end(); ++i)
{
if(!i->compare(0, 2, "-I"))
bool get_need_path() const { return need_path; }
- static BinaryPackage *from_pkgconfig(Builder &, const std::string &);
+ static BinaryPackage *from_flags(Builder &, const std::string &, const std::vector<std::string> &);
private:
virtual void create_build_info();
};
#include "binarypackage.h"
#include "builder.h"
#include "copy.h"
-#include "externaltask.h"
#include "gnuarchiver.h"
#include "gnuccompiler.h"
#include "gnucxxcompiler.h"
using namespace Msp;
Builder::Builder(int argc, char **argv):
+ package_manager(*this),
main_pkg(0),
native_arch(*this, string()),
vfs(*this),
StringList cmdline_warn;
string prfx;
string arch;
+ bool no_externals = false;
GetOpt getopt;
getopt.add_option('a', "analyze", analyze_mode, GetOpt::REQUIRED_ARG).set_help("Perform analysis. MODE can be deps, alldeps or rebuild.", "MODE");
cwd = FS::getcwd();
+ package_manager.set_no_externals(no_externals);
+
load_build_file((FS::get_sys_data_dir(argv[0], "builder")/"builderrc").str());
load_build_file((FS::get_user_data_dir("builder")/"rc").str());
vector<string> warns = split(*i, ',');
warnings.insert(warnings.end(), warns.begin(), warns.end());
}
-
- pkg_path.push_back(cwd/".");
- pkg_path.push_back(cwd/"..");
}
Builder::~Builder()
{
- for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
- delete i->second;
for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
delete i->second;
delete analyzer;
if(verbose>=1)
{
+ const PackageManager::PackageMap &packages = package_manager.get_packages();
unsigned n_packages = 0;
- for(PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
+ for(PackageManager::PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
if(i->second && i->second->is_configured())
++n_packages;
IO::print("%d active packages, %d targets\n", n_packages, targets.size());
if(verbose>=2)
{
- for(PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
+ const PackageManager::PackageMap &packages = package_manager.get_packages();
+ for(PackageManager::PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
{
if(!i->second->is_configured())
continue;
return exit_code;
}
-string Builder::run_pkgconfig(const string &pkg, const string &what)
-{
- ExternalTask::Arguments argv;
- argv.push_back("pkg-config");
- if(what=="cflags" || what=="libs")
- argv.push_back("--"+what);
- else if(what=="flags")
- {
- argv.push_back("--cflags");
- argv.push_back("--libs");
- }
- else
- argv.push_back("--variable="+what);
- argv.push_back(pkg);
-
- if(verbose>=4)
- IO::print("Running %s\n", join(argv.begin(), argv.end()));
-
- ExternalTask task(argv);
- task.set_stdout(ExternalTask::CAPTURE);
- task.set_stderr(ExternalTask::IGNORE);
- task.start();
- Task::Status status;
- while((status=task.check())==Task::RUNNING)
- Time::sleep(10*Time::msec);
- if(status==Task::ERROR)
- throw runtime_error(format("pkg-config for package %s failed", pkg));
-
- return task.get_output();
-}
-
-Package *Builder::get_package(const string &name)
-{
- PackageMap::iterator i = packages.find(format("%s/%s", name, current_arch->get_system()));
- if(i==packages.end())
- i = packages.find(name);
- if(i!=packages.end())
- return i->second;
-
- if(!no_externals)
- {
- FS::Path path = get_package_location(name);
- if(!path.empty() && !load_build_file(path/"Build"))
- {
- i = packages.find(name);
- if(i!=packages.end())
- return i->second;
- }
- }
-
- Package *pkg = 0;
- try
- {
- // Package source not found - create a binary package
- pkg = BinaryPackage::from_pkgconfig(*this, name);
- }
- catch(...)
- {
- problem(name, "not found");
- }
-
- packages.insert(PackageMap::value_type(name, pkg));
-
- return pkg;
-}
-
Target *Builder::get_target(const string &n) const
{
TargetMap::const_iterator i = targets.find(n);
}
}
-FS::Path Builder::get_package_location(const string &name)
-{
- if(verbose>=3)
- IO::print("Looking for package %s\n", name);
-
- try
- {
- // Try to get source directory with pkgconfig
- string srcdir = strip(run_pkgconfig(name, "source"));
- if(!srcdir.empty())
- return srcdir;
- }
- catch(...)
- { }
-
- if(pkg_dirs.empty())
- {
- for(list<FS::Path>::const_iterator i=pkg_path.begin(); i!=pkg_path.end(); ++i)
- {
- list<string> files = list_files(*i);
- for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
- {
- FS::Path full = *i / *j;
- if(FS::exists(full/"Build"))
- pkg_dirs.push_back(full);
- }
- }
- if(verbose>=3)
- IO::print("%d packages found in path\n", pkg_dirs.size());
- }
-
- bool msp = !name.compare(0, 3, "msp");
- for(list<FS::Path>::const_iterator i=pkg_dirs.begin(); i!=pkg_dirs.end(); ++i)
- {
- string base = basename(*i);
- unsigned dash = base.rfind('-');
-
- if(!base.compare(0, dash, name))
- return *i;
- else if(msp && !base.compare(0, dash-3, name, 3, string::npos))
- return *i;
- }
-
- return FS::Path();
-}
-
int Builder::load_build_file(const FS::Path &fn)
{
if(!FS::exists(fn))
Target *tarballs = new VirtualTarget(*this, "tarballs");
world->add_depend(tarballs);
- for(PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
+ const PackageManager::PackageMap &packages = package_manager.get_packages();
+ for(PackageManager::PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
if(i->second && i->second->is_configured())
i->second->create_targets();
i->second->force_rebuild();
}
- for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+ for(PackageManager::PackageMap::const_iterator i=packages.begin(); i!=packages.end(); ++i)
if(SourcePackage *spkg = dynamic_cast<SourcePackage *>(i->second))
spkg->get_deps_cache().save();
{
BinaryPackage *pkg = new BinaryPackage(bld, n);
load_sub(*pkg);
- bld.packages.insert(PackageMap::value_type(n, pkg));
}
void Builder::Loader::cross_prefix(const string &a, const string &p)
bld.main_pkg = pkg;
load_sub(*pkg);
- bld.packages.insert(PackageMap::value_type(n, pkg));
}
#include "architecture.h"
#include "config.h"
#include "misc.h"
+#include "packagemanager.h"
#include "problem.h"
#include "target.h"
#include "toolchain.h"
typedef std::list<Target *> TargetList;
private:
- typedef std::list<Package *> PackageList;
- typedef std::map<std::string, Package *> PackageMap;
typedef std::map<std::string, StringMap> ProfileTemplateMap;
StringList cmdline_targets;
StringMap cmdline_options;
Msp::FS::Path cwd;
- PackageMap packages;
+ PackageManager package_manager;
SourcePackage *main_pkg;
- PathList pkg_path;
- PathList pkg_dirs;
- bool no_externals;
TargetMap targets;
TargetList new_tgts;
int main();
unsigned get_verbose() const { return verbose; }
bool get_dry_run() const { return dry_run; }
- bool get_build_all() const { return build_all; }
-
- /** Gets a package by name, possibly creating it. Returns 0 if the package
- could not be located. */
- Package *get_package(const std::string &);
+ PackageManager &get_package_manager() { return package_manager; }
SourcePackage *get_main_package() const { return main_pkg; }
- std::string run_pkgconfig(const std::string &, const std::string &);
-
/** Looks up a target by name. Returns 0 if no such target exists. */
Target *get_target(const std::string &) const;
static void usage(const char *, const char *, bool);
-private:
- /** Determines the source directory of a package. Pkg-config is consulted
- first, and if it fails, the package path is searched for matches. */
- Msp::FS::Path get_package_location(const std::string &);
-
/** Loads a build file. Returns 0 on success or -1 if the file could not be
opened. */
int load_build_file(const Msp::FS::Path &);
+private:
/** Creates targets for all packages and prepares them for building.
Returns 0 if everything went ok, -1 if something bad happened and a build
shouldn't be attempted. */
void Component::Loader::require(const string &n)
{
- Package *req = comp.pkg.get_builder().get_package(n);
+ Package *req = comp.pkg.get_builder().get_package_manager().find_package(n);
if(req)
comp.requires.push_back(req);
}
name(n),
conf_done(false),
use_pkgconfig(true)
-{ }
+{
+ builder.get_package_manager().add_package(this);
+}
void Package::configure(const StringMap &opts, unsigned flag)
{
void Package::Loader::require(const string &n)
{
- Package *req = pkg.builder.get_package(n);
+ Package *req = pkg.builder.get_package_manager().find_package(n);
if(req)
pkg.requires.push_back(req);
}
--- /dev/null
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#include "binarypackage.h"
+#include "builder.h"
+#include "externaltask.h"
+#include "package.h"
+#include "packagemanager.h"
+
+using namespace std;
+using namespace Msp;
+
+PackageManager::PackageManager(Builder &b):
+ builder(b),
+ no_externals(false)
+{
+ pkg_path.push_back(builder.get_cwd()/".");
+ pkg_path.push_back(builder.get_cwd()/"..");
+}
+
+PackageManager::~PackageManager()
+{
+ for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+ delete i->second;
+}
+
+void PackageManager::set_no_externals(bool x)
+{
+ no_externals = x;
+}
+
+void PackageManager::add_package(Package *pkg)
+{
+ PackageMap::iterator i = packages.find(pkg->get_name());
+ if(i!=packages.end())
+ {
+ if(i->second!=pkg)
+ throw invalid_argument("Package name conflict");
+ else
+ throw logic_error("Package is already managed");
+ }
+
+ packages.insert(PackageMap::value_type(pkg->get_name(), pkg));
+}
+
+Package *PackageManager::find_package(const string &name)
+{
+ PackageMap::iterator i = packages.find(format("%s/%s", name, builder.get_current_arch().get_system()));
+ if(i==packages.end())
+ i = packages.find(name);
+ if(i!=packages.end())
+ return i->second;
+
+ if(!no_externals)
+ {
+ FS::Path path = get_package_location(name);
+ if(!path.empty() && !builder.load_build_file(path/"Build"))
+ {
+ i = packages.find(name);
+ if(i!=packages.end())
+ return i->second;
+ }
+ }
+
+ Package *pkg = 0;
+ try
+ {
+ // Package source not found - create a binary package
+ string flags_str = run_pkgconfig(name, "flags");
+ vector<string> flags = split(flags_str);
+ pkg = BinaryPackage::from_flags(builder, name, flags);
+ }
+ catch(...)
+ {
+ builder.problem(name, "not found");
+ }
+
+ packages.insert(PackageMap::value_type(name, pkg));
+
+ return pkg;
+}
+
+string PackageManager::run_pkgconfig(const string &pkg, const string &what)
+{
+ ExternalTask::Arguments argv;
+ argv.push_back("pkg-config");
+ if(what=="cflags" || what=="libs")
+ argv.push_back("--"+what);
+ else if(what=="flags")
+ {
+ argv.push_back("--cflags");
+ argv.push_back("--libs");
+ }
+ else
+ argv.push_back("--variable="+what);
+ argv.push_back(pkg);
+
+ if(builder.get_verbose()>=4)
+ IO::print("Running %s\n", join(argv.begin(), argv.end()));
+
+ ExternalTask task(argv);
+ task.set_stdout(ExternalTask::CAPTURE);
+ task.set_stderr(ExternalTask::IGNORE);
+ task.start();
+ Task::Status status;
+ while((status=task.check())==Task::RUNNING)
+ Time::sleep(10*Time::msec);
+ if(status==Task::ERROR)
+ throw runtime_error(format("pkg-config for package %s failed", pkg));
+
+ return task.get_output();
+}
+
+FS::Path PackageManager::get_package_location(const string &name)
+{
+ if(builder.get_verbose()>=3)
+ IO::print("Looking for package %s\n", name);
+
+ try
+ {
+ // Try to get source directory with pkgconfig
+ string srcdir = strip(run_pkgconfig(name, "source"));
+ if(!srcdir.empty())
+ return srcdir;
+ }
+ catch(...)
+ { }
+
+ if(pkg_dirs.empty())
+ {
+ for(list<FS::Path>::const_iterator i=pkg_path.begin(); i!=pkg_path.end(); ++i)
+ {
+ list<string> files = list_files(*i);
+ for(list<string>::const_iterator j=files.begin(); j!=files.end(); ++j)
+ {
+ FS::Path full = *i / *j;
+ if(FS::exists(full/"Build"))
+ pkg_dirs.push_back(full);
+ }
+ }
+ if(builder.get_verbose()>=3)
+ IO::print("%d packages found in path\n", pkg_dirs.size());
+ }
+
+ bool msp = !name.compare(0, 3, "msp");
+ for(list<FS::Path>::const_iterator i=pkg_dirs.begin(); i!=pkg_dirs.end(); ++i)
+ {
+ string base = basename(*i);
+ unsigned dash = base.rfind('-');
+
+ if(!base.compare(0, dash, name))
+ return *i;
+ else if(msp && !base.compare(0, dash-3, name, 3, string::npos))
+ return *i;
+ }
+
+ return FS::Path();
+}
--- /dev/null
+#ifndef PACKAGEMANAGER_H_
+#define PACKAGEMANAGER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <msp/fs/path.h>
+
+class Builder;
+class Package;
+
+/**
+Keeps track of packages. Also responsible for locating previously unknown
+packages by name.
+*/
+class PackageManager
+{
+public:
+ typedef std::map<std::string, Package *> PackageMap;
+
+private:
+ typedef std::list<Msp::FS::Path> SearchPath;
+
+ Builder &builder;
+ SearchPath pkg_path;
+ SearchPath pkg_dirs;
+ bool no_externals;
+ PackageMap packages;
+
+public:
+ PackageManager(Builder &);
+ ~PackageManager();
+
+ void set_no_externals(bool);
+
+ /** Adds a package to the manager. Called from Package constructor. */
+ void add_package(Package *);
+
+ /** Returns a package from the cache. */
+ Package *get_package(const std::string &);
+
+ const PackageMap &get_packages() const { return packages; }
+
+ /** Locates a package and creates it if necessary. */
+ Package *find_package(const std::string &);
+
+private:
+ std::string run_pkgconfig(const std::string &, const std::string &);
+
+ /** Determines the source directory of a package. Pkg-config is consulted
+ first, and if it fails, the package path is searched for matches. */
+ Msp::FS::Path get_package_location(const std::string &);
+};
+
+#endif
{
const StringList &reqs = i->get_requires();
for(StringList::const_iterator j=reqs.begin(); j!=reqs.end(); ++j)
- if(Package *pkg = builder.get_package(*j))
+ if(Package *pkg = builder.get_package_manager().find_package(*j))
requires.push_back(pkg);
}