Doesn't build anything yet.
--- /dev/null
+package "builder"
+{
+ version "0.1";
+ description "Mikkosoft Productions software builder";
+
+ require "mspframework";
+ require "mspparser";
+ require "msppath";
+ require "mspregex++";
+
+ program "builder"
+ {
+ source "source";
+ install true;
+ };
+};
--- /dev/null
+#include <fstream>
+#include <msp/strutils.h>
+#include <msp/parser/parser.h>
+#include <msp/path/utils.h>
+#include "builder.h"
+#include "executable.h"
+#include "header.h"
+#include "misc.h"
+#include "objectfile.h"
+#include "package.h"
+#include "virtualtarget.h"
+
+using namespace std;
+using namespace Msp;
+
+Builder::Builder(int argc, char **argv):
+ verbose(1),
+ cwd(Path::getcwd())
+{
+ for(int i=1; i<argc; ++i)
+ cmdline_targets.push_back(argv[i]);
+ if(cmdline_targets.empty())
+ cmdline_targets.push_back("default");
+}
+
+Package *Builder::get_package(const string &n)
+{
+ PackageMap::iterator i=packages.find(n);
+ if(i!=packages.end())
+ return i->second;
+
+ list<string> argv;
+ argv.push_back("pkg-config");
+ argv.push_back("--variable=source");
+ argv.push_back(n);
+ Path::Path srcdir=run_command(argv);
+ if(srcdir.empty())
+ {
+ string dirname=n;
+ if(dirname.compare(0, 3, "msp"))
+ dirname.erase(0, 3);
+ if(Path::exists(cwd/dirname))
+ srcdir=cwd/dirname;
+ else if(Path::exists(cwd/".."/dirname))
+ srcdir=cwd/".."/dirname;
+ }
+ else
+ srcdir=strip(srcdir.str());
+
+ if(!srcdir.empty())
+ load_build_file(srcdir/"Build");
+
+ return 0;
+}
+
+Target *Builder::get_target(const string &n)
+{
+ TargetMap::iterator i=targets.find(n);
+ if(i!=targets.end())
+ return i->second;
+ return 0;
+}
+
+Target *Builder::get_header(const string &include, const string &from, const list<string> &path)
+{
+ string id=from+":"+include;
+ TargetMap::iterator i=includes.find(id);
+ if(i!=includes.end())
+ return i->second;
+
+ string fn=include.substr(1);
+ Target *tgt;
+ if(include[0]=='"' && (tgt=check_header(Path::Path(from)/fn)))
+ return tgt;
+ if((tgt=check_header(Path::Path("/usr/include")/fn)))
+ return tgt;
+ if((tgt=check_header(Path::Path("/usr/include/c++/4.1.2")/fn)))
+ return tgt;
+ for(list<string>::const_iterator j=path.begin(); j!=path.end(); ++j)
+ if((tgt=check_header(Path::Path(*j)/fn)))
+ return tgt;
+
+ return 0;
+}
+
+int Builder::main()
+{
+ if(load_build_file("Build"))
+ {
+ cerr<<"No build info here.\n";
+ return 1;
+ }
+
+ default_pkg=packages.begin()->second;
+
+ while(!new_pkgs.empty())
+ {
+ Package *pkg=new_pkgs.front();
+ new_pkgs.erase(new_pkgs.begin());
+ pkg->resolve_refs();
+ }
+
+ cout<<"Active packages:";
+ for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+ {
+ cout<<' '<<i->second->get_name();
+ if(i->second->get_buildable())
+ cout<<'*';
+ }
+ cout<<'\n';
+
+ create_targets();
+
+ for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
+ cout<<i->second->get_name()<<' '<<i->second->get_type()<<'\n';
+
+ cout<<"Active targets: "<<targets.size()<<'\n';
+
+ return 0;
+}
+
+int Builder::load_build_file(const Path::Path &fn)
+{
+ ifstream in(fn.str().c_str());
+ if(!in)
+ return -1;
+
+ Parser::Parser parser(in, fn.str());
+ Loader loader(*this, cwd/fn.subpath(0, fn.size()-1));
+ loader.load(parser);
+
+ return 0;
+}
+
+void Builder::create_targets()
+{
+ Target *world=new VirtualTarget(*this, "world");
+ add_target(world);
+ Target *def_tgt=new VirtualTarget(*this, "default");
+ add_target(def_tgt);
+
+ for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+ {
+ cout<<i->second->get_source()<<'\n';
+ const ComponentList &components=i->second->get_components();
+ for(ComponentList::const_iterator j=components.begin(); j!=components.end(); ++j)
+ {
+ Path::Path base=i->second->get_source()/j->get_source();
+ cout<<base<<'\n';
+ list<string> files=list_files(base);
+
+ list<ObjectFile *> objs;
+ for(list<string>::iterator k=files.begin(); k!=files.end(); ++k)
+ {
+ Path::Path fn=base/ *k;
+ //cout<<*k<<'\n';
+ string ext=Path::splitext(*k).ext;
+ if(ext==".cpp" || ext==".c")
+ {
+ SourceFile *src=new SourceFile(*this, &*j, fn.str());
+ add_target(src);
+
+ ObjectFile *obj=new ObjectFile(*this, *j, *src);
+ add_target(obj);
+ objs.push_back(obj);
+ }
+ else if(ext==".h")
+ add_target(new Header(*this, &*j, fn.str()));
+ }
+
+ Executable *exe=new Executable(*this, *j, objs);
+ add_target(exe);
+ if(i->second==default_pkg)
+ def_tgt->add_depend(exe);
+ }
+ }
+
+ while(!new_tgts.empty())
+ {
+ Target *tgt=new_tgts.front();
+ new_tgts.erase(new_tgts.begin());
+ tgt->find_depends();
+ }
+}
+
+Target *Builder::check_header(const Msp::Path::Path &fn)
+{
+ Target *tgt=get_target(fn.str());
+ if(tgt) return tgt;
+
+ if(Path::exists(fn))
+ {
+ add_target(tgt=new SystemHeader(*this, fn.str()));
+ return tgt;
+ }
+ return 0;
+}
+
+void Builder::add_target(Target *t)
+{
+ targets.insert(TargetMap::value_type(t->get_name(), t));
+ new_tgts.push_back(t);
+}
+
+Application::RegApp<Builder> Builder::reg;
+
+Builder::Loader::Loader(Builder &b, const Path::Path &s):
+ bld(b),
+ src(s)
+{
+ add("package", &Loader::package);
+}
+
+void Builder::Loader::package(const string &n)
+{
+ Package *pkg=new Package(bld, n, src);
+ load_sub(*pkg);
+ bld.packages.insert(PackageMap::value_type(n, pkg));
+ bld.new_pkgs.push_back(pkg);
+ //cout<<"loaded "<<pkg->get_name()<<'\n';
+}
+
--- /dev/null
+#ifndef BUILDER_H_
+#define BUILDER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <msp/framework/application.h>
+#include <msp/parser/loader.h>
+#include <msp/path/path.h>
+
+class Package;
+class Target;
+
+class Builder: public Msp::Application
+{
+public:
+ Builder(int, char **);
+ unsigned get_verbose() const { return verbose; }
+ Package *get_package(const std::string &);
+ Target *get_target(const std::string &);
+ Target *get_header(const std::string &, const std::string &, const std::list<std::string> &);
+ int main();
+private:
+ class Loader: public Msp::Parser::Loader
+ {
+ public:
+ Loader(Builder &, const Msp::Path::Path &);
+ private:
+ Builder &bld;
+ Msp::Path::Path src;
+
+ void package(const std::string &);
+ };
+
+ typedef std::map<std::string, Package *> PackageMap;
+ typedef std::map<std::string, Target *> TargetMap;
+
+ std::list<std::string> cmdline_targets;
+ TargetMap targets;
+ PackageMap packages;
+ unsigned verbose;
+ Package *default_pkg;
+ Msp::Path::Path cwd;
+ std::list<Package *> new_pkgs;
+ TargetMap includes;
+ std::list<Target *> new_tgts;
+
+ int load_build_file(const Msp::Path::Path &);
+ void create_targets();
+ Target *check_header(const Msp::Path::Path &);
+ void add_target(Target *);
+
+ static Msp::Application::RegApp<Builder> reg;
+};
+
+#endif
--- /dev/null
+#ifndef BUILDINFO_H_
+#define BUILDINFO_H_
+
+#include <list>
+#include <string>
+
+class BuildInfo
+{
+public:
+ typedef std::list<std::string> InfoList;
+
+ InfoList cflags;
+ InfoList defines;
+ InfoList incpath;
+ InfoList ldflags;
+ InfoList libpath;
+ InfoList libs;
+
+ void add(const BuildInfo &);
+ void unique();
+};
+
+#endif
--- /dev/null
+#include "component.h"
+#include "package.h"
+
+using namespace std;
+
+Component::Component(Package &p, Type t, const string &n):
+ pkg(p),
+ type(t),
+ name(n)
+{ }
+
+Component::Loader::Loader(Component &c):
+ comp(c)
+{
+ add("source", &Loader::source);
+ add("install", &Component::install);
+ add("install_headers", &Component::install_headers);
+}
+
+void Component::Loader::source(const string &s)
+{
+ comp.source=comp.pkg.get_source()/s;
+}
--- /dev/null
+#ifndef COMPONENT_H_
+#define COMPONENT_H_
+
+#include <string>
+#include <msp/parser/loader.h>
+#include <msp/path/path.h>
+#include "buildinfo.h"
+
+class Package;
+
+class Component
+{
+public:
+ class Loader: public Msp::Parser::Loader
+ {
+ public:
+ Loader(Component &);
+ Component &get_object() { return comp; }
+ private:
+ Component ∁
+
+ void source(const std::string &);
+ };
+
+ enum Type
+ {
+ PROGRAM,
+ LIBRARY,
+ MODULE
+ };
+
+ Component(Package &, Type, const std::string &);
+ const Package &get_package() const { return pkg; }
+ Type get_type() const { return type; }
+ const std::string &get_name() const { return name; }
+ const Msp::Path::Path &get_source() const { return source; }
+ const BuildInfo &get_build_info() const { return build_info; }
+protected:
+ Package &pkg;
+ Type type;
+ std::string name;
+ Msp::Path::Path source;
+ bool install;
+ std::string install_headers;
+ BuildInfo build_info;
+};
+typedef std::list<Component> ComponentList;
+
+#endif
--- /dev/null
+#include "component.h"
+#include "executable.h"
+#include "objectfile.h"
+#include "package.h"
+
+using namespace std;
+
+Executable::Executable(Builder &b, const Component &c, const list<ObjectFile *> &objs):
+ Target(b, &c.get_package(), generate_target_name(c)),
+ comp(c)
+{
+ for(list<ObjectFile *>::const_iterator i=objs.begin(); i!=objs.end(); ++i)
+ add_depend(*i);
+}
+
+string Executable::generate_target_name(const Component &comp)
+{
+ string prefix;
+ string suffix;
+
+ if(comp.get_type()==Component::LIBRARY)
+ {
+ prefix="lib";
+ suffix=".so";
+ }
+
+ return (comp.get_package().get_source()/(prefix+comp.get_name()+suffix)).str();
+}
--- /dev/null
+#ifndef EXECUTABLE_H_
+#define EXECUTABLE_H_
+
+#include "target.h"
+
+class Component;
+class ObjectFile;
+
+class Executable: public Target
+{
+public:
+ Executable(Builder &, const Component &, const std::list<ObjectFile *> &);
+ const char *get_type() const { return "Executable"; }
+ void find_depends() { }
+private:
+ const Component ∁
+
+ static std::string generate_target_name(const Component &);
+};
+
+#endif
--- /dev/null
+#ifndef HEADER_H_
+#define HEADER_H_
+
+#include "sourcefile.h"
+
+class Header: public SourceFile
+{
+public:
+ Header(Builder &b, const Component *c, const std::string &f): SourceFile(b,c,f) { }
+ const char *get_type() const { return "Header"; }
+};
+
+class SystemHeader: public Header
+{
+public:
+ SystemHeader(Builder &b, const std::string &f): Header(b,0,f) { }
+ const char *get_type() const { return "SystemHeader"; }
+ void find_depends() { }
+};
+
+#endif
--- /dev/null
+#include <iostream>
+#include <sys/wait.h>
+#include <msp/iter.h>
+#include "misc.h"
+
+using namespace std;
+using namespace Msp;
+
+string run_command(const list<string> &argv)
+{
+ int pfd[2];
+ pipe(pfd);
+
+ string result;
+
+ pid_t pid=fork();
+ if(pid==0)
+ {
+ char *argv_[argv.size()+1];
+ for(CountingIterator<const string, list<string>::const_iterator> i=argv.begin(); i!=argv.end(); ++i)
+ argv_[i.count()]=strdup(i->c_str());
+ argv_[argv.size()]=0;
+ close(pfd[0]);
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ execvp(argv_[0], argv_);
+ ::exit(1);
+ }
+ else if(pid==-1)
+ cerr<<"Failed to execute "<<argv.front()<<'\n';
+ else
+ {
+ close(pfd[1]);
+ while(1)
+ {
+ char buf[1024];
+ int len=read(pfd[0], buf, sizeof(buf));
+ if(len<=0)
+ {
+ if(waitpid(pid, 0, WNOHANG))
+ break;
+ }
+ else
+ result.append(buf, len);
+ }
+ }
+
+ return result;
+}
+
+
--- /dev/null
+#ifndef MISC_H_
+#define MISC_H_
+
+#include <list>
+#include <string>
+
+std::string run_command(const std::list<std::string> &);
+
+#endif
--- /dev/null
+#include <msp/path/utils.h>
+#include "builder.h"
+#include "component.h"
+#include "objectfile.h"
+#include "package.h"
+#include "sourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+ObjectFile::ObjectFile(Builder &b, const Component &c, SourceFile &src):
+ Target(b, &c.get_package(), generate_target_name(c, src.get_name())),
+ comp(c)
+{
+ add_depend(&src);
+}
+
+string ObjectFile::generate_target_name(const Component &comp, const string &src)
+{
+ return (comp.get_package().get_source()/"temp"/comp.get_name()/(Path::splitext(src.substr(src.rfind('/')+1)).base+".o")).str();
+}
--- /dev/null
+#ifndef OBJECTFILE_H_
+#define OBJECTFILE_H_
+
+#include "target.h"
+
+class Component;
+class SourceFile;
+
+class ObjectFile: public Target
+{
+public:
+ ObjectFile(Builder &, const Component &, SourceFile &);
+ const char *get_type() const { return "ObjectFile"; }
+ void find_depends() { }
+private:
+ const Component ∁
+
+ static std::string generate_target_name(const Component &, const std::string &);
+};
+
+#endif
--- /dev/null
+#include "builder.h"
+#include "package.h"
+
+using namespace std;
+using namespace Msp;
+
+PackageRef::PackageRef(Builder &b, const string &n):
+ builder(b),
+ name(n),
+ package(0)
+{ }
+
+Package *PackageRef::get_package()
+{
+ if(!package)
+ package=builder.get_package(name);
+ return package;
+}
+
+Package::Package(Builder &b, const string &n, const Path::Path &s):
+ builder(b),
+ name(n),
+ source(s),
+ buildable(false)
+{
+}
+
+void Package::resolve_refs()
+{
+ for(list<PackageRef>::iterator i=requires.begin(); i!=requires.end(); ++i)
+ i->get_package();
+}
+
+Package::Loader::Loader(Package &p):
+ pkg(p)
+{
+ add("version", &Package::version);
+ add("description", &Package::description);
+ add("require", &Loader::require);
+ add("program", &Loader::program);
+ add("library", &Loader::library);
+}
+
+Package::Loader::~Loader()
+{
+ pkg.buildable=true;
+}
+
+void Package::Loader::require(const string &n)
+{
+ pkg.requires.push_back(PackageRef(pkg.builder, n));
+}
+
+void Package::Loader::program(const std::string &n)
+{
+ Component prog(pkg, Component::PROGRAM, n);
+ load_sub(prog);
+ pkg.components.push_back(prog);
+}
+
+void Package::Loader::library(const std::string &n)
+{
+ Component prog(pkg, Component::LIBRARY, n);
+ load_sub(prog);
+ pkg.components.push_back(prog);
+}
--- /dev/null
+#ifndef PACKAGE_H_
+#define PACKAGE_H_
+
+#include <list>
+#include <string>
+#include <msp/parser/loader.h>
+#include "buildinfo.h"
+#include "component.h"
+
+class Builder;
+class Package;
+
+class PackageRef
+{
+public:
+ PackageRef(Builder &, const std::string &);
+ Package *get_package();
+private:
+ Builder &builder;
+ std::string name;
+ Package *package;
+};
+
+class Package
+{
+public:
+ class Loader: public Msp::Parser::Loader
+ {
+ public:
+ Loader(Package &);
+ Package &get_object() { return pkg; }
+ ~Loader();
+ private:
+ Package &pkg;
+
+ void require(const std::string &);
+ void program(const std::string &);
+ void library(const std::string &);
+ };
+
+ Package(Builder &, const std::string &, const Msp::Path::Path &);
+ Package(Builder &, const std::string &, const std::list<std::string> &);
+ const std::string &get_name() const { return name; }
+ const Msp::Path::Path &get_source() const { return source; }
+ const ComponentList &get_components() const { return components; }
+ bool get_buildable() const { return buildable; }
+ void resolve_refs();
+
+ static Package *create(Builder &, const std::string &);
+private:
+ Builder &builder;
+ std::string name;
+ std::string version;
+ std::string description;
+ std::list<PackageRef> requires;
+ BuildInfo build_info;
+ BuildInfo export_binfo;
+ Msp::Path::Path source;
+ bool buildable;
+ ComponentList components;
+};
+
+#endif
--- /dev/null
+#include <fstream>
+#include <msp/regex++/regex++.h>
+#include "builder.h"
+#include "component.h"
+#include "sourcefile.h"
+
+using namespace std;
+using namespace Msp;
+
+SourceFile::SourceFile(Builder &b, const Component *c, const string &n):
+ Target(b, c?&c->get_package():0, n),
+ comp(c)
+{ }
+
+void SourceFile::find_depends()
+{
+ ifstream in(name.c_str());
+ if(!in) return;
+
+ Regex r_include("^[ \t]*#include[ \t]+([\"<].*)[\">]");
+
+ string line;
+ while(getline(in, line))
+ if(RegMatch match=r_include.match(line))
+ includes.push_back(match[1].str());
+
+ string path=name.substr(0, name.rfind('/'));
+ for(list<string>::iterator i=includes.begin(); i!=includes.end(); ++i)
+ {
+ Target *hdr=builder.get_header(*i, path, comp->get_build_info().incpath);
+ if(hdr)
+ add_depend(hdr);
+ }
+}
--- /dev/null
+#ifndef SOURCEFILE_H_
+#define SOURCEFILE_H_
+
+#include "target.h"
+
+class Component;
+
+class SourceFile: public Target
+{
+public:
+ SourceFile(Builder &, const Component *, const std::string &);
+ void find_depends();
+ const char *get_type() const { return "SourceFile"; }
+private:
+ const Component *comp;
+ std::list<std::string> includes;
+};
+
+#endif
--- /dev/null
+#include "builder.h"
+#include "target.h"
+
+using namespace std;
+
+Target *TargetRef::get_target()
+{
+ if(!target)
+ target=builder.get_target(name);
+ return target;
+}
+
+Target *Target::get_buildable_target()
+{
+ if(rebuild && ready_for_build && !building)
+ return this;
+
+ for(list<Target *>::iterator i=depends.begin(); i!=depends.end(); ++i)
+ {
+ Target *tgt=(*i)->get_buildable_target();
+ if(tgt)
+ return tgt;
+ }
+
+ return 0;
+}
+
+void Target::add_depend(Target *dep)
+{
+ depends.push_back(dep);
+ dep->rdepends.push_back(this);
+}
+
+Target::Target(Builder &b, const Package *p, const string &n):
+ builder(b),
+ package(p),
+ name(n),
+ building(false),
+ rebuild(false),
+ ready_for_build(false)
+{ }
+
+void Target::mark_rebuild(const std::string &reason)
+{
+ rebuild=true;
+ rebuild_reason=reason;
+}
--- /dev/null
+#ifndef TARGET_H_
+#define TARGET_H_
+
+#include <list>
+#include <string>
+#include <msp/time/timestamp.h>
+
+class Builder;
+class Package;
+class Target;
+
+class TargetRef
+{
+public:
+ TargetRef(Builder &b, const std::string &n): builder(b), name(n) { }
+ Target *get_target();
+private:
+ Builder &builder;
+ std::string name;
+ Target *target;
+};
+
+class Target
+{
+public:
+ const std::string &get_name() const { return name; }
+ Target *get_buildable_target();
+ bool get_rebuild() const { return rebuild; }
+ const std::string &get_rebuild_reason() const { return rebuild_reason; }
+ virtual void find_depends()=0;
+ void add_depend(Target *);
+ virtual const char *get_type() const=0;
+ virtual ~Target() { }
+protected:
+ Builder &builder;
+ const Package *package;
+ std::string name;
+ bool building;
+ bool rebuild;
+ std::string rebuild_reason;
+ Msp::Time::TimeStamp mtime;
+ std::list<Target *> depends;
+ std::list<Target *> rdepends;
+ bool ready_for_build;
+
+ Target(Builder &, const Package *, const std::string &);
+ void mark_rebuild(const std::string &);
+};
+
+#endif
--- /dev/null
+#ifndef VIRTUALTARGET_H_
+#define VIRTUALTARGET_H_
+
+#include "target.h"
+
+class VirtualTarget: public Target
+{
+public:
+ VirtualTarget(Builder &b, const std::string &n): Target(b,0,n) { }
+ const char *get_type() const { return "VirtualTarget"; }
+ void find_depends() { }
+};
+
+#endif