]> git.tdb.fi Git - builder.git/commitdiff
Builder can build itself now.
authorMikko Rasa <tdb@tdb.fi>
Sun, 27 Aug 2006 21:09:00 +0000 (21:09 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sun, 27 Aug 2006 21:09:00 +0000 (21:09 +0000)
32 files changed:
Build
source/action.cpp [new file with mode: 0644]
source/action.h [new file with mode: 0644]
source/builder.cpp
source/builder.h
source/buildinfo.cpp [new file with mode: 0644]
source/buildinfo.h
source/compile.cpp [new file with mode: 0644]
source/compile.h [new file with mode: 0644]
source/component.cpp
source/component.h
source/config.cpp [new file with mode: 0644]
source/config.h [new file with mode: 0644]
source/executable.cpp
source/executable.h
source/externalaction.cpp [new file with mode: 0644]
source/externalaction.h [new file with mode: 0644]
source/install.h [new file with mode: 0644]
source/link.cpp [new file with mode: 0644]
source/link.h [new file with mode: 0644]
source/objectfile.cpp
source/objectfile.h
source/option.cpp [new file with mode: 0644]
source/option.h [new file with mode: 0644]
source/package.cpp
source/package.h
source/sourcefile.cpp
source/sourcefile.h
source/target.cpp
source/target.h
source/virtualtarget.cpp [new file with mode: 0644]
source/virtualtarget.h

diff --git a/Build b/Build
index 4db65e6774164b966d58887ee29ec9244518cd97..16e9982c9117b7898d3b37947b68b727234cd0e2 100644 (file)
--- a/Build
+++ b/Build
@@ -3,10 +3,11 @@ package "builder"
        version "0.1";
        description "Mikkosoft Productions software builder";
 
-       require "mspframework";
+       require "mspcore";
        require "mspparser";
        require "msppath";
        require "mspregex++";
+       require "sigc++-2.0";
 
        program "builder"
        {
diff --git a/source/action.cpp b/source/action.cpp
new file mode 100644 (file)
index 0000000..7ed0da1
--- /dev/null
@@ -0,0 +1,16 @@
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include "action.h"
+
+using namespace std;
+
+void Action::announce(const string &pkg, const string &tool, const string &tgt)
+{
+       ostringstream line;
+       line<<left;
+       line<<'['<<setw(10)<<pkg<<"] ";
+       line<<'['<<setw(4)<<tool<<"] ";
+       line<<tgt;
+       cout<<line.str()<<'\n';
+}
diff --git a/source/action.h b/source/action.h
new file mode 100644 (file)
index 0000000..60d1450
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef ACTION_H_
+#define ACTION_H_
+
+#include <string>
+#include <sigc++/sigc++.h>
+
+class Builder;
+
+class Action
+{
+public:
+       sigc::signal<void> signal_done;
+       
+       virtual int check()=0;
+       virtual ~Action() { }
+protected:
+       Builder &builder;
+       
+       Action(Builder &b): builder(b) { }
+       void announce(const std::string &, const std::string &, const std::string &);
+};
+
+#endif
index 3f93a672cfe46e8ef0de8c7e9c8ca5da8279ee3c..05ecf877c7f47265131491fa50f0e00189015c1d 100644 (file)
@@ -2,6 +2,8 @@
 #include <msp/strutils.h>
 #include <msp/parser/parser.h>
 #include <msp/path/utils.h>
+#include <msp/time/units.h>
+#include "action.h"
 #include "builder.h"
 #include "executable.h"
 #include "header.h"
@@ -18,7 +20,15 @@ Builder::Builder(int argc, char **argv):
        cwd(Path::getcwd())
 {
        for(int i=1; i<argc; ++i)
-               cmdline_targets.push_back(argv[i]);
+       {
+               string v(argv[i]);
+               unsigned equal=v.find('=');
+               if(equal!=string::npos)
+                       cmdline_options.insert(RawOptionMap::value_type(v.substr(0, equal), v.substr(equal+1)));
+               else
+                       cmdline_targets.push_back(argv[i]);
+       }
+
        if(cmdline_targets.empty())
                cmdline_targets.push_back("default");
 }
@@ -33,24 +43,33 @@ Package *Builder::get_package(const string &n)
        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());
+       string srcdir=strip(run_command(argv));
+       cout<<srcdir;
        
+       list<Path::Path> dirs;
        if(!srcdir.empty())
-               load_build_file(srcdir/"Build");
+               dirs.push_back(srcdir);
 
-       return 0;
+       string dirname=n;
+       if(dirname.compare(0, 3, "msp"))
+               dirname.erase(0, 3);
+       dirs.push_back(cwd/dirname);
+       dirs.push_back(cwd/".."/dirname);
+
+       for(list<Path::Path>::iterator j=dirs.begin(); j!=dirs.end(); ++j)
+               if(!load_build_file(*j/"Build"))
+               {
+                       i=packages.find(n);
+                       if(i!=packages.end())
+                               return i->second;
+                       return 0;
+               }
+       
+       Package *pkg=Package::create(*this, n);
+       packages.insert(PackageMap::value_type(n, pkg));
+       new_pkgs.push_back(pkg);
+
+       return pkg;
 }
 
 Target *Builder::get_target(const string &n)
@@ -100,6 +119,27 @@ int Builder::main()
                pkg->resolve_refs();
        }
 
+       std::list<std::string> missing;
+       for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+       {
+               const list<PackageRef> &requires=i->second->get_requires();
+               for(list<PackageRef>::const_iterator j=requires.begin(); j!=requires.end(); ++j)
+                       if(!j->get_package())
+                               missing.push_back(j->get_name());
+       }
+
+       if(!missing.empty())
+       {
+               missing.sort();
+               missing.unique();
+               cerr<<"The following packages were not found on the system:\n";
+               for(list<string>::iterator i=missing.begin(); i!=missing.end(); ++i)
+                       cerr<<"  "<<*i<<'\n';
+               cerr<<"Please install them and try again.\n";
+       }
+
+       default_pkg->create_build_info();
+
        cout<<"Active packages:";
        for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
        {
@@ -109,14 +149,17 @@ int Builder::main()
        }
        cout<<'\n';
        
-       create_targets();
+       if(create_targets())
+               return 1;
 
        for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
-               cout<<i->second->get_name()<<' '<<i->second->get_type()<<'\n';
+               cout<<i->second->get_name()<<' '<<i->second->get_type()<<' '<<i->second->get_rebuild()<<' '<<i->second->get_rebuild_reason()<<'\n';
 
        cout<<"Active targets: "<<targets.size()<<'\n';
 
-       return 0;
+       build();
+
+       return exit_code;
 }
 
 int Builder::load_build_file(const Path::Path &fn)
@@ -132,28 +175,30 @@ int Builder::load_build_file(const Path::Path &fn)
        return 0;
 }
 
-void Builder::create_targets()
+int Builder::create_targets()
 {
        Target *world=new VirtualTarget(*this, "world");
        add_target(world);
+
        Target *def_tgt=new VirtualTarget(*this, "default");
        add_target(def_tgt);
+       world->add_depend(def_tgt);
 
        for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
        {
-               cout<<i->second->get_source()<<'\n';
+               if(!i->second->get_buildable())
+                       continue;
+
                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")
                                {
@@ -172,6 +217,8 @@ void Builder::create_targets()
                        add_target(exe);
                        if(i->second==default_pkg)
                                def_tgt->add_depend(exe);
+                       else
+                               world->add_depend(exe);
                }
        }
 
@@ -181,6 +228,26 @@ void Builder::create_targets()
                new_tgts.erase(new_tgts.begin());
                tgt->find_depends();
        }
+
+       Target *cmdline=new VirtualTarget(*this, "cmdline");
+       add_target(cmdline);
+       world->add_depend(cmdline);
+       for(list<string>::iterator i=cmdline_targets.begin(); i!=cmdline_targets.end(); ++i)
+       {
+               Target *tgt=get_target(*i);
+               if(!tgt)
+                       tgt=get_target((cwd/ *i).str());
+               if(!tgt)
+               {
+                       cerr<<"I don't know anything about "<<*i<<'\n';
+                       return 1;
+               }
+               cmdline->add_depend(tgt);
+       }
+
+       world->prepare();
+
+       return 0;
 }
 
 Target *Builder::check_header(const Msp::Path::Path &fn)
@@ -202,6 +269,44 @@ void Builder::add_target(Target *t)
        new_tgts.push_back(t);
 }
 
+int Builder::build()
+{
+       Target *cmdline=get_target("cmdline");
+       list<Action *> actions;
+       bool fail=false;
+
+       while(cmdline->get_rebuild() && !fail)
+       {
+               if(actions.empty() && !fail)
+               {
+                       Target *tgt=cmdline->get_buildable_target();
+                       if(tgt)
+                       {
+                               cout<<"Build "<<tgt->get_name()<<'\n';
+                               Action *action=tgt->build();
+                               if(action)
+                                       actions.push_back(action);
+                       }
+               }
+               else
+                       sleep(10*Time::msec);
+
+               for(list<Action *>::iterator i=actions.begin(); i!=actions.end();)
+               {
+                       int status=(*i)->check();
+                       if(status>=0)
+                       {
+                               delete *i;
+                               i=actions.erase(i);
+                               if(status>0)
+                                       fail=true;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 Application::RegApp<Builder> Builder::reg;
 
 Builder::Loader::Loader(Builder &b, const Path::Path &s):
index f66b31096773f35c04c01aac3a343314a066271d..bdc5e2fb931e36d6df53229702197ed04447f504 100644 (file)
@@ -4,9 +4,10 @@
 #include <list>
 #include <map>
 #include <string>
-#include <msp/framework/application.h>
+#include <msp/core/application.h>
 #include <msp/parser/loader.h>
 #include <msp/path/path.h>
+#include "config.h"
 
 class Package;
 class Target;
@@ -32,10 +33,12 @@ private:
                void package(const std::string &);
        };
 
-       typedef std::map<std::string, Package *> PackageMap;
-       typedef std::map<std::string, Target *>  TargetMap;
+       typedef std::map<std::string, Package *>   PackageMap;
+       typedef std::map<std::string, Target *>    TargetMap;
+       typedef std::map<std::string, std::string> ToolMap;
        
        std::list<std::string> cmdline_targets;
+       RawOptionMap cmdline_options;
        TargetMap  targets;
        PackageMap packages;
        unsigned   verbose;
@@ -44,11 +47,13 @@ private:
        std::list<Package *> new_pkgs;
        TargetMap  includes;
        std::list<Target *> new_tgts;
+       ToolMap    tools;
 
        int load_build_file(const Msp::Path::Path &);
-       void create_targets();
+       int create_targets();
        Target *check_header(const Msp::Path::Path &);
        void add_target(Target *);
+       int build();
        
        static Msp::Application::RegApp<Builder> reg;
 };
diff --git a/source/buildinfo.cpp b/source/buildinfo.cpp
new file mode 100644 (file)
index 0000000..7669546
--- /dev/null
@@ -0,0 +1,33 @@
+#include <msp/algo.h>
+#include "buildinfo.h"
+
+using namespace Msp;
+
+void BuildInfo::add(const BuildInfo &bi)
+{
+       cflags.insert(cflags.end(), bi.cflags.begin(), bi.cflags.end());
+       defines.insert(defines.end(), bi.defines.begin(), bi.defines.end());
+       incpath.insert(incpath.end(), bi.incpath.begin(), bi.incpath.end());
+       ldflags.insert(ldflags.end(), bi.ldflags.begin(), bi.ldflags.end());
+       libpath.insert(libpath.end(), bi.libpath.begin(), bi.libpath.end());
+       libs.insert(libs.end(), bi.libs.begin(), bi.libs.end());
+}
+
+void BuildInfo::unique()
+{
+       unique(cflags);
+       unique(defines);
+       unique(incpath);
+       unique(ldflags);
+       unique(libpath);
+       unique(libs);
+}
+
+void BuildInfo::unique(InfoList &l)
+{
+       InfoList l2;
+       for(InfoList::iterator i=l.begin(); i!=l.end(); ++i)
+               if(!contains(l2, *i))
+                       l2.push_back(*i);
+       swap(l, l2);
+}
index 02549ea3a00628a781319700b26a8fcd9b38463d..a4035b1c47c3ecb06ce572a388d48dbe3b8219b7 100644 (file)
@@ -18,6 +18,8 @@ public:
 
        void add(const BuildInfo &);
        void unique();
+private:
+       void unique(InfoList &);
 };
 
 #endif
diff --git a/source/compile.cpp b/source/compile.cpp
new file mode 100644 (file)
index 0000000..15aaee9
--- /dev/null
@@ -0,0 +1,47 @@
+#include <msp/path/utils.h>
+#include "buildinfo.h"
+#include "compile.h"
+#include "component.h"
+#include "package.h"
+
+using namespace std;
+using namespace Msp;
+
+Compile::Compile(Builder &b, const Path::Path &s, const Path::Path &o, const Component &c):
+       ExternalAction(b),
+       source(s),
+       object(o),
+       comp(c)
+{
+       string ext=Path::splitext(source.str()).ext;
+       const char *tool=0;
+       if(ext==".cpp" || ext==".cc")
+       {
+               tool="CXX";
+               argv.push_back("g++");
+       }
+       else
+       {
+               tool="CC";
+               argv.push_back("gcc");
+       }
+       argv.push_back("-c");
+       
+       const BuildInfo &binfo=comp.get_build_info();
+       for(list<string>::const_iterator i=binfo.cflags.begin(); i!=binfo.cflags.end(); ++i)
+               argv.push_back(*i);
+       for(list<string>::const_iterator i=binfo.incpath.begin(); i!=binfo.incpath.end(); ++i)
+               argv.push_back("-I"+*i);
+       for(list<string>::const_iterator i=binfo.defines.begin(); i!=binfo.defines.end(); ++i)
+               argv.push_back("-D"+*i);
+       
+       argv.push_back("-o");
+       argv.push_back(object.str());
+       argv.push_back(source.str());
+
+       Path::mkpath(object.subpath(0, object.size()-1), 0755);
+
+       announce(comp.get_package().get_name(), tool, relative(object.str(), comp.get_package().get_source()).str());
+
+       launch();
+}
diff --git a/source/compile.h b/source/compile.h
new file mode 100644 (file)
index 0000000..42ae95c
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef COMPILE_H_
+#define COMPILE_H_
+
+#include <msp/path/path.h>
+#include "externalaction.h"
+
+class Component;
+
+class Compile: public ExternalAction
+{
+public:
+       Compile(Builder &, const Msp::Path::Path &, const Msp::Path::Path &, const Component &);
+private:
+       Msp::Path::Path source;
+       Msp::Path::Path object;
+       const Component &comp;
+};
+
+#endif
index bab7ba1e264a3e80e6ebfba74133e2c3fd7a843f..a94a5663563d677e5a14f9fde795599ff48de93d 100644 (file)
@@ -9,6 +9,12 @@ Component::Component(Package &p, Type t, const string &n):
        name(n)
 { }
 
+void Component::create_build_info()
+{
+       build_info=pkg.get_build_info();
+       build_info.unique();
+}
+
 Component::Loader::Loader(Component &c):
        comp(c)
 {
index c5e1d0ccb9a1a98b7296493ec32045a6129fd059..a27ddcab28a2ebaa4203b17726a3e702f03d4a97 100644 (file)
@@ -26,15 +26,19 @@ public:
        {
                PROGRAM,
                LIBRARY,
-               MODULE
+               MODULE,
+               HEADERS
        };
 
        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; }
+       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; }
+       bool              get_install() const          { return install; }
+       const std::string &get_install_headers() const { return install_headers; }
+       void              create_build_info();
 protected:
        Package     &pkg;
        Type        type;
diff --git a/source/config.cpp b/source/config.cpp
new file mode 100644 (file)
index 0000000..50e0de9
--- /dev/null
@@ -0,0 +1,48 @@
+#include <msp/error.h>
+#include "config.h"
+
+using namespace std;
+using namespace Msp;
+
+void Config::add_option(const string &n, const string &v, const string &d)
+{
+       options.insert(OptionMap::value_type(n, Option(n, v, d)));
+}
+
+const Config::Option &Config::get_option(const string &name) const
+{
+       OptionMap::const_iterator i=options.find(name);
+       if(i==options.end())
+               throw Exception("Tried to access nonexistent option "+name);
+
+       return i->second;
+}
+
+bool Config::is_option(const string &name) const
+{
+       return options.count(name);
+}
+
+bool Config::process(const RawOptionMap &opts)
+{
+       bool changed=false;
+       for(RawOptionMap::const_iterator i=opts.begin(); i!=opts.end(); ++i)
+       {
+               OptionMap::iterator j=options.find(i->first);
+               if(j!=options.end())
+               {
+                       if(i->second!=j->second.value)
+                               changed=true;
+                       j->second.value=i->second;
+               }
+       }
+
+       return changed;
+}
+
+Config::Option::Option(const string &n, const string &v, const string &d):
+       name(n),
+       defv(v),
+       descr(d),
+       value(v)
+{ }
diff --git a/source/config.h b/source/config.h
new file mode 100644 (file)
index 0000000..1b78e3a
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include <map>
+#include <string>
+#include "option.h"
+
+typedef std::map<std::string, std::string> RawOptionMap;
+
+class Config
+{
+public:
+       struct Option
+       {
+               std::string name;
+               std::string defv;
+               std::string descr;
+               std::string value;
+
+               Option(const std::string &, const std::string &, const std::string &);
+       };
+       typedef std::map<std::string, Option> OptionMap;
+
+       void add_option(const std::string &, const std::string &, const std::string &);
+       const Option &get_option(const std::string &) const;
+       bool is_option(const std::string &) const;
+       bool process(const RawOptionMap &);
+       void load(const std::string &); 
+private:
+       OptionMap options;
+};
+
+#endif
index 2dfeee8affbbbce98ee5a812da75433ca60d32b2..156823340a533849346e9425a54d0d59eb35e0ca 100644 (file)
@@ -1,5 +1,6 @@
 #include "component.h"
 #include "executable.h"
+#include "link.h"
 #include "objectfile.h"
 #include "package.h"
 
@@ -9,10 +10,16 @@ Executable::Executable(Builder &b, const Component &c, const list<ObjectFile *>
        Target(b, &c.get_package(), generate_target_name(c)),
        comp(c)
 {
+       buildable=true;
        for(list<ObjectFile *>::const_iterator i=objs.begin(); i!=objs.end(); ++i)
                add_depend(*i);
 }
 
+Action *Executable::build()
+{
+       return Target::build(new Link(builder, *this, comp));;
+}
+
 string Executable::generate_target_name(const Component &comp)
 {
        string prefix;
index 3e34851e4abef92fa2ceb8a04068e4741a03b870..52b0ceedd2cf4adb62d78df66b2c18e920bc0c16 100644 (file)
@@ -12,6 +12,7 @@ public:
        Executable(Builder &, const Component &, const std::list<ObjectFile *> &);
        const char *get_type() const { return "Executable"; }
        void find_depends() { }
+       Action *build();
 private:
        const Component &comp;
 
diff --git a/source/externalaction.cpp b/source/externalaction.cpp
new file mode 100644 (file)
index 0000000..d4a48d7
--- /dev/null
@@ -0,0 +1,52 @@
+#include <sys/wait.h>
+#include <msp/iter.h>
+#include "builder.h"
+#include "externalaction.h"
+
+using namespace std;
+using namespace Msp;
+
+int ExternalAction::check()
+{
+       if(!pid)
+               return 255;
+
+       int status;
+       if(waitpid(pid, &status, WNOHANG)==pid)
+       {
+               signal_done.emit();
+               if(WIFEXITED(status))
+                       return WEXITSTATUS(status);
+               else
+                       return 254;
+       }
+       else
+               return -1;
+}
+
+void ExternalAction::launch()
+{
+       if(builder.get_verbose()>=1)
+       {
+               for(list<string>::const_iterator i=argv.begin(); i!=argv.end(); ++i)
+               {
+                       if(i!=argv.begin())
+                               cout<<' ';
+                       cout<<*i;
+               }
+               cout<<'\n';
+       }
+       
+       pid=fork();
+       if(pid==0)
+       {
+               char *argv_[argv.size()+1];
+               for(CountingIterator<string, list<string>::iterator> i=argv.begin(); i!=argv.end(); ++i)
+                       argv_[i.count()]=strdup(i->c_str());
+               argv_[argv.size()]=0;
+               execvp(argv_[0], argv_);
+               exit(1);
+       }
+       else if(pid<0)
+               pid=0;
+}
diff --git a/source/externalaction.h b/source/externalaction.h
new file mode 100644 (file)
index 0000000..52eed37
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef EXTERNALACTION_H_
+#define EXTERNALACTION_H_
+
+#include <list>
+#include <string>
+#include "action.h"
+
+class ExternalAction: public Action
+{
+public:
+       int check();
+protected:
+       std::list<std::string> argv;
+       int pid;
+       
+       ExternalAction(Builder &b): Action(b) { }
+       void launch();
+};
+
+#endif
diff --git a/source/install.h b/source/install.h
new file mode 100644 (file)
index 0000000..3c179c6
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef INSTALL_H_
+#define INSTALL_H_
+
+#include <msp/core/thread.h>
+#include <msp/path/path.h>
+#include "action.h"
+
+class Install: public Action
+{
+public:
+       Install(const Msp::Path::Path &, const Msp::Path::Path &);
+private:
+       class Worker: public Msp::Thread
+       {
+       public:
+               Worker(Install &i): install(i), done(false) { launch(); }
+       private:
+               Install &install;
+               bool done;
+
+               void main();
+       };
+
+       Msp::Path::Path src;
+       Msp::Path::Path dest;
+       Worker worker;
+};
+
+#endif
diff --git a/source/link.cpp b/source/link.cpp
new file mode 100644 (file)
index 0000000..242c966
--- /dev/null
@@ -0,0 +1,35 @@
+#include <msp/path/utils.h>
+#include "component.h"
+#include "executable.h"
+#include "link.h"
+#include "package.h"
+
+using namespace std;
+using namespace Msp;
+
+Link::Link(Builder &b, const Executable &exe, const Component &comp):
+       ExternalAction(b)
+{
+       argv.push_back("g++");
+       
+       const BuildInfo &binfo=comp.get_build_info();
+       for(list<string>::const_iterator i=binfo.ldflags.begin(); i!=binfo.ldflags.end(); ++i)
+               argv.push_back(*i);
+       for(list<string>::const_iterator i=binfo.libpath.begin(); i!=binfo.libpath.end(); ++i)
+               argv.push_back("-L"+*i);
+       for(list<string>::const_iterator i=binfo.libs.begin(); i!=binfo.libs.end(); ++i)
+               argv.push_back("-l"+*i);
+       
+       argv.push_back("-o");
+       argv.push_back(exe.get_name());
+       const list<Target *> &deps=exe.get_depends();
+       for(list<Target *>::const_iterator i=deps.begin(); i!=deps.end(); ++i)
+               argv.push_back((*i)->get_name());
+
+       Path::Path epath=exe.get_name();
+       Path::mkpath(epath.subpath(0, epath.size()-1), 0755);
+
+       announce(comp.get_package().get_name(), "LINK", relative(epath, comp.get_package().get_source()).str());
+
+       launch();
+}
diff --git a/source/link.h b/source/link.h
new file mode 100644 (file)
index 0000000..eecbcbd
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef LINK_H_
+#define LINK_H_
+
+#include "externalaction.h"
+
+class Component;
+class Executable;
+
+class Link: public ExternalAction
+{
+public:
+       Link(Builder &, const Executable &, const Component &);
+};
+
+#endif
index b308c2b34301bc51c81929b25df7e86a5d6fbfa1..d29cd11df850759833705e0103264c50d09de512 100644 (file)
@@ -1,5 +1,6 @@
 #include <msp/path/utils.h>
 #include "builder.h"
+#include "compile.h"
 #include "component.h"
 #include "objectfile.h"
 #include "package.h"
@@ -12,9 +13,15 @@ ObjectFile::ObjectFile(Builder &b, const Component &c, SourceFile &src):
        Target(b, &c.get_package(), generate_target_name(c, src.get_name())),
        comp(c)
 {
+       buildable=true;
        add_depend(&src);
 }
 
+Action *ObjectFile::build()
+{
+       return Target::build(new Compile(builder, depends.front()->get_name(), name, comp));
+}
+
 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();
index d270529b73cdefac2ca56a5be84f40a3669f7891..9fa49794610bcd2ba53b2ffa5d87e2718becc2de 100644 (file)
@@ -12,6 +12,7 @@ public:
        ObjectFile(Builder &, const Component &, SourceFile &);
        const char *get_type() const { return "ObjectFile"; }
        void find_depends() { }
+       Action *build();
 private:
        const Component &comp;
        
diff --git a/source/option.cpp b/source/option.cpp
new file mode 100644 (file)
index 0000000..8f1a6e9
--- /dev/null
@@ -0,0 +1,10 @@
+#include "option.h"
+
+using namespace std;
+
+Option::Option(const string &n, const string &v, const string &d):
+       name(n),
+       defv(v),
+       descr(d),
+       value(v)
+{ }
diff --git a/source/option.h b/source/option.h
new file mode 100644 (file)
index 0000000..cbf1769
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef OPTION_H_
+#define OPTION_H_
+
+#include <string>
+
+class Option
+{
+public:
+       Option(const std::string &, const std::string &, const std::string &);
+       void set_value(const std::string &v) { value=v; }
+       const std::string &get_value() const { return value; }
+       const std::string &get_name() const { return name; }
+       const std::string &get_default_value() const { return defv; }
+       const std::string &get_description() const { return descr; }
+private:
+       std::string name;
+       std::string defv;
+       std::string descr;
+       std::string value;
+};
+
+#endif
index 5cf7e07b6bd7e67e00cdf550c217dfb6dc80a6cb..fcbe9f7a6208fe8c10f3bac150f78d5c6986138c 100644 (file)
@@ -1,4 +1,6 @@
+#include <msp/strutils.h>
 #include "builder.h"
+#include "misc.h"
 #include "package.h"
 
 using namespace std;
@@ -10,7 +12,7 @@ PackageRef::PackageRef(Builder &b, const string &n):
        package(0)
 { }
 
-Package *PackageRef::get_package()
+Package *PackageRef::resolve()
 {
        if(!package)
                package=builder.get_package(name);
@@ -21,14 +23,135 @@ Package::Package(Builder &b, const string &n, const Path::Path &s):
        builder(b),
        name(n),
        source(s),
-       buildable(false)
+       buildable(true),
+       build_info_ready(false)
+{ }
+
+Package::Package(Builder &b, const string &n, const vector<string> &info):
+       builder(b),
+       name(n),
+       buildable(false),
+       build_info_ready(true)
 {
+       for(vector<string>::const_iterator i=info.begin(); i!=info.end(); ++i)
+       {
+               if(!i->compare(0, 2, "-I"))
+                       export_binfo.incpath.push_back(i->substr(2));
+               else if(!i->compare(0, 2, "-D"))
+                       export_binfo.defines.push_back(i->substr(2));
+               else if(!i->compare(0, 2, "-L"))
+                       export_binfo.libpath.push_back(i->substr(2));
+               else if(!i->compare(0, 2, "-l"))
+                       export_binfo.libs.push_back(i->substr(2));
+       }
 }
 
 void Package::resolve_refs()
 {
        for(list<PackageRef>::iterator i=requires.begin(); i!=requires.end(); ++i)
-               i->get_package();
+               i->resolve();
+}
+
+void Package::create_build_info()
+{
+       if(build_info_ready)
+               return;
+       
+       for(list<PackageRef>::iterator i=requires.begin(); i!=requires.end(); ++i)
+       {
+               if(!i->get_package())
+                       continue;
+               i->get_package()->create_build_info();
+               build_info.add(i->get_package()->get_exported_binfo());
+       }
+       
+       build_info.cflags.push_back("-Wall");
+       build_info.cflags.push_back("-Wshadow");
+       build_info.cflags.push_back("-Wextra");
+       build_info.cflags.push_back("-Wpointer-arith");
+       build_info.cflags.push_back("-Wconversion");
+       build_info.cflags.push_back("-Werror");
+
+       unsigned flags=get_install_flags();
+
+       if(flags&INCLUDE)
+               export_binfo.incpath.push_back((Path::Path(config.get_option("prefix").value)/"include").str());
+       if(flags&LIB)
+               export_binfo.libpath.push_back((Path::Path(config.get_option("prefix").value)/"lib").str());
+
+       build_info.unique();
+       export_binfo.unique();
+
+       for(list<Component>::iterator i=components.begin(); i!=components.end(); ++i)
+       {
+               i->create_build_info();
+               if(i->get_type()==Component::LIBRARY)
+                       export_binfo.libs.push_back(i->get_name());
+       }
+
+       build_info_ready=true;
+}
+
+void Package::process_options(const RawOptionMap &opts)
+{
+       config.process(opts);
+}
+
+Package *Package::create(Builder &b, const string &name)
+{
+       list<string> argv;
+       argv.push_back("pkg-config");
+       argv.push_back("--cflags");
+       argv.push_back("--libs");
+       argv.push_back(name);
+       vector<string> info=split(run_command(argv));
+       
+       if(info.empty())
+               return 0;
+       
+       Package *pkg=new Package(b, name, info);
+       return pkg;
+}
+
+void Package::init_buildable()
+{
+       buildable=true;
+
+       config.add_option("tempdir",  "temp", "Directory for storing temporary files");
+       config.add_option("optimize", "0",    "Apply compiler optimizations");
+       config.add_option("debug",    "0",    "Produce debugging symbols");
+
+       const char *home=getenv("HOME");
+       unsigned flags=get_install_flags();
+       if(flags)
+               config.add_option("prefix",     string(home)+"/local"/*"/usr"*/,            "Installation prefix");
+       /*if(flags&INCLUDE)
+               config.add_option("includedir", "$prefix/include", "Header installation directory");
+       if(flags&BIN)
+               config.add_option("includedir", "$prefix/bin",     "Binary installation directory");
+       if(flags&LIB)
+               config.add_option("includedir", "$prefix/lib",     "Library installation directory");
+       if(flags&DATA)
+               config.add_option("includedir", "$prefix/share",   "Data installation directory");*/
+}
+
+unsigned Package::get_install_flags()
+{
+       unsigned flags=0;
+       for(ComponentList::iterator i=components.begin(); i!=components.end(); ++i)
+       {
+               if(i->get_install())
+               {
+                       if(i->get_type()==Component::PROGRAM)
+                               flags|=BIN;
+                       else if(i->get_type()==Component::LIBRARY || i->get_type()==Component::MODULE)
+                               flags|=LIB;
+               }
+               if(!i->get_install_headers().empty())
+                       flags|=INCLUDE;
+       }
+
+       return flags;
 }
 
 Package::Loader::Loader(Package &p):
@@ -43,7 +166,7 @@ Package::Loader::Loader(Package &p):
 
 Package::Loader::~Loader()
 {
-       pkg.buildable=true;
+       pkg.init_buildable();
 }
 
 void Package::Loader::require(const string &n)
index 42c453ad2e0e99200c905dfdd05f62327b22ac54..aafec0fc54cd2c58d0b63ef2f4758ab647f8ccdf 100644 (file)
@@ -6,6 +6,7 @@
 #include <msp/parser/loader.h>
 #include "buildinfo.h"
 #include "component.h"
+#include "config.h"
 
 class Builder;
 class Package;
@@ -14,7 +15,9 @@ class PackageRef
 {
 public:
        PackageRef(Builder &, const std::string &);
-       Package *get_package();
+       const std::string &get_name() const { return name; }
+       Package *get_package() const { return package; }
+       Package *resolve();
 private:
        Builder     &builder;
        std::string name;
@@ -39,15 +42,29 @@ public:
        };
 
        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; }
+       Package(Builder &, const std::string &, const std::vector<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();
+       bool                get_buildable() const   { return buildable; }
+       const Config        &get_config() const     { return config; }
+       const std::list<PackageRef> &get_requires() const { return requires; }
+       const BuildInfo     &get_build_info() const { return build_info; }
+       const BuildInfo     &get_exported_binfo() const { return export_binfo; }
+       void                resolve_refs();
+       void                create_build_info();
+       void                process_options(const RawOptionMap &);
 
        static Package *create(Builder &, const std::string &);
 private:
+       enum InstallFlags
+       {
+               INCLUDE=1,
+               BIN=2,
+               LIB=4,
+               DATA=8
+       };
+       
        Builder       &builder;
        std::string   name;
        std::string   version;
@@ -58,6 +75,11 @@ private:
        Msp::Path::Path source;
        bool          buildable;
        ComponentList components;
+       Config        config;
+       bool          build_info_ready;
+
+       void init_buildable();
+       unsigned get_install_flags();
 };
 
 #endif
index 55910a64823742b12f45b259289ebae5270daa6c..42142536607b9a4540929703ae14383538823ea8 100644 (file)
@@ -32,3 +32,9 @@ void SourceFile::find_depends()
                        add_depend(hdr);
        }
 }
+
+void SourceFile::check_rebuild()
+{
+       for(list<Target *>::iterator i=depends.begin(); i!=depends.end(); ++i)
+               vmtime=max(vmtime, (*i)->get_virtual_mtime());
+}
index 962c575558f8eb04bb79503d946391a302def8f6..af25fe9253428924968f85654746a1ac31edc219 100644 (file)
@@ -9,11 +9,14 @@ class SourceFile: public Target
 {
 public:
        SourceFile(Builder &, const Component *, const std::string &);
-       void find_depends();
        const char *get_type() const { return "SourceFile"; }
+       void find_depends();
+       Action *build() { rebuild=false; return 0; }
 private:
        const Component *comp;
        std::list<std::string> includes;
+
+       void check_rebuild();
 };
 
 #endif
index 481d2f4a68dd460a6f3c0a8222af58be349208c9..8ba3cc38896faefc3493329da6a2570dfd83a268 100644 (file)
@@ -1,27 +1,26 @@
+#include <msp/path/utils.h>
+#include "action.h"
 #include "builder.h"
 #include "target.h"
 
 using namespace std;
-
-Target *TargetRef::get_target()
-{
-       if(!target)
-               target=builder.get_target(name);
-       return target;
-}
+using namespace Msp;
 
 Target *Target::get_buildable_target()
 {
-       if(rebuild && ready_for_build && !building)
-               return this;
-
+       bool self_ok=true;
        for(list<Target *>::iterator i=depends.begin(); i!=depends.end(); ++i)
        {
                Target *tgt=(*i)->get_buildable_target();
                if(tgt)
                        return tgt;
+               else if((*i)->get_rebuild())
+                       self_ok=false;
        }
 
+       if(self_ok && rebuild && !building)
+               return this;
+
        return 0;
 }
 
@@ -31,17 +30,68 @@ void Target::add_depend(Target *dep)
        dep->rdepends.push_back(this);
 }
 
+void Target::prepare()
+{
+       if(prepared)
+               return;
+
+       for(list<Target *>::iterator i=depends.begin(); i!=depends.end(); ++i)
+               (*i)->prepare();
+
+       check_rebuild();
+}
+
 Target::Target(Builder &b, const Package *p, const string &n):
        builder(b),
        package(p),
        name(n),
        building(false),
        rebuild(false),
-       ready_for_build(false)
-{ }
+       prepared(false),
+       buildable(false)
+{
+       struct stat st;
+       if(!Path::stat(name, st))
+       {
+               mtime=Time::TimeStamp::from_unixtime(st.st_mtime);
+               vmtime=mtime;
+       }
+}
 
 void Target::mark_rebuild(const std::string &reason)
 {
        rebuild=true;
        rebuild_reason=reason;
 }
+
+void Target::check_rebuild()
+{
+       if(!buildable)
+               return;
+
+       if(!mtime)
+               mark_rebuild("Does not exist");
+       else
+       {
+               for(list<Target *>::iterator i=depends.begin(); (i!=depends.end() && !rebuild); ++i)
+               {
+                       if((*i)->get_virtual_mtime()>mtime)
+                               mark_rebuild((*i)->get_name()+" has changed");
+                       else if((*i)->get_rebuild())
+                               mark_rebuild((*i)->get_name()+" needs rebuilding");
+               }
+       }
+}
+
+Action *Target::build(Action *action)
+{
+       building=true;
+       action->signal_done.connect(sigc::mem_fun(this, &Target::build_done));
+       return action;
+}
+
+void Target::build_done()
+{
+       building=false;
+       rebuild=false;
+}
index 21135b03970aa96a7b0641e096b4cba6b513860a..371445bc5007ebc4e7c3d4af6c525c7c79bd2231 100644 (file)
@@ -5,20 +5,9 @@
 #include <string>
 #include <msp/time/timestamp.h>
 
+class Action;
 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
 {
@@ -27,9 +16,14 @@ public:
        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 *);
+       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
+       const Msp::Time::TimeStamp &get_virtual_mtime() const { return vmtime; }
        virtual const char *get_type() const=0;
+       const std::list<Target *> &get_depends() const { return depends; }
+       void   add_depend(Target *);
+       virtual void find_depends()=0;
+       virtual void prepare();
+       virtual Action *build()=0;
        virtual ~Target() { }
 protected:
        Builder     &builder;
@@ -39,12 +33,17 @@ protected:
        bool        rebuild;
        std::string rebuild_reason;
        Msp::Time::TimeStamp mtime;
+       Msp::Time::TimeStamp vmtime;
        std::list<Target *> depends;
        std::list<Target *> rdepends;
-       bool        ready_for_build;
+       bool        prepared;
+       bool        buildable;
 
        Target(Builder &, const Package *, const std::string &);
        void mark_rebuild(const std::string &);
+       virtual void check_rebuild();
+       Action *build(Action *);
+       virtual void build_done();
 };
 
 #endif
diff --git a/source/virtualtarget.cpp b/source/virtualtarget.cpp
new file mode 100644 (file)
index 0000000..5c1e4c7
--- /dev/null
@@ -0,0 +1,10 @@
+#include "virtualtarget.h"
+
+using namespace std;
+
+void VirtualTarget::check_rebuild()
+{
+       for(list<Target *>::iterator i=depends.begin(); (i!=depends.end() && !rebuild); ++i)
+               if((*i)->get_rebuild())
+                       mark_rebuild((*i)->get_name()+" needs rebuilding");
+}
index e388061027a20fa9cfe4bae3d6b8f3aede19fec9..83ac8f93ceca649fbffba945e01327b662465736 100644 (file)
@@ -9,6 +9,9 @@ public:
        VirtualTarget(Builder &b, const std::string &n): Target(b,0,n) { }
        const char *get_type() const { return "VirtualTarget"; }
        void find_depends() { }
+       Action *build() { rebuild=false; return 0; }
+private:
+       void check_rebuild();
 };
 
 #endif