-rw-r--r-- | lib/component_factory.cc | 279 |
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 @@ | |||
1 | #ifdef USE_PCH | ||
2 | #include "pch.h" | ||
3 | #else | ||
4 | #include <sys/types.h> | ||
5 | #include <sys/stat.h> | ||
6 | #include <unistd.h> | ||
7 | #include <sys/wait.h> | ||
8 | #include <fcntl.h> | ||
9 | #include <iostream> | ||
10 | #include <fstream> | ||
11 | #include <stdexcept> | ||
12 | #include <vector> | ||
13 | using namespace std; | ||
14 | #include "sitecing/component_factory.h" | ||
15 | #include "sitecing/sitecing_util.h" | ||
16 | #include "sitecing/sitecing_parser.h" | ||
17 | #include "sitecing/sitecing_exception.h" | ||
18 | #endif | ||
19 | |||
20 | namespace sitecing { | ||
21 | |||
22 | static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" }; | ||
23 | |||
24 | component_factory::component_factory(configuration& c) | ||
25 | : config(c), | ||
26 | root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'), | ||
27 | root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'), | ||
28 | root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') { | ||
29 | } | ||
30 | |||
31 | void component_factory::get_dependencies(const string& dst,file_list_t& deps) { | ||
32 | deps.clear(); | ||
33 | string dp = normalize_path(dst,strip_trailing_slash); | ||
34 | // source documents | ||
35 | try { // XXX: or just compare it off? | ||
36 | string noro = strip_prefix(dp,root_source); | ||
37 | return; | ||
38 | }catch(utility_no_affix& una) { | ||
39 | } | ||
40 | // .so binaries | ||
41 | try { | ||
42 | string noso = strip_suffix(dp,".so"); | ||
43 | string noro = strip_prefix(noso,root_so); | ||
44 | deps.push_back(root_intermediate+noro+".cc"); | ||
45 | config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps); | ||
46 | if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) { | ||
47 | ifstream df((root_intermediate+noro+".d").c_str(),ios::in); | ||
48 | if(df.good()) { | ||
49 | string str; | ||
50 | while(!df.eof()) { | ||
51 | df >> str; | ||
52 | if(str.find_first_of("\\:")==string::npos) | ||
53 | deps.push_back(combine_path(config.root_source+noro,str)); | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps); | ||
58 | if(co_so_deps) { | ||
59 | for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i) | ||
60 | deps.push_back(*i); | ||
61 | } | ||
62 | return; | ||
63 | }catch(utility_no_prefix& unp) { | ||
64 | throw konforka::exception(CODEPOINT,"component is outside of component root"); | ||
65 | }catch(utility_no_suffix& uns) { | ||
66 | } | ||
67 | // preprocessor targets | ||
68 | for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { | ||
69 | try { | ||
70 | string nos = strip_suffix(dp,pp_targets[ppt]); | ||
71 | string noro = strip_prefix(nos,root_intermediate); | ||
72 | deps.push_back(root_source+noro); | ||
73 | ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in); | ||
74 | if(imports.good()) { | ||
75 | string str; | ||
76 | while(!imports.eof()) { | ||
77 | imports >> str; | ||
78 | if(!str.empty()) | ||
79 | deps.push_back(root_intermediate+str+".classname"); | ||
80 | } | ||
81 | } | ||
82 | ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in); | ||
83 | if(ancestors.good()) { | ||
84 | string str; | ||
85 | while(!ancestors.eof()) { | ||
86 | ancestors >> str; | ||
87 | if(!str.empty()) | ||
88 | deps.push_back(root_intermediate+str+".classname"); | ||
89 | } | ||
90 | } | ||
91 | config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps); | ||
92 | if(co_intermediate_deps) { | ||
93 | for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i) | ||
94 | deps.push_back(*i); | ||
95 | } | ||
96 | return; | ||
97 | }catch(utility_no_affix& una) { | ||
98 | // do nothing. must be a cpp dependency. | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | bool component_factory::is_uptodate(const string& dst,file_list_t *deps) { | ||
104 | string dp = normalize_path(dst,strip_trailing_slash); | ||
105 | // XXX: or just compare it off, instead of throwing things around. | ||
106 | try { | ||
107 | strip_prefix(dp,root_intermediate); | ||
108 | return file_factory::is_uptodate(dst,deps); | ||
109 | }catch(utility_no_prefix& unp) { | ||
110 | } | ||
111 | try { | ||
112 | strip_prefix(dp,root_so); | ||
113 | return file_factory::is_uptodate(dst,deps); | ||
114 | }catch(utility_no_prefix& unp) { | ||
115 | } | ||
116 | return true; | ||
117 | } | ||
118 | |||
119 | void component_factory::build(const string& dst) { | ||
120 | string dp = normalize_path(dst,strip_trailing_slash); | ||
121 | // sources | ||
122 | try { | ||
123 | string noro = strip_prefix(dp,root_source); | ||
124 | // building the sources is left up to developer | ||
125 | return; | ||
126 | }catch(utility_no_prefix& unp) { | ||
127 | } | ||
128 | // .so files | ||
129 | try { | ||
130 | string noso = strip_suffix(dp,".so"); | ||
131 | string noro = strip_prefix(noso,root_so); | ||
132 | string cc = root_intermediate+noro+".cc"; | ||
133 | if(access(cc.c_str(),R_OK)) | ||
134 | throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")"); | ||
135 | make_path(dir_name(root_so+noro),0755); | ||
136 | string pwd = dir_name(root_source+noro); | ||
137 | auto_chdir dir_changer(pwd); | ||
138 | file_lock lock_source(root_intermediate+noro+".lock"); | ||
139 | file_lock lock_so(root_so+noro+".so.lock"); | ||
140 | int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); | ||
141 | if(stdO<0) | ||
142 | throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout"); | ||
143 | int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); | ||
144 | if(stdE<0) { | ||
145 | close(stdO); | ||
146 | throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr"); | ||
147 | } | ||
148 | list<string> args; | ||
149 | config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags); | ||
150 | if(co_cpp_flags) { | ||
151 | args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end()); | ||
152 | } | ||
153 | config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags); | ||
154 | if(co_ld_flags) { | ||
155 | args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end()); | ||
156 | } | ||
157 | // TODO: maybe move it to separare config option like CoreCPPFLags? | ||
158 | args.push_back("-I"+root_intermediate); | ||
159 | args.push_back("-I"+root_source); | ||
160 | args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d"); | ||
161 | args.push_back("-shared"); | ||
162 | args.push_back("-o"); args.push_back(dp); | ||
163 | args.push_back(cc); | ||
164 | file_list_t ancestors; | ||
165 | get_ancestors(noro,ancestors); | ||
166 | for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) { | ||
167 | string aso=root_so+*i+".so"; | ||
168 | make(aso); | ||
169 | args.push_back(aso); | ||
170 | } | ||
171 | // TODO: "g++" configurable | ||
172 | int rv = execute("g++",args,stdO,stdE); | ||
173 | if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) ) | ||
174 | throw compile_error(CODEPOINT,"failed to compile component",noro); | ||
175 | return; | ||
176 | }catch(utility_no_prefix& unp) { | ||
177 | throw konforka::exception(CODEPOINT,"component is outside of component root"); | ||
178 | }catch(utility_no_suffix& uns) { | ||
179 | } | ||
180 | // preprocessor targets | ||
181 | for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { | ||
182 | try { | ||
183 | string nos = strip_suffix(dp,pp_targets[ppt]); | ||
184 | string noro = strip_prefix(nos,root_intermediate); | ||
185 | string src = root_source+noro; | ||
186 | if(access(src.c_str(),R_OK)) | ||
187 | throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")"); | ||
188 | make_path(dir_name(root_intermediate+noro),0755); | ||
189 | file_lock lock(root_intermediate+noro+".lock"); | ||
190 | sitecing_parser parser(*this); | ||
191 | config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton); | ||
192 | if(co_skeleton) | ||
193 | parser.skeleton = co_skeleton->skeleton; | ||
194 | static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
195 | parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash); | ||
196 | 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)) { | ||
197 | string::size_type lc = parser.class_name.find_first_of(id_chars,illc); | ||
198 | int n = ((lc==string::npos)?parser.class_name.length():lc)-illc; | ||
199 | parser.class_name.replace(illc,n,n,'_'); | ||
200 | } | ||
201 | parser.class_name = "_SCC_"+parser.class_name; | ||
202 | parser.output_basename = nos; | ||
203 | parser.component_basename = noro; | ||
204 | try { | ||
205 | parser.preprocess(src); | ||
206 | }catch(preprocessor_error& pe) { | ||
207 | pe.component_name = noro; | ||
208 | pe.see(CODEPOINT); | ||
209 | throw; | ||
210 | } | ||
211 | return; | ||
212 | }catch(utility_no_affix& una) { | ||
213 | // must be a crap from .d file | ||
214 | } | ||
215 | } | ||
216 | cerr << "ignoring build request for " << dp << endl; | ||
217 | } | ||
218 | |||
219 | int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) { | ||
220 | // XXX: is it right that we do stdio/stderr tricks outside of the function? | ||
221 | cerr << "executing: " << cmd; | ||
222 | vector<const char*> argv(args.size()+2); | ||
223 | argv[0]=cmd.c_str(); | ||
224 | int an = 1; | ||
225 | for(list<string>::const_iterator i=args.begin();i!=args.end();i++) { | ||
226 | cerr << " " << *i ; | ||
227 | argv[an++] = i->c_str(); | ||
228 | } | ||
229 | cerr << endl; | ||
230 | argv[an++]=NULL; | ||
231 | pid_t pid = vfork(); | ||
232 | if(pid==-1) { | ||
233 | close(stdo); close(stde); | ||
234 | throw konforka::exception(CODEPOINT,"failed to vfork()"); | ||
235 | } | ||
236 | if(!pid) { | ||
237 | // child | ||
238 | if(dup2(stdo,1)!=1) | ||
239 | _exit(-1); | ||
240 | if(dup2(stde,2)!=2) | ||
241 | _exit(-1); | ||
242 | close(0); | ||
243 | execvp(cmd.c_str(),(char**)&argv.front()); | ||
244 | _exit(-1); | ||
245 | } | ||
246 | // parent | ||
247 | close(stdo); close(stde); | ||
248 | int rv; | ||
249 | if(waitpid(pid,&rv,0)<0) | ||
250 | throw konforka::exception(CODEPOINT,"failed to waitpid()"); | ||
251 | return rv; | ||
252 | } | ||
253 | |||
254 | string component_factory::get_classname(const string& component) { | ||
255 | string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname"; | ||
256 | make(cn); | ||
257 | ifstream ifs(cn.c_str()); | ||
258 | if(!ifs.good()) | ||
259 | throw konforka::exception(CODEPOINT,"failed to access component .classname"); | ||
260 | ifs >> cn; | ||
261 | return cn; | ||
262 | } | ||
263 | |||
264 | void component_factory::get_ancestors(const string& component,file_list_t& rv) { | ||
265 | string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors"; | ||
266 | make(cn); | ||
267 | ifstream ifs(cn.c_str()); | ||
268 | if(!ifs.good()) | ||
269 | throw konforka::exception(CODEPOINT,"filed to access component .ancestors"); | ||
270 | rv.clear(); | ||
271 | while(!ifs.eof()) { | ||
272 | string a; | ||
273 | ifs >> a; | ||
274 | if(!a.empty()) | ||
275 | rv.push_back(a); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | } | ||