summaryrefslogtreecommitdiffabout
path: root/lib/component_factory.cc
blob: af3d911fd1f74e1d7ddc67460a5e9dbe6cd491de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#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 <konforka/util.h>
 #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", ".pp_stamp" };
    static const char *cc_targets[] = { ".o", ".d" };

    component_factory::component_factory(configuration& c)
	: config(c),
    root_source(konforka::normalize_path(c.root_source,konforka::strip_trailing_slash)+'/'),
    root_intermediate(konforka::normalize_path(c.root_intermediate,konforka::strip_trailing_slash)+'/'),
    root_so(konforka::normalize_path(c.root_so,konforka::strip_trailing_slash)+'/') {
    }

    void component_factory::get_dependencies(const string& dst,file_list_t& deps) {
	deps.clear();
	string dp = konforka::normalize_path(dst,konforka::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+".o");
	    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) { }
	try {
	    // preprocessor targets
	    string noro = strip_prefix(dp,root_intermediate);
	    for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
		try {
		    string nos = strip_suffix(noro,pp_targets[ppt]);
		    deps.push_back(root_source+nos); // depends on the source
		    ifstream imports((root_intermediate+nos+".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+nos+".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");
			}
		    }
		    // XXX: intermediate_deps should be broken down
		    config_options *co_intermediate_deps = config.lookup_config(nos,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_suffix& uns) { }
	    }
	    // compiler targets
	    for(int cct=0;cct<sizeof(cc_targets)/sizeof(*cc_targets);cct++) {
		try {
		    string nos = strip_suffix(noro,cc_targets[cct]);
		    deps.push_back(root_intermediate+nos+".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+nos+".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(konforka::combine_path(config.root_source+nos,str));
			    }
			}
		    }
		    // XXX: intermediate_deps should be broken down
		    config_options *co_intermediate_deps = config.lookup_config(nos,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);
		    }
		}catch(utility_no_suffix& uns) { }
	    }
	}catch(utility_no_prefix& unp) { }
    }

    bool component_factory::is_uptodate(const string& dst,file_list_t *deps) {
	string dp = konforka::normalize_path(dst,konforka::strip_trailing_slash);
	try {
	    string noro = strip_prefix(dp,root_intermediate);
	    for(int ppt=0;(ppt+1)<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
		try {
		    string nos = strip_suffix(noro,pp_targets[ppt]);
		    return file_factory::is_uptodate(root_intermediate+nos+".pp_stamp",deps);
		}catch(utility_no_suffix& uns) { }
	    }
	    bool rv =  file_factory::is_uptodate(dst,deps);
	    return rv;
	}catch(utility_no_prefix& unp) { }
	// XXX: or just compare it off, instead of throwing things around.
	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 = konforka::normalize_path(dst,konforka::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 o = root_intermediate+noro+".o";
	    cerr << "Linking " << noro << endl;
	    if(access(o.c_str(),R_OK))
		throw konforka::exception(CODEPOINT,string("can't access compiled component code (")+o+")");
	    konforka::make_path(konforka::dir_name(root_so+noro),0755);
	    file_lock lock_cc(root_intermediate+noro+".o.lock");
	    file_lock lock_so(root_so+noro+".so.lock");
	    int stdO = open((root_intermediate+noro+".ld.stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
	    if(stdO<0)
		throw konforka::exception(CODEPOINT,"failed to open/create linker stdout");
	    int stdE = open((root_intermediate+noro+".ld.stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
	    if(stdE<0) {
		close(stdO);
		throw konforka::exception(CODEPOINT,"failed to open/create linker stderr");
	    }
	    list<string> args;
	    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());
	    }
	    args.push_back("-shared");
	    args.push_back(o);
	    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);
	    }
	    args.push_back("-o"); args.push_back(dp);
	    // TODO: "g++" configurable
	    int rv = execute("g++",args,stdO,stdE);
	    if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) )
		throw link_error(CODEPOINT,"failed to link component",noro);
	    return;
	}catch(utility_no_prefix& unp) {
	    throw konforka::exception(CODEPOINT,"component is outside of component root");
	}catch(utility_no_suffix& uns) { }
	try {
	    string noro = strip_prefix(dp,root_intermediate);
	    // compiler targets
	    for(int cct=0;cct<sizeof(cc_targets)/sizeof(*cc_targets);cct++) {
		try {
		    string nos = strip_suffix(noro,cc_targets[cct]);
		    string cc = root_intermediate+nos+".cc";
		    string o = root_intermediate+nos+".o";
		    cerr << "Compiling " << nos << endl;
		    if(access(cc.c_str(),R_OK))
			throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")");
		    konforka::make_path(konforka::dir_name(cc),0755);
		    string pwd = konforka::dir_name(root_source+nos);
		    auto_chdir dir_changer(pwd);
		    file_lock lock_source(root_intermediate+nos+".lock");
		    file_lock lock_cc(root_intermediate+nos+".o.lock");
		    int stdO = open((root_intermediate+nos+".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+nos+".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(nos,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());
		    }
		    // 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+nos+".d");
		    args.push_back("-c");
		    args.push_back(cc);
		    args.push_back("-o"); args.push_back(o);
		    // TODO: "g++" configurable
		    int rv = execute("g++",args,stdO,stdE);
		    if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) )
			throw compile_error(CODEPOINT,"failed to compile component",nos);
		    return;
		}catch(utility_no_suffix& uns) { }
	    }
	    // preprocessor targets
	    for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
		try {
		    string nos = strip_suffix(noro,pp_targets[ppt]);
		    string src = root_source+nos;
		    cerr << "Preprocessing " << nos << endl;
		    if(access(src.c_str(),R_OK))
			throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")");
		    konforka::make_path(konforka::dir_name(root_intermediate+nos),0755);
		    file_lock lock(root_intermediate+nos+".lock");
		    sitecing_parser parser(*this);
		    config_options *co_skeleton = config.lookup_config(nos,config_options::flag_skeleton);
		    if(co_skeleton)
			parser.skeleton = co_skeleton->skeleton;
		    static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		    parser.class_name = konforka::normalize_path(nos,konforka::strip_leading_slash|konforka::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 = root_intermediate+nos;
		    parser.component_basename = nos;
		    try {
			parser.preprocess(src);
			string sf = root_intermediate+nos+".pp_stamp";
			ofstream sfs(sf.c_str(),ios::trunc|ios::out); // touch .pp_stamp
		    }catch(preprocessor_error& pe) {
			pe.component_name = nos;
			pe.see(CODEPOINT);
			throw;
		    }
		    return;
		}catch(utility_no_suffix& uns) { }
	    }
	}catch(utility_no_prefix& unp) { }
	cerr << "ignoring build request for " << dp << endl;
    }

    void component_factory::make(const string& dst) {
	string dp = konforka::normalize_path(dst,konforka::strip_trailing_slash);
	try {
	    string noso = strip_suffix(dp,".so");
	    string noro = strip_prefix(noso,root_so);
	    file_list_t a;
	    get_ancestors(noro,a);
	    for(file_list_t::const_iterator i=a.begin();i!=a.end();++i) {
		make(root_so+*i+".so");
	    }
	}catch(utility_no_affix& una) { }
	file_factory::make(dst);
    }

    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+konforka::normalize_path(component,konforka::strip_trailing_slash|konforka::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+konforka::normalize_path(component,konforka::strip_trailing_slash|konforka::strip_leading_slash)+".ancestors";
	make(cn);
	ifstream ifs(cn.c_str());
	if(!ifs.good())
	    throw konforka::exception(CODEPOINT,string("failed to access component '")+component+"' .ancestors");
	rv.clear();
	while(!ifs.eof()) {
	    string a;
	    ifs >> a;
	    if(!a.empty())
		rv.push_back(a);
	}
    }

}