]> git.tdb.fi Git - builder.git/commitdiff
Add command line options (not all of them work yet)
authorMikko Rasa <tdb@tdb.fi>
Thu, 31 Aug 2006 14:03:51 +0000 (14:03 +0000)
committerMikko Rasa <tdb@tdb.fi>
Thu, 31 Aug 2006 14:03:51 +0000 (14:03 +0000)
Make object files depend on all included headers
Support multiple source directories per component
Support installing files
Make executables depend on all required libraries
Add analyzer
Enhance and clean up normal output
Support parallel builds
Add component type headers
Add bootstrap.sh for initial build
Add Readme.txt

27 files changed:
Build
Readme.txt [new file with mode: 0644]
bootstrap.sh [new file with mode: 0755]
source/analyzer.cpp [new file with mode: 0644]
source/analyzer.h [new file with mode: 0644]
source/builder.cpp
source/builder.h
source/component.cpp
source/component.h
source/copy.cpp [new file with mode: 0644]
source/copy.h
source/executable.cpp
source/executable.h
source/install.cpp [new file with mode: 0644]
source/install.h [new file with mode: 0644]
source/link.cpp
source/misc.h
source/objectfile.cpp
source/objectfile.h
source/package.cpp
source/package.h
source/sourcefile.cpp
source/sourcefile.h
source/systemlibrary.h [new file with mode: 0644]
source/target.cpp
source/target.h
source/virtualtarget.cpp

diff --git a/Build b/Build
index 16e9982c9117b7898d3b37947b68b727234cd0e2..8c59e73272cf69ef1dcf2556fde836d9d2eac352 100644 (file)
--- a/Build
+++ b/Build
@@ -7,6 +7,7 @@ package "builder"
        require "mspparser";
        require "msppath";
        require "mspregex++";
+       require "mspgetopt++";
        require "sigc++-2.0";
 
        program "builder"
diff --git a/Readme.txt b/Readme.txt
new file mode 100644 (file)
index 0000000..c0004ad
--- /dev/null
@@ -0,0 +1,27 @@
+Builder
+Copyright © 2006  Mikko Rasa, Mikkosoft Productions
+Version 0.1
+
+
+Builder is a program for building other programs, similar to make, scons and
+others.  It is specifically designed to suit my needs, with the goal of
+minimizing the amount of per-package configuration.
+
+
+* Building Builder
+
+Builder is normally built using itself.  However, if you just downloaded the
+source and don't yet have a Builder binary, how is that possible?  To resolve
+this problem, there's a script called bootstrap.sh in the Builder main
+directory.  Before running it, make sure you have the following libraries
+available:
+
+MSP libraries: misc core parser path regex++ getopt++
+
+Others: sigc++-2.0
+
+Since the MSP libraries are also normally built with Builder, the script will
+need to have their sources available.  By default, it will look at ../libs.
+You can change this by setting the LIBPATH evironment variable for the script.
+If everything goes well, you should have a builder-stage1 binary that you can
+use to build a normal version of Builder.
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755 (executable)
index 0000000..2f74909
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+REQUIRED="misc core parser path regex++ getopt++"
+CFLAGS="-Iinclude `pkg-config --cflags sigc++-2.0`"
+LIBS="`pkg-config --libs sigc++-2.0` -lpthread"
+if [ -z "$LIBPATH" ]; then
+       LIBPATH=`pwd`/../libs
+fi
+
+mkdir -p include/msp
+
+sources=source/*.cpp
+
+for i in $REQUIRED; do
+       path=$LIBPATH/$i
+       if [ ! -e $path ]; then
+               echo $i missing
+               exit 1
+       fi
+       if [ $i == "core" ]; then
+               ln -sf $path/source/core include/msp/core
+               ln -sf $path/source/time include/msp/time
+       elif [ $i == "misc" ]; then
+               ln -sf $path/source/*.h include/msp
+       else
+               ln -sf $path/source include/msp/$i
+       fi
+       sources="$sources `find $path/source -name '*.cpp'`"
+done
+
+g++ $sources -o builder-stage1 $CFLAGS $LIBS
diff --git a/source/analyzer.cpp b/source/analyzer.cpp
new file mode 100644 (file)
index 0000000..a4780fd
--- /dev/null
@@ -0,0 +1,110 @@
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <msp/path/path.h>
+#include "analyzer.h"
+#include "builder.h"
+#include "install.h"
+#include "objectfile.h"
+#include "package.h"
+#include "target.h"
+
+using namespace std;
+using namespace Msp;
+
+Analyzer::Analyzer(Builder &b):
+       builder(b),
+       max_depth(0),
+       full_paths(false)
+{ }
+
+void Analyzer::analyze()
+{
+       TableRow row;
+       row.push_back("Name");
+       row.push_back("Package");
+       row.push_back("Type");
+       row.push_back("Rebuild");
+       table.push_back(row);
+       
+       build_depend_table(*builder.get_target("cmdline"), 0);
+
+       print_table();
+}
+
+void Analyzer::build_depend_table(Target &tgt, unsigned depth)
+{
+       if(mode!=REBUILD)
+       {
+               if(dynamic_cast<ObjectFile *>(&tgt))
+                       return build_depend_table(*tgt.get_depends().front(), depth);
+               else if(dynamic_cast<Install *>(&tgt))
+                       return build_depend_table(*tgt.get_depends().front(), depth);
+       }
+       else if(!tgt.get_rebuild())
+               return;
+       
+       TableRow row;
+
+       string fn;
+       if(full_paths)
+               fn=tgt.get_name();
+       else
+               fn=Path::Path(tgt.get_name())[-1];
+       row.push_back(string(depth*2, ' ')+fn);
+
+       const Package *pkg=tgt.get_package();
+       if(pkg)
+               row.push_back(pkg->get_name());
+       else
+               row.push_back("");
+       
+       row.push_back(tgt.get_type());
+       
+       if(tgt.get_rebuild())
+       {
+               if(tgt.get_rebuild_reason().empty())
+                       row.push_back("Yes (no reason)");
+               else
+                       row.push_back(tgt.get_rebuild_reason());
+       }
+
+       table.push_back(row);
+
+       if(!max_depth || depth<max_depth-1)
+       {
+               const list<Target *> &depends=tgt.get_depends();
+               //depends.sort(target_order);
+               for(list<Target *>::const_iterator i=depends.begin(); i!=depends.end(); ++i)
+                       build_depend_table(**i, depth+1);
+       }
+}
+
+void Analyzer::print_table() const
+{
+       vector<unsigned> col_width;
+
+       for(Table::const_iterator i=table.begin(); i!=table.end(); ++i)
+       {
+               if(col_width.size()<i->size())
+                       col_width.resize(i->size(), 0);
+               for(unsigned j=0; j<i->size(); ++j)
+                       col_width[j]=max(col_width[j], (*i)[j].size());
+       }
+
+       for(Table::const_iterator i=table.begin(); i!=table.end(); ++i)
+       {
+               ostringstream ss;
+               ss<<left;
+               for(unsigned j=0; j<i->size(); ++j)
+               {
+                       if(j>0)
+                               ss<<"  ";
+                       ss<<setw(col_width[j])<<(*i)[j];
+               }
+               cout<<ss.str()<<'\n';
+       }
+}
+
+bool Analyzer::target_order(Target *t1, Target *t2)
+{ return t1->get_name()<t2->get_name(); }
diff --git a/source/analyzer.h b/source/analyzer.h
new file mode 100644 (file)
index 0000000..6c7b9dc
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef ANALYZER_H_
+#define ANALYZER_H_
+
+#include <list>
+#include <string>
+#include <vector>
+
+class Builder;
+class Target;
+
+class Analyzer
+{
+public:
+       enum Mode
+       {
+               DEPS,
+               ALLDEPS,
+               REBUILD,
+               RDEPS
+       };
+
+       Analyzer(Builder &);
+       void set_mode(Mode m)          { mode=m; }
+       void set_max_depth(unsigned m) { max_depth=m; }
+       void set_full_paths(bool f)    { full_paths=f; }
+       void analyze();
+private:
+       typedef std::vector<std::string> TableRow;
+       typedef std::list<TableRow> Table;
+
+       Builder  &builder;
+       Mode     mode;
+       Table    table;
+       unsigned max_depth;
+       bool     full_paths;
+
+       void build_depend_table(Target &, unsigned);
+       void print_table() const;
+
+       static bool target_order(Target *, Target *);
+};
+
+#endif
index c900837a2d6e978079294606ccbf4f182ae520e8..cb8ddf610fe7b0237fb5a9f078ebcd122144e3ee 100644 (file)
@@ -1,15 +1,23 @@
 #include <fstream>
+#include <msp/progress.h>
+#include <msp/strconv.h>
 #include <msp/strutils.h>
+#include <msp/core/error.h>
+#include <msp/getopt++/getopt++.h>
 #include <msp/parser/parser.h>
 #include <msp/path/utils.h>
 #include <msp/time/units.h>
+#include <msp/time/utils.h>
 #include "action.h"
+#include "analyzer.h"
 #include "builder.h"
 #include "executable.h"
 #include "header.h"
+#include "install.h"
 #include "misc.h"
 #include "objectfile.h"
 #include "package.h"
+#include "systemlibrary.h"
 #include "virtualtarget.h"
 
 using namespace std;
@@ -17,9 +25,60 @@ using namespace Msp;
 
 Builder::Builder(int argc, char **argv):
        verbose(1),
-       cwd(Path::getcwd())
+       cwd(Path::getcwd()),
+       analyzer(0),
+       jobs(1),
+       chrome(false)
 {
-       for(int i=1; i<argc; ++i)
+       GetOpt getopt;
+       getopt.add_option(GetOpt::Option('v', "verbose", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option('a', "analyze", GetOpt::REQUIRED));
+       getopt.add_option(GetOpt::Option('b', "build", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option("max-depth", GetOpt::REQUIRED));
+       getopt.add_option(GetOpt::Option('n', "dry-run", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option('W', "what-if", GetOpt::REQUIRED));
+       getopt.add_option(GetOpt::Option('B', "build-all", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option('C', "chdir", GetOpt::REQUIRED));
+       getopt.add_option(GetOpt::Option('j', "jobs", GetOpt::REQUIRED));
+       getopt.add_option(GetOpt::Option('h', "help", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option('c', "clean", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option("chrome", GetOpt::NONE));
+       getopt.add_option(GetOpt::Option("full-paths", GetOpt::NONE));
+       int index=getopt(argc, argv);
+
+       verbose+=getopt['v'].count();
+
+       if(getopt['a'])
+       {
+               analyzer=new Analyzer(*this);
+
+               string mode=getopt['a'].arg();
+               if(mode=="deps")
+                       analyzer->set_mode(Analyzer::DEPS);
+               else if(mode=="alldeps")
+                       analyzer->set_mode(Analyzer::ALLDEPS);
+               else if(mode=="rebuild")
+                       analyzer->set_mode(Analyzer::REBUILD);
+               else if(mode=="rdeps")
+                       analyzer->set_mode(Analyzer::RDEPS);
+               else
+                       throw UsageError("Invalid analysis mode");
+
+               if(getopt["max-depth"])
+                       analyzer->set_max_depth(strtol(getopt["max-depth"].arg()));
+               analyzer->set_full_paths(getopt["full-paths"]);
+       }
+
+       if(getopt['j'])
+               jobs=max(strtol(getopt['j'].arg()), 1L);
+
+       if(getopt["chrome"])
+               chrome=true;
+
+       if(getopt['C'])
+               chdir(getopt['C'].arg().c_str());
+
+       for(int i=index; i<argc; ++i)
        {
                string v(argv[i]);
                unsigned equal=v.find('=');
@@ -33,6 +92,13 @@ Builder::Builder(int argc, char **argv):
                cmdline_targets.push_back("default");
 }
 
+/**
+Gets a package with the specified name, possibly creating it.
+
+@param   n  Package name
+
+@return  Pointer to the package, or 0 if the package could not be located
+*/
 Package *Builder::get_package(const string &n)
 {
        PackageMap::iterator i=packages.find(n);
@@ -45,23 +111,23 @@ Package *Builder::get_package(const string &n)
        argv.push_back(n);
        string srcdir=strip(run_command(argv));
        
-       list<Path::Path> dirs;
+       PathList dirs;
        if(!srcdir.empty())
                dirs.push_back(srcdir);
 
        string dirname=n;
-       if(dirname.compare(0, 3, "msp"))
+       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)
+       for(PathList::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;
+                       break;
                }
        
        Package *pkg=Package::create(*this, n);
@@ -81,6 +147,7 @@ Target *Builder::get_target(const string &n)
 
 Target *Builder::get_header(const string &include, const string &from, const list<string> &path)
 {
+       //XXX Should really hash the include path here
        string id=from+":"+include;
        TargetMap::iterator i=includes.find(id);
        if(i!=includes.end())
@@ -101,9 +168,33 @@ Target *Builder::get_header(const string &include, const string &from, const lis
        return 0;
 }
 
+Target *Builder::get_library(const string &lib, const list<string> &path)
+{
+       string hash(8, 0);
+       for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
+               for(unsigned j=0; j<i->size(); ++j)
+                       hash[j%8]^=(*i)[j];
+
+       string basename="lib"+lib+".so";
+       for(list<string>::const_iterator i=path.begin(); i!=path.end(); ++i)
+       {
+               string full=(Path::Path(*i)/basename).str();
+               Target *tgt=get_target(full);
+               if(tgt) return tgt;
+               
+               if(Path::exists(full))
+               {
+                       add_target(tgt=new SystemLibrary(*this, full));
+                       return tgt;
+               }
+       }
+
+       return 0;
+}
+
 int Builder::main()
 {
-       if(load_build_file("Build"))
+       if(load_build_file(cwd/"Build"))
        {
                cerr<<"No build info here.\n";
                return 1;
@@ -137,32 +228,58 @@ int Builder::main()
                for(list<string>::iterator i=missing.begin(); i!=missing.end(); ++i)
                        cerr<<"  "<<*i<<'\n';
                cerr<<"Please install them and try again.\n";
+               return 1;
        }
 
        default_pkg->create_build_info();
 
-       cout<<"Active packages:";
+       /*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';
+       cout<<'\n';*/
        
        if(create_targets())
                return 1;
 
-       /*for(TargetMap::iterator i=targets.begin(); i!=targets.end(); ++i)
-               cout<<i->second->get_name()<<' '<<i->second->get_type()<<' '<<i->second->get_rebuild()<<' '<<i->second->get_rebuild_reason()<<'\n';*/
+       cout<<packages.size()<<" packages, "<<targets.size()<<" targets\n";
+       if(verbose>=2)
+       {
+               for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
+               {
+                       cout<<' '<<i->second->get_name();
+                       if(i->second->get_buildable())
+                               cout<<'*';
+                       unsigned count=0;
+                       for(TargetMap::iterator j=targets.begin(); j!=targets.end(); ++j)
+                               if(j->second->get_package()==i->second)
+                                       ++count;
+                       cout<<" ("<<count<<" targets)\n";
+               }
+       }
 
-       cout<<"Active targets: "<<targets.size()<<'\n';
+       //cout<<"Active targets: "<<targets.size()<<'\n';
+
+       if(analyzer)
+               analyzer->analyze();
 
        build();
 
        return exit_code;
 }
 
+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;
+}
+
 int Builder::load_build_file(const Path::Path &fn)
 {
        ifstream in(fn.str().c_str());
@@ -170,7 +287,7 @@ int Builder::load_build_file(const Path::Path &fn)
                return -1;
 
        Parser::Parser parser(in, fn.str());
-       Loader loader(*this, cwd/fn.subpath(0, fn.size()-1));
+       Loader loader(*this, fn.subpath(0, fn.size()-1));
        loader.load(parser);
 
        return 0;
@@ -185,41 +302,92 @@ int Builder::create_targets()
        add_target(def_tgt);
        world->add_depend(def_tgt);
 
+       Target *install=new VirtualTarget(*this, "install");
+       add_target(install);
+       world->add_depend(install);
+
        for(PackageMap::iterator i=packages.begin(); i!=packages.end(); ++i)
        {
+               if(!i->second)
+                       continue;
                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();
-                       list<string> files=list_files(base);
+                       PathList files;
+                       const PathList &sources=j->get_sources();
+                       for(PathList::const_iterator k=sources.begin(); k!=sources.end(); ++k)
+                       {
+                               list<string> sfiles=list_files(*k);
+                               for(list<string>::iterator l=sfiles.begin(); l!=sfiles.end(); ++l)
+                                       files.push_back(*k / *l);
+                       }
+
+                       Path::Path inst_base=i->second->get_config().get_option("prefix").value;
 
+                       bool build_exe=j->get_type()!=Component::HEADERS;
+                       
                        list<ObjectFile *> objs;
-                       for(list<string>::iterator k=files.begin(); k!=files.end(); ++k)
+                       for(PathList::iterator k=files.begin(); k!=files.end(); ++k)
                        {
-                               Path::Path fn=base/ *k;
-                               string ext=Path::splitext(*k).ext;
+                               string basename=(*k)[-1];
+                               string ext=Path::splitext(basename).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);
+                                       if(build_exe)
+                                       {
+                                               SourceFile *src=new SourceFile(*this, &*j, k->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()));
+                               {
+                                       Target *hdr=get_target(k->str());
+                                       if(!hdr)
+                                       {
+                                               hdr=new Header(*this, &*j, k->str());
+                                               add_target(hdr);
+                                       }
+                                       if(!j->get_install_headers().empty())
+                                       {
+                                               Path::Path inst_path=inst_base/"include"/j->get_install_headers()/basename;
+                                               Install *inst=new Install(*this, *i->second, *hdr, inst_path.str());
+                                               add_target(inst);
+                                               install->add_depend(inst);
+                                       }
+                               }
                        }
 
-                       Executable *exe=new Executable(*this, *j, objs);
-                       add_target(exe);
-                       if(i->second==default_pkg)
-                               def_tgt->add_depend(exe);
-                       else
-                               world->add_depend(exe);
+                       if(build_exe)
+                       {
+                               Executable *exe=new Executable(*this, *j, objs);
+                               add_target(exe);
+                               if(i->second==default_pkg)
+                                       def_tgt->add_depend(exe);
+                               else
+                                       world->add_depend(exe);
+
+                               if(j->get_install())
+                               {
+                                       string inst_dir;
+                                       if(j->get_type()==Component::PROGRAM)
+                                               inst_dir="bin";
+                                       else if(j->get_type()==Component::LIBRARY)
+                                               inst_dir="lib";
+                                       if(!inst_dir.empty())
+                                       {
+                                               Install *inst=new Install(*this, *i->second, *exe, (inst_base/inst_dir/Path::basename(exe->get_name())).str());
+                                               add_target(inst);
+                                               install->add_depend(inst);
+                                       }
+                               }
+                       }
                }
        }
 
@@ -241,7 +409,7 @@ int Builder::create_targets()
                if(!tgt)
                {
                        cerr<<"I don't know anything about "<<*i<<'\n';
-                       return 1;
+                       return -1;
                }
                cmdline->add_depend(tgt);
        }
@@ -273,44 +441,81 @@ void Builder::add_target(Target *t)
 int Builder::build()
 {
        Target *cmdline=get_target("cmdline");
-       list<Action *> actions;
-       bool fail=false;
 
-       if(!cmdline->get_rebuild())
+       unsigned total=cmdline->count_rebuild();
+       if(!total)
+       {
                cout<<"Already up to date\n";
+               return 0;
+       }
+       cout<<"Will build "<<total<<" target(s)\n";
+
+       vector<Action *> actions;
+
+       //ProgressBar  *progress=0;
+       if(chrome)
+       {
+               //progress=new ProgressBar(cout, total);
+               cout<<"0 targets built\n";
+       }
+       unsigned count=0;
 
-       while(cmdline->get_rebuild() && !fail)
+       bool fail=false;
+       bool finish=false;
+
+       while(!finish)
        {
-               if(actions.empty() && !fail)
+               if(actions.size()<jobs && !finish)
                {
                        Target *tgt=cmdline->get_buildable_target();
                        if(tgt)
                        {
-                               //cout<<"Build "<<tgt->get_name()<<'\n';
+                               /*if(chrome)
+                               {
+                                       cout<<"\e["<<actions.size()+1<<'A';
+                                       //progress->set(count);
+                                       cout<<"\e["<<actions.size()+1<<'B';
+                               }*/
                                Action *action=tgt->build();
                                if(action)
                                        actions.push_back(action);
                        }
+                       else if(actions.empty())
+                               finish=true;
                }
                else
-                       sleep(10*Time::msec);
+                       Time::sleep(10*Time::msec);
 
-               for(list<Action *>::iterator i=actions.begin(); i!=actions.end();)
+               for(unsigned i=0; i<actions.size();)
                {
-                       int status=(*i)->check();
+                       int status=actions[i]->check();
                        if(status>=0)
                        {
-                               delete *i;
-                               i=actions.erase(i);
+                               ++count;
+                               if(chrome)
+                               {
+                                       cout<<"\e["<<actions.size()+1<<'A';
+                                       cout<<count<<" targets built\n";
+                                       if(i)
+                                               cout<<"\e["<<i<<"B";
+                                       cout<<"\e[M";
+                                       if(i<actions.size()-1)
+                                               cout<<"\e["<<actions.size()-i-1<<"B";
+                                       cout.flush();
+                               }
+                               delete actions[i];
+                               actions.erase(actions.begin()+i);
                                if(status>0)
-                                       fail=true;
+                                       finish=fail=true;
                        }
                        else
                                ++i;
                }
        }
 
-       return 0;
+       //delete progress;
+
+       return fail?-1:0;
 }
 
 Application::RegApp<Builder> Builder::reg;
@@ -328,6 +533,5 @@ void Builder::Loader::package(const string &n)
        load_sub(*pkg);
        bld.packages.insert(PackageMap::value_type(n, pkg));
        bld.new_pkgs.push_back(pkg);
-       //cout<<"loaded "<<pkg->get_name()<<'\n';
 }
 
index bdc5e2fb931e36d6df53229702197ed04447f504..34afb92e91aafccdaef1896042404d251b9da8e9 100644 (file)
@@ -9,6 +9,7 @@
 #include <msp/path/path.h>
 #include "config.h"
 
+class Analyzer;
 class Package;
 class Target;
 
@@ -20,7 +21,9 @@ public:
        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> &);
+       Target   *get_library(const std::string &, const std::list<std::string> &);
        int      main();
+       ~Builder();
 private:
        class Loader: public Msp::Parser::Loader
        {
@@ -39,15 +42,23 @@ private:
        
        std::list<std::string> cmdline_targets;
        RawOptionMap cmdline_options;
-       TargetMap  targets;
-       PackageMap packages;
-       unsigned   verbose;
-       Package    *default_pkg;
-       Msp::Path::Path cwd;
+       
+       PackageMap  packages;
        std::list<Package *> new_pkgs;
-       TargetMap  includes;
+       Package     *default_pkg;
+
+       TargetMap   targets;
        std::list<Target *> new_tgts;
-       ToolMap    tools;
+       TargetMap   includes;
+       TargetMap   libraries;
+
+       ToolMap     tools;
+       unsigned    verbose;
+       Msp::Path::Path cwd;
+       Analyzer    *analyzer;
+       unsigned    jobs;
+       std::list<std::string> what_if;
+       bool        chrome;
 
        int load_build_file(const Msp::Path::Path &);
        int create_targets();
index a94a5663563d677e5a14f9fde795599ff48de93d..38f4d6b4279ccfc897ef6392565c4670857c51d7 100644 (file)
@@ -25,5 +25,5 @@ Component::Loader::Loader(Component &c):
 
 void Component::Loader::source(const string &s)
 {
-       comp.source=comp.pkg.get_source()/s;
+       comp.sources.push_back(comp.pkg.get_source()/s);
 }
index a27ddcab28a2ebaa4203b17726a3e702f03d4a97..af2fc2fae380a3accd0294c25239b268c6c67c1c 100644 (file)
@@ -5,6 +5,7 @@
 #include <msp/parser/loader.h>
 #include <msp/path/path.h>
 #include "buildinfo.h"
+#include "misc.h"
 
 class Package;
 
@@ -34,7 +35,7 @@ public:
        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 PathList    &get_sources() const         { return sources; }
        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; }
@@ -43,7 +44,7 @@ protected:
        Package     &pkg;
        Type        type;
        std::string name;
-       Msp::Path::Path source;
+       PathList    sources;
        bool        install;
        std::string install_headers;
        BuildInfo   build_info;
diff --git a/source/copy.cpp b/source/copy.cpp
new file mode 100644 (file)
index 0000000..23cd6f1
--- /dev/null
@@ -0,0 +1,48 @@
+#include <fstream>
+#include <msp/path/utils.h>
+#include "copy.h"
+#include "package.h"
+
+using namespace std;
+using namespace Msp;
+
+Copy::Copy(Builder &b, const Package &pkg, const Path::Path &s, const Path::Path &d):
+       Action(b),
+       src(s),
+       dest(d),
+       worker(*this)
+{
+       announce(pkg.get_name(), "INST", dest[-1]);
+}
+
+int Copy::check()
+{
+       if(worker.get_done())
+       {
+               signal_done.emit();
+               worker.join();
+               return 0;
+       }
+       return -1;
+}
+
+void Copy::Worker::main()
+{
+       Path::mkpath(copy.src.subpath(0, copy.src.size()-1), 0755);
+       unlink(copy.dest.str().c_str());
+       ifstream in(copy.src.str().c_str());
+       ofstream out(copy.dest.str().c_str());
+
+       char buf[16384];
+       while(!in.eof())
+       {
+               in.read(buf, sizeof(buf));
+               out.write(buf, in.gcount());
+       }
+
+       struct stat st;
+       Path::stat(copy.src, st);
+       chmod(copy.dest.str().c_str(), st.st_mode&0777);
+
+       done=true;
+}
index e39be16baedeb4793634533030f441761df8019a..a6d5c51673e8fa6593bd8fddb0a0d79f608099e6 100644 (file)
@@ -5,10 +5,12 @@
 #include <msp/path/path.h>
 #include "action.h"
 
+class Package;
+
 class Copy: public Action
 {
 public:
-       Copy(Builder &, const Msp::Path::Path &, const Msp::Path::Path &);
+       Copy(Builder &, const Package &, const Msp::Path::Path &, const Msp::Path::Path &);
        int check();
 private:
        class Worker: public Msp::Thread
index 156823340a533849346e9425a54d0d59eb35e0ca..4e7b1f4382c3b7224661bd72f83e0ec5e2c65e42 100644 (file)
@@ -1,3 +1,4 @@
+#include "builder.h"
 #include "component.h"
 #include "executable.h"
 #include "link.h"
@@ -15,6 +16,17 @@ Executable::Executable(Builder &b, const Component &c, const list<ObjectFile *>
                add_depend(*i);
 }
 
+void Executable::find_depends()
+{
+       const list<string> &libs=comp.get_build_info().libs;
+       for(list<string>::const_iterator i=libs.begin(); i!=libs.end(); ++i)
+       {
+               Target *lib=builder.get_library(*i, comp.get_build_info().libpath);
+               if(lib)
+                       add_depend(lib);
+       }
+}
+
 Action *Executable::build()
 {
        return Target::build(new Link(builder, *this, comp));;
index 52b0ceedd2cf4adb62d78df66b2c18e920bc0c16..f118b8762b0ab568cc2ad8ae5e88a1299a754c5d 100644 (file)
@@ -11,7 +11,7 @@ class Executable: public Target
 public:
        Executable(Builder &, const Component &, const std::list<ObjectFile *> &);
        const char *get_type() const { return "Executable"; }
-       void find_depends() { }
+       void find_depends();
        Action *build();
 private:
        const Component &comp;
diff --git a/source/install.cpp b/source/install.cpp
new file mode 100644 (file)
index 0000000..1ade8b2
--- /dev/null
@@ -0,0 +1,33 @@
+#include "builder.h"
+#include "copy.h"
+#include "header.h"
+#include "install.h"
+#include "package.h"
+
+using namespace std;
+
+Install::Install(Builder &b, const Package &p, Target &tgt, const string &n):
+       Target(b, &p, n)
+{
+       buildable=true;
+       add_depend(&tgt);
+}
+
+void Install::check_rebuild()
+{
+       if(!mtime)
+               mark_rebuild("Does not exist");
+       else
+       {
+               Target *dep=depends.front();
+               if(dep->get_mtime()>mtime)
+                       mark_rebuild(dep->get_name()+" has changed");
+               else if(dep->get_rebuild())
+                       mark_rebuild(dep->get_name()+" needs rebuilding");
+       }
+}
+
+Action *Install::build()
+{
+       return Target::build(new Copy(builder, *package, depends.front()->get_name(), name));
+}
diff --git a/source/install.h b/source/install.h
new file mode 100644 (file)
index 0000000..b81ac9b
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef INSTALL_H_
+#define INSTALL_H_
+
+#include "target.h"
+
+class Install: public Target
+{
+public:
+       Install(Builder &, const Package &, Target &, const std::string &);
+       const char *get_type() const { return "Install"; }
+       void find_depends() { }
+       void check_rebuild();
+       Action *build();
+private:
+};
+
+#endif
index 242c966dfab7ab58acc77054e1ae0478e7f70ed8..e2c0f1222a83cb9d7baea296f8fcda4da17c0130 100644 (file)
@@ -12,6 +12,9 @@ Link::Link(Builder &b, const Executable &exe, const Component &comp):
 {
        argv.push_back("g++");
        
+       if(comp.get_type()==Component::LIBRARY)
+               argv.push_back("-shared");
+
        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);
index ae2ff69437354231395ce3e3a92f79b4b3d21838..a35c0ed3093cf371baf8e6f43b18630c7a16b697 100644 (file)
@@ -3,6 +3,9 @@
 
 #include <list>
 #include <string>
+#include <msp/path/path.h>
+
+typedef std::list<Msp::Path::Path> PathList;
 
 std::string run_command(const std::list<std::string> &);
 
index d29cd11df850759833705e0103264c50d09de512..3e531f7ae8a09d2b8fb3def6265ee5e5659cc552 100644 (file)
@@ -2,6 +2,7 @@
 #include "builder.h"
 #include "compile.h"
 #include "component.h"
+#include "install.h"
 #include "objectfile.h"
 #include "package.h"
 #include "sourcefile.h"
@@ -17,11 +18,40 @@ ObjectFile::ObjectFile(Builder &b, const Component &c, SourceFile &src):
        add_depend(&src);
 }
 
+void ObjectFile::find_depends()
+{
+       find_depends(depends.front());
+}
+
 Action *ObjectFile::build()
 {
        return Target::build(new Compile(builder, depends.front()->get_name(), name, comp));
 }
 
+void ObjectFile::find_depends(Target *tgt)
+{
+       SourceFile *src=dynamic_cast<SourceFile *>(tgt);
+       if(!src)
+       {
+               Install *inst=dynamic_cast<Install *>(tgt);
+               if(inst)
+                       src=dynamic_cast<SourceFile *>(inst->get_depends().front());
+       }
+       if(!src)
+               return;
+
+       const string &sname=src->get_name();
+       string path=sname.substr(0, sname.rfind('/'));
+
+       const list<string> &includes=src->get_includes();
+       for(list<string>::const_iterator i=includes.begin(); i!=includes.end(); ++i)
+       {
+               Target *hdr2=builder.get_header(*i, path, package->get_build_info().incpath);
+               if(hdr2)
+                       add_depend(hdr2);
+       }
+}
+
 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 9fa49794610bcd2ba53b2ffa5d87e2718becc2de..962151c5e412f1923d873ec23504851f5ddd0d4e 100644 (file)
@@ -11,11 +11,13 @@ class ObjectFile: public Target
 public:
        ObjectFile(Builder &, const Component &, SourceFile &);
        const char *get_type() const { return "ObjectFile"; }
-       void find_depends() { }
+       void find_depends();
        Action *build();
 private:
        const Component &comp;
        
+       void find_depends(Target *);
+
        static std::string generate_target_name(const Component &, const std::string &);
 };
 
index e6e3b5099c1814a46ab02586e750f936843e3c27..3072fd6a65ccd1d6a6303d54b80ffdb789ca35da 100644 (file)
@@ -114,13 +114,21 @@ Package *Package::create(Builder &b, const string &name)
 {
        list<string> argv;
        argv.push_back("pkg-config");
+       argv.push_back("--silence-errors");
        argv.push_back("--cflags");
        argv.push_back("--libs");
        argv.push_back(name);
        vector<string> info=split(run_command(argv));
        
        if(info.empty())
-               return 0;
+       {
+               if(name=="opengl")
+                       info.push_back("-lGL");
+               else if(name=="pthread")
+                       info.push_back("-lpthread");
+               else
+                       return 0;
+       }
        
        Package *pkg=new Package(b, name, info);
        return pkg;
@@ -177,6 +185,7 @@ Package::Loader::Loader(Package &p):
        add("require",     &Loader::require);
        add("program",     &Loader::program);
        add("library",     &Loader::library);
+       add("headers",     &Loader::headers);
 }
 
 Package::Loader::~Loader()
@@ -189,16 +198,23 @@ void Package::Loader::require(const string &n)
        pkg.requires.push_back(PackageRef(pkg.builder, n));
 }
 
-void Package::Loader::program(const std::string &n)
+void Package::Loader::program(const string &n)
 {
        Component prog(pkg, Component::PROGRAM, n);
        load_sub(prog);
        pkg.components.push_back(prog);
 }
 
-void Package::Loader::library(const std::string &n)
+void Package::Loader::library(const string &n)
 {
        Component prog(pkg, Component::LIBRARY, n);
        load_sub(prog);
        pkg.components.push_back(prog);
 }
+
+void Package::Loader::headers(const string &n)
+{
+       Component prog(pkg, Component::HEADERS, n);
+       load_sub(prog);
+       pkg.components.push_back(prog);
+}
index aafec0fc54cd2c58d0b63ef2f4758ab647f8ccdf..06a68ba65ca78656d9c91ebed35c2cb8fe81c8d7 100644 (file)
@@ -39,6 +39,7 @@ public:
                void require(const std::string &);
                void program(const std::string &);
                void library(const std::string &);
+               void headers(const std::string &);
        };
 
        Package(Builder &, const std::string &, const Msp::Path::Path &);
index 42142536607b9a4540929703ae14383538823ea8..ea76efd28a6fd6435e6d9c2a4f74c50a40c83a31 100644 (file)
@@ -7,6 +7,8 @@
 using namespace std;
 using namespace Msp;
 
+#include <iostream>
+
 SourceFile::SourceFile(Builder &b, const Component *c, const string &n):
        Target(b, c?&c->get_package():0, n),
        comp(c)
@@ -32,9 +34,3 @@ 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 af25fe9253428924968f85654746a1ac31edc219..b1ec8294f578d796ab85f61cb83ea804ebe50b55 100644 (file)
@@ -9,14 +9,13 @@ class SourceFile: public Target
 {
 public:
        SourceFile(Builder &, const Component *, const std::string &);
+       const std::list<std::string> &get_includes() const { return includes; }
        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
diff --git a/source/systemlibrary.h b/source/systemlibrary.h
new file mode 100644 (file)
index 0000000..9751270
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef SYSTEMLIBRARY_H_
+#define SYSTEMLIBRARY_H_
+
+#include "target.h"
+
+class SystemLibrary: public Target
+{
+public:
+       SystemLibrary(Builder &b, const std::string &n): Target(b,0,n) { }
+       const char *get_type() const { return "SystemLibrary"; }
+       void find_depends() { }
+       Action *build() { return 0; }
+};
+
+#endif
index b3dc50904717450c938f65f47add7b490073a317..d4c243120bbeb693f54abd6ff9674543f640b84b 100644 (file)
@@ -1,4 +1,5 @@
 #include <msp/path/utils.h>
+#include <msp/time/utils.h>
 #include "action.h"
 #include "builder.h"
 #include "package.h"
@@ -42,6 +43,23 @@ void Target::prepare()
        check_rebuild();
 }
 
+unsigned Target::count_rebuild()
+{
+       if(counted)
+               return 0;
+
+       counted=true;
+       unsigned count=rebuild;
+       for(list<Target *>::iterator i=depends.begin(); i!=depends.end(); ++i)
+               count+=(*i)->count_rebuild();
+       return count;
+}
+
+void Target::touch()
+{
+       mtime=Time::now();
+}
+
 Target::Target(Builder &b, const Package *p, const string &n):
        builder(b),
        package(p),
@@ -49,14 +67,12 @@ Target::Target(Builder &b, const Package *p, const string &n):
        building(false),
        rebuild(false),
        prepared(false),
-       buildable(false)
+       buildable(false),
+       counted(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)
@@ -76,10 +92,10 @@ void Target::check_rebuild()
        {
                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");
+                       if((*i)->get_mtime()>mtime)
+                               mark_rebuild(Path::basename((*i)->get_name())+" has changed");
                        else if((*i)->get_rebuild())
-                               mark_rebuild((*i)->get_name()+" needs rebuilding");
+                               mark_rebuild(Path::basename((*i)->get_name())+" needs rebuilding");
                }
        }
        if(!rebuild && package && package->get_config().get_mtime()>mtime)
index 371445bc5007ebc4e7c3d4af6c525c7c79bd2231..f0ec0f264916c56da0e32af9b1aa6f003ab175d6 100644 (file)
@@ -12,18 +12,21 @@ class Package;
 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; }
-       const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
-       const Msp::Time::TimeStamp &get_virtual_mtime() const { return vmtime; }
+       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; }
+       const Msp::Time::TimeStamp &get_mtime() const  { return mtime; }
        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;
+       const Package     *get_package() const         { return package; }
+       void              add_depend(Target *);
+       virtual void      find_depends()=0;
+       virtual void      prepare();
+       virtual Action    *build()=0;
+       void              reset_count()                { counted=false; }
+       unsigned          count_rebuild();
+       void              touch();
        virtual ~Target() { }
 protected:
        Builder     &builder;
@@ -33,11 +36,11 @@ protected:
        bool        rebuild;
        std::string rebuild_reason;
        Msp::Time::TimeStamp mtime;
-       Msp::Time::TimeStamp vmtime;
        std::list<Target *> depends;
        std::list<Target *> rdepends;
        bool        prepared;
        bool        buildable;
+       bool        counted;
 
        Target(Builder &, const Package *, const std::string &);
        void mark_rebuild(const std::string &);
index 5c1e4c71a88d569b5392577edd7573e95e9e730e..d3f80df2c762812a5bbfe06245a6215c32c51504 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/path/utils.h>
 #include "virtualtarget.h"
 
 using namespace std;
@@ -6,5 +7,5 @@ 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");
+                       mark_rebuild(Msp::Path::basename((*i)->get_name())+" needs rebuilding");
 }