summaryrefslogtreecommitdiffabout
path: root/lib/component_factory.cc
Side-by-side diff
Diffstat (limited to 'lib/component_factory.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/component_factory.cc279
1 files changed, 279 insertions, 0 deletions
diff --git a/lib/component_factory.cc b/lib/component_factory.cc
new file mode 100644
index 0000000..bcf19f2
--- a/dev/null
+++ b/lib/component_factory.cc
@@ -0,0 +1,279 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ #include <sys/wait.h>
+ #include <fcntl.h>
+ #include <iostream>
+ #include <fstream>
+ #include <stdexcept>
+ #include <vector>
+ using namespace std;
+ #include "sitecing/component_factory.h"
+ #include "sitecing/sitecing_util.h"
+ #include "sitecing/sitecing_parser.h"
+ #include "sitecing/sitecing_exception.h"
+#endif
+
+namespace sitecing {
+
+ static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" };
+
+ component_factory::component_factory(configuration& c)
+ : config(c),
+ root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'),
+ root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'),
+ root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') {
+ }
+
+ void component_factory::get_dependencies(const string& dst,file_list_t& deps) {
+ deps.clear();
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // source documents
+ try { // XXX: or just compare it off?
+ string noro = strip_prefix(dp,root_source);
+ return;
+ }catch(utility_no_affix& una) {
+ }
+ // .so binaries
+ try {
+ string noso = strip_suffix(dp,".so");
+ string noro = strip_prefix(noso,root_so);
+ deps.push_back(root_intermediate+noro+".cc");
+ config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps);
+ if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) {
+ ifstream df((root_intermediate+noro+".d").c_str(),ios::in);
+ if(df.good()) {
+ string str;
+ while(!df.eof()) {
+ df >> str;
+ if(str.find_first_of("\\:")==string::npos)
+ deps.push_back(combine_path(config.root_source+noro,str));
+ }
+ }
+ }
+ config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps);
+ if(co_so_deps) {
+ for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i)
+ deps.push_back(*i);
+ }
+ return;
+ }catch(utility_no_prefix& unp) {
+ throw konforka::exception(CODEPOINT,"component is outside of component root");
+ }catch(utility_no_suffix& uns) {
+ }
+ // preprocessor targets
+ for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
+ try {
+ string nos = strip_suffix(dp,pp_targets[ppt]);
+ string noro = strip_prefix(nos,root_intermediate);
+ deps.push_back(root_source+noro);
+ ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in);
+ if(imports.good()) {
+ string str;
+ while(!imports.eof()) {
+ imports >> str;
+ if(!str.empty())
+ deps.push_back(root_intermediate+str+".classname");
+ }
+ }
+ ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in);
+ if(ancestors.good()) {
+ string str;
+ while(!ancestors.eof()) {
+ ancestors >> str;
+ if(!str.empty())
+ deps.push_back(root_intermediate+str+".classname");
+ }
+ }
+ config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps);
+ if(co_intermediate_deps) {
+ for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i)
+ deps.push_back(*i);
+ }
+ return;
+ }catch(utility_no_affix& una) {
+ // do nothing. must be a cpp dependency.
+ }
+ }
+ }
+
+ bool component_factory::is_uptodate(const string& dst,file_list_t *deps) {
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // XXX: or just compare it off, instead of throwing things around.
+ try {
+ strip_prefix(dp,root_intermediate);
+ return file_factory::is_uptodate(dst,deps);
+ }catch(utility_no_prefix& unp) {
+ }
+ try {
+ strip_prefix(dp,root_so);
+ return file_factory::is_uptodate(dst,deps);
+ }catch(utility_no_prefix& unp) {
+ }
+ return true;
+ }
+
+ void component_factory::build(const string& dst) {
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // sources
+ try {
+ string noro = strip_prefix(dp,root_source);
+ // building the sources is left up to developer
+ return;
+ }catch(utility_no_prefix& unp) {
+ }
+ // .so files
+ try {
+ string noso = strip_suffix(dp,".so");
+ string noro = strip_prefix(noso,root_so);
+ string cc = root_intermediate+noro+".cc";
+ if(access(cc.c_str(),R_OK))
+ throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")");
+ make_path(dir_name(root_so+noro),0755);
+ string pwd = dir_name(root_source+noro);
+ auto_chdir dir_changer(pwd);
+ file_lock lock_source(root_intermediate+noro+".lock");
+ file_lock lock_so(root_so+noro+".so.lock");
+ int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
+ if(stdO<0)
+ throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout");
+ int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
+ if(stdE<0) {
+ close(stdO);
+ throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr");
+ }
+ list<string> args;
+ config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags);
+ if(co_cpp_flags) {
+ args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end());
+ }
+ config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags);
+ if(co_ld_flags) {
+ args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end());
+ }
+ // TODO: maybe move it to separare config option like CoreCPPFLags?
+ args.push_back("-I"+root_intermediate);
+ args.push_back("-I"+root_source);
+ args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d");
+ args.push_back("-shared");
+ args.push_back("-o"); args.push_back(dp);
+ args.push_back(cc);
+ file_list_t ancestors;
+ get_ancestors(noro,ancestors);
+ for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) {
+ string aso=root_so+*i+".so";
+ make(aso);
+ args.push_back(aso);
+ }
+ // TODO: "g++" configurable
+ int rv = execute("g++",args,stdO,stdE);
+ if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) )
+ throw compile_error(CODEPOINT,"failed to compile component",noro);
+ return;
+ }catch(utility_no_prefix& unp) {
+ throw konforka::exception(CODEPOINT,"component is outside of component root");
+ }catch(utility_no_suffix& uns) {
+ }
+ // preprocessor targets
+ for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
+ try {
+ string nos = strip_suffix(dp,pp_targets[ppt]);
+ string noro = strip_prefix(nos,root_intermediate);
+ string src = root_source+noro;
+ if(access(src.c_str(),R_OK))
+ throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")");
+ make_path(dir_name(root_intermediate+noro),0755);
+ file_lock lock(root_intermediate+noro+".lock");
+ sitecing_parser parser(*this);
+ config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton);
+ if(co_skeleton)
+ parser.skeleton = co_skeleton->skeleton;
+ static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash);
+ for(string::size_type illc = parser.class_name.find_first_not_of(id_chars);illc!=string::npos;illc=parser.class_name.find_first_not_of(id_chars,illc)) {
+ string::size_type lc = parser.class_name.find_first_of(id_chars,illc);
+ int n = ((lc==string::npos)?parser.class_name.length():lc)-illc;
+ parser.class_name.replace(illc,n,n,'_');
+ }
+ parser.class_name = "_SCC_"+parser.class_name;
+ parser.output_basename = nos;
+ parser.component_basename = noro;
+ try {
+ parser.preprocess(src);
+ }catch(preprocessor_error& pe) {
+ pe.component_name = noro;
+ pe.see(CODEPOINT);
+ throw;
+ }
+ return;
+ }catch(utility_no_affix& una) {
+ // must be a crap from .d file
+ }
+ }
+ cerr << "ignoring build request for " << dp << endl;
+ }
+
+ int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) {
+ // XXX: is it right that we do stdio/stderr tricks outside of the function?
+ cerr << "executing: " << cmd;
+ vector<const char*> argv(args.size()+2);
+ argv[0]=cmd.c_str();
+ int an = 1;
+ for(list<string>::const_iterator i=args.begin();i!=args.end();i++) {
+ cerr << " " << *i ;
+ argv[an++] = i->c_str();
+ }
+ cerr << endl;
+ argv[an++]=NULL;
+ pid_t pid = vfork();
+ if(pid==-1) {
+ close(stdo); close(stde);
+ throw konforka::exception(CODEPOINT,"failed to vfork()");
+ }
+ if(!pid) {
+ // child
+ if(dup2(stdo,1)!=1)
+ _exit(-1);
+ if(dup2(stde,2)!=2)
+ _exit(-1);
+ close(0);
+ execvp(cmd.c_str(),(char**)&argv.front());
+ _exit(-1);
+ }
+ // parent
+ close(stdo); close(stde);
+ int rv;
+ if(waitpid(pid,&rv,0)<0)
+ throw konforka::exception(CODEPOINT,"failed to waitpid()");
+ return rv;
+ }
+
+ string component_factory::get_classname(const string& component) {
+ string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname";
+ make(cn);
+ ifstream ifs(cn.c_str());
+ if(!ifs.good())
+ throw konforka::exception(CODEPOINT,"failed to access component .classname");
+ ifs >> cn;
+ return cn;
+ }
+
+ void component_factory::get_ancestors(const string& component,file_list_t& rv) {
+ string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors";
+ make(cn);
+ ifstream ifs(cn.c_str());
+ if(!ifs.good())
+ throw konforka::exception(CODEPOINT,"filed to access component .ancestors");
+ rv.clear();
+ while(!ifs.eof()) {
+ string a;
+ ifs >> a;
+ if(!a.empty())
+ rv.push_back(a);
+ }
+ }
+
+}