require "mspparser";
require "msppath";
require "mspregex++";
+ require "mspgetopt++";
require "sigc++-2.0";
program "builder"
--- /dev/null
+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.
--- /dev/null
+#!/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
--- /dev/null
+#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(); }
--- /dev/null
+#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
#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;
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('=');
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);
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);
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())
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;
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());
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;
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);
+ }
+ }
+ }
}
}
if(!tgt)
{
cerr<<"I don't know anything about "<<*i<<'\n';
- return 1;
+ return -1;
}
cmdline->add_depend(tgt);
}
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;
load_sub(*pkg);
bld.packages.insert(PackageMap::value_type(n, pkg));
bld.new_pkgs.push_back(pkg);
- //cout<<"loaded "<<pkg->get_name()<<'\n';
}
#include <msp/path/path.h>
#include "config.h"
+class Analyzer;
class Package;
class Target;
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
{
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();
void Component::Loader::source(const string &s)
{
- comp.source=comp.pkg.get_source()/s;
+ comp.sources.push_back(comp.pkg.get_source()/s);
}
#include <msp/parser/loader.h>
#include <msp/path/path.h>
#include "buildinfo.h"
+#include "misc.h"
class Package;
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; }
Package &pkg;
Type type;
std::string name;
- Msp::Path::Path source;
+ PathList sources;
bool install;
std::string install_headers;
BuildInfo build_info;
--- /dev/null
+#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;
+}
#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
+#include "builder.h"
#include "component.h"
#include "executable.h"
#include "link.h"
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));;
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 ∁
--- /dev/null
+#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));
+}
--- /dev/null
+#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
{
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);
#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> &);
#include "builder.h"
#include "compile.h"
#include "component.h"
+#include "install.h"
#include "objectfile.h"
#include "package.h"
#include "sourcefile.h"
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();
public:
ObjectFile(Builder &, const Component &, SourceFile &);
const char *get_type() const { return "ObjectFile"; }
- void find_depends() { }
+ void find_depends();
Action *build();
private:
const Component ∁
+ void find_depends(Target *);
+
static std::string generate_target_name(const Component &, const std::string &);
};
{
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;
add("require", &Loader::require);
add("program", &Loader::program);
add("library", &Loader::library);
+ add("headers", &Loader::headers);
}
Package::Loader::~Loader()
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);
+}
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 &);
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)
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());
-}
{
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
--- /dev/null
+#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
#include <msp/path/utils.h>
+#include <msp/time/utils.h>
#include "action.h"
#include "builder.h"
#include "package.h"
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),
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)
{
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)
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;
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 &);
+#include <msp/path/utils.h>
#include "virtualtarget.h"
using namespace std;
{
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");
}