efl
546 строк · 18.1 Кб
1/*
2* Copyright 2019 by its authors. See AUTHORS.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17#include <iostream>
18#include <fstream>
19
20#include <stdlib.h>
21#include <unistd.h>
22#include <getopt.h>
23#include <libgen.h>
24
25#include <string>
26#include <algorithm>
27#include <stdexcept>
28#include <iosfwd>
29#include <type_traits>
30#include <cassert>
31
32#ifdef HAVE_CONFIG_H
33# include <config.h>
34#endif
35#include <Eolian.h>
36
37#include <Eina.hh>
38#include <Eolian_Cxx.hh>
39
40#include "grammar/klass_def.hpp"
41#include "grammar/header.hpp"
42#include "grammar/impl_header.hpp"
43#include "grammar/types_definition.hpp"
44
45namespace eolian_cxx {
46
47/// Program options.
48struct options_type
49{
50std::vector<std::string> include_dirs;
51std::vector<std::string> in_files;
52mutable Eolian_State* state;
53mutable Eolian_Unit const* unit;
54std::string out_file;
55bool main_header;
56
57options_type() : main_header(false) {}
58};
59
60static efl::eina::log_domain domain("eolian_cxx");
61
62static bool
63opts_check(eolian_cxx::options_type const& opts)
64{
65if (opts.in_files.empty())
66{
67EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
68<< "Nothing to generate?" << std::endl;
69}
70else
71{
72return true; // valid.
73}
74return false;
75}
76
77static bool
78generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts,
79std::string const& cpp_types_header)
80{
81std::string header_decl_file_name = opts.out_file.empty()
82? (::eolian_object_file_get((const Eolian_Object *)klass) + std::string(".hh")) : opts.out_file;
83
84std::string header_impl_file_name = header_decl_file_name;
85std::size_t dot_pos = header_impl_file_name.rfind(".hh");
86if (dot_pos != std::string::npos)
87header_impl_file_name.insert(dot_pos, ".impl");
88else
89header_impl_file_name.insert(header_impl_file_name.size(), ".impl");
90
91efl::eolian::grammar::attributes::klass_def klass_def(klass, opts.unit);
92std::vector<efl::eolian::grammar::attributes::klass_def> klasses{klass_def};
93std::set<efl::eolian::grammar::attributes::klass_def> forward_klasses{};
94
95std::set<std::string> c_headers;
96std::set<std::string> cpp_headers;
97c_headers.insert(eolian_object_file_get((const Eolian_Object *)klass) + std::string(".h"));
98
99std::function<void(efl::eolian::grammar::attributes::type_def const&)>
100variant_function;
101auto klass_name_function
102= [&] (efl::eolian::grammar::attributes::klass_name const& name)
103{
104Eolian_Class const* klass2 = get_klass(name, opts.unit);
105assert(klass2);
106c_headers.insert(eolian_object_file_get((const Eolian_Object *)klass2) + std::string(".h"));
107cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)klass2) + std::string(".hh"));
108efl::eolian::grammar::attributes::klass_def cls{klass2, opts.unit};
109forward_klasses.insert(cls);
110};
111auto complex_function
112= [&] (efl::eolian::grammar::attributes::complex_type_def const& complex)
113{
114for(auto&& t : complex.subtypes)
115{
116variant_function(t);
117}
118};
119variant_function = [&] (efl::eolian::grammar::attributes::type_def const& type)
120{
121if(efl::eolian::grammar::attributes::klass_name const*
122name = efl::eina::get<efl::eolian::grammar::attributes::klass_name>
123(&type.original_type))
124klass_name_function(*name);
125else if(efl::eolian::grammar::attributes::complex_type_def const*
126complex = efl::eina::get<efl::eolian::grammar::attributes::complex_type_def>
127(&type.original_type))
128complex_function(*complex);
129};
130
131std::function<void(Eolian_Class const*)> klass_function
132= [&] (Eolian_Class const* klass2)
133{
134if(::eolian_class_parent_get(klass2))
135{
136Eolian_Class const* inherit = ::eolian_class_parent_get(klass2);
137c_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".h"));
138cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".hh"));
139efl::eolian::grammar::attributes::klass_def klass3{inherit, opts.unit};
140forward_klasses.insert(klass3);
141
142klass_function(inherit);
143}
144for(efl::eina::iterator<Eolian_Class const> inherit_iterator ( ::eolian_class_extensions_get(klass2))
145, inherit_last; inherit_iterator != inherit_last; ++inherit_iterator)
146{
147Eolian_Class const* inherit = &*inherit_iterator;
148c_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".h"));
149cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".hh"));
150efl::eolian::grammar::attributes::klass_def klass3{inherit, opts.unit};
151forward_klasses.insert(klass3);
152
153klass_function(inherit);
154}
155
156efl::eolian::grammar::attributes::klass_def klass2_def(klass2, opts.unit);
157for(auto&& f : klass2_def.functions)
158{
159variant_function(f.return_type);
160for(auto&& p : f.parameters)
161{
162variant_function(p.type);
163}
164}
165for(auto&& e : klass2_def.events)
166{
167if(e.type)
168variant_function(*e.type);
169}
170};
171klass_function(klass);
172
173for(efl::eina::iterator<Eolian_Part const> parts_itr ( ::eolian_class_parts_get(klass))
174, parts_last; parts_itr != parts_last; ++parts_itr)
175{
176Eolian_Class const* eolian_part_klass = ::eolian_part_class_get(&*parts_itr);
177efl::eolian::grammar::attributes::klass_def part_klass(eolian_part_klass, opts.unit);
178forward_klasses.insert(part_klass);
179}
180
181cpp_headers.erase(eolian_object_file_get((const Eolian_Object *)klass) + std::string(".hh"));
182
183std::string guard_name;
184as_generator(*(efl::eolian::grammar::string << "_") << efl::eolian::grammar::string << "_EO_HH")
185.generate(std::back_insert_iterator<std::string>(guard_name)
186, std::make_tuple(klass_def.namespaces, klass_def.eolian_name)
187, efl::eolian::grammar::context_null{});
188
189std::tuple<std::string, std::set<std::string>&, std::set<std::string>&
190, std::vector<efl::eolian::grammar::attributes::klass_def>&
191, std::set<efl::eolian::grammar::attributes::klass_def> const&
192, std::string const&
193, std::vector<efl::eolian::grammar::attributes::klass_def>&
194, std::vector<efl::eolian::grammar::attributes::klass_def>&
195> attributes
196{guard_name, c_headers, cpp_headers, klasses, forward_klasses, cpp_types_header, klasses, klasses};
197
198if(opts.out_file == "-")
199{
200std::ostream_iterator<char> iterator(std::cout);
201
202efl::eolian::grammar::class_header.generate(iterator, attributes, efl::eolian::grammar::context_null());
203std::endl(std::cout);
204
205efl::eolian::grammar::impl_header.generate(iterator, klasses, efl::eolian::grammar::context_null());
206
207std::endl(std::cout);
208std::flush(std::cout);
209std::flush(std::cerr);
210}
211else
212{
213std::ofstream header_decl;
214header_decl.open(header_decl_file_name);
215if (!header_decl.good())
216{
217EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
218<< "Can't open output file: " << header_decl_file_name << std::endl;
219return false;
220}
221
222std::ofstream header_impl;
223header_impl.open(header_impl_file_name);
224if (!header_impl.good())
225{
226EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
227<< "Can't open output file: " << header_impl_file_name << std::endl;
228return false;
229}
230
231efl::eolian::grammar::class_header.generate
232(std::ostream_iterator<char>(header_decl), attributes, efl::eolian::grammar::context_null());
233
234efl::eolian::grammar::impl_header.generate
235(std::ostream_iterator<char>(header_impl), klasses, efl::eolian::grammar::context_null());
236
237header_impl.close();
238header_decl.close();
239}
240return true;
241}
242
243static bool
244types_generate(std::string const& fname, options_type const& opts,
245std::string& cpp_types_header)
246{
247using namespace efl::eolian::grammar::attributes;
248
249std::vector<function_def> functions;
250Eina_Iterator *itr = eolian_state_objects_by_file_get(opts.state, fname.c_str());
251/* const */ Eolian_Object *decl;
252
253// Build list of functions with their parameters
254while(::eina_iterator_next(itr, reinterpret_cast<void**>(&decl)))
255{
256Eolian_Object_Type dt = eolian_object_type_get(decl);
257if (dt != EOLIAN_OBJECT_TYPEDECL)
258continue;
259
260const Eolian_Typedecl *tp = (const Eolian_Typedecl *)decl;
261
262if (eolian_typedecl_is_extern(tp))
263continue;
264
265if (::eolian_typedecl_type_get(tp) != EOLIAN_TYPEDECL_FUNCTION_POINTER)
266continue;
267
268const Eolian_Function *func = eolian_typedecl_function_pointer_get(tp);
269if (!func) return false;
270
271function_def def(func, EOLIAN_FUNCTION_POINTER, tp, opts.unit);
272def.c_name = eolian_typedecl_name_get(tp);
273std::replace(def.c_name.begin(), def.c_name.end(), '.', '_');
274functions.push_back(std::move(def));
275}
276::eina_iterator_free(itr);
277
278if (functions.empty())
279return true;
280
281std::stringstream sink;
282
283sink.write("\n", 1);
284if (!efl::eolian::grammar::types_definition
285.generate(std::ostream_iterator<char>(sink),
286functions, efl::eolian::grammar::context_null()))
287return false;
288
289cpp_types_header = sink.str();
290
291return true;
292}
293
294static void
295run(options_type const& opts)
296{
297if(!opts.main_header)
298{
299const Eolian_Class *klass = nullptr;
300char* dup = strdup(opts.in_files[0].c_str());
301std::string base (basename(dup));
302std::string cpp_types_header;
303opts.unit = (Eolian_Unit*)opts.state;
304klass = ::eolian_state_class_by_file_get(opts.state, base.c_str());
305free(dup);
306if (klass)
307{
308if (!types_generate(base, opts, cpp_types_header) ||
309!generate(klass, opts, cpp_types_header))
310{
311EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
312<< "Error generating: " << ::eolian_class_short_name_get(klass)
313<< std::endl;
314assert(false && "error generating class");
315}
316}
317else
318{
319if (!types_generate(base, opts, cpp_types_header))
320{
321EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
322<< "Error generating: " << ::eolian_class_short_name_get(klass)
323<< std::endl;
324assert(false && "error generating class");
325}
326else
327{
328std::ofstream header_decl;
329header_decl.open(opts.out_file);
330if (!header_decl.good())
331{
332EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
333<< "Can't open output file: " << opts.out_file << std::endl;
334assert(false && "error opening file");
335}
336std::copy (cpp_types_header.begin(), cpp_types_header.end()
337, std::ostream_iterator<char>(header_decl));
338}
339}
340}
341else
342{
343std::set<std::string> headers;
344std::set<std::string> eo_files;
345
346for(auto&& name : opts.in_files)
347{
348Eolian_Unit const* unit = ::eolian_state_file_path_parse(opts.state, name.c_str());
349if(!unit)
350{
351EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
352<< "Failed parsing: " << name << ".";
353}
354else
355{
356if(!opts.unit)
357opts.unit = unit;
358}
359char* dup = strdup(name.c_str());
360std::string base(basename(dup));
361Eolian_Class const* klass = ::eolian_state_class_by_file_get(opts.state, base.c_str());
362free(dup);
363if (klass)
364{
365std::string filename = eolian_object_file_get((const Eolian_Object *)klass);
366headers.insert(filename + std::string(".hh"));
367eo_files.insert(filename);
368}
369else
370{
371headers.insert (base + std::string(".hh"));
372}
373}
374
375using efl::eolian::grammar::header_include_directive;
376using efl::eolian::grammar::implementation_include_directive;
377
378auto main_header_grammar =
379*header_include_directive // sequence<string>
380<< *implementation_include_directive // sequence<string>
381;
382
383std::tuple<std::set<std::string>&, std::set<std::string>&> attributes{headers, eo_files};
384
385std::ofstream main_header;
386main_header.open(opts.out_file);
387if (!main_header.good())
388{
389EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
390<< "Can't open output file: " << opts.out_file << std::endl;
391return;
392}
393
394main_header_grammar.generate(std::ostream_iterator<char>(main_header)
395, attributes, efl::eolian::grammar::context_null());
396}
397}
398
399static void
400database_load(options_type const& opts)
401{
402for (auto src : opts.include_dirs)
403{
404if (!::eolian_state_directory_add(opts.state, src.c_str()))
405{
406EINA_CXX_DOM_LOG_WARN(eolian_cxx::domain)
407<< "Couldn't load eolian from '" << src << "'.";
408}
409}
410if (!::eolian_state_all_eot_files_parse(opts.state))
411{
412EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
413<< "Eolian failed parsing eot files";
414assert(false && "Error parsing eot files");
415}
416if (opts.in_files.empty())
417{
418EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
419<< "No input file.";
420assert(false && "Error parsing input file");
421}
422if (!opts.main_header && !::eolian_state_file_path_parse(opts.state, opts.in_files[0].c_str()))
423{
424EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
425<< "Failed parsing: " << opts.in_files[0] << ".";
426assert(false && "Error parsing input file");
427}
428}
429
430} // namespace eolian_cxx {
431
432static void
433_print_version()
434{
435std::cerr
436<< "Eolian C++ Binding Generator (EFL "
437<< PACKAGE_VERSION << ")" << std::endl;
438}
439
440static void
441_usage(const char *progname)
442{
443std::cerr
444<< progname
445<< " [options] [file.eo]" << std::endl
446<< " A single input file must be provided (unless -a is specified)." << std::endl
447<< "Options:" << std::endl
448<< " -a, --all Generate bindings for all Eo classes." << std::endl
449<< " -c, --class <name> The Eo class name to generate code for." << std::endl
450<< " -D, --out-dir <dir> Output directory where generated code will be written." << std::endl
451<< " -I, --in <file/dir> The source containing the .eo descriptions." << std::endl
452<< " -o, --out-file <file> The output file name. [default: <classname>.eo.hh]" << std::endl
453<< " -n, --namespace <ns> Wrap generated code in a namespace. [Eg: efl::ecore::file]" << std::endl
454<< " -r, --recurse Recurse input directories loading .eo files." << std::endl
455<< " -v, --version Print the version." << std::endl
456<< " -h, --help Print this help." << std::endl;
457exit(EXIT_FAILURE);
458}
459
460static void
461_assert_not_dup(std::string option, std::string value)
462{
463if (value != "")
464{
465EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain) <<
466"Option -" + option + " already set (" + value + ")";
467}
468}
469
470static eolian_cxx::options_type
471opts_get(int argc, char **argv)
472{
473eolian_cxx::options_type opts;
474
475const struct option long_options[] =
476{
477{ "in", required_argument, nullptr, 'I' },
478{ "out-file", required_argument, nullptr, 'o' },
479{ "version", no_argument, nullptr, 'v' },
480{ "help", no_argument, nullptr, 'h' },
481{ "main-header", no_argument, nullptr, 'm' },
482{ nullptr, 0, nullptr, 0 }
483};
484const char* options = "I:D:o:c::marvh";
485
486int c, idx;
487while ( (c = getopt_long(argc, argv, options, long_options, &idx)) != -1)
488{
489if (c == 'I')
490{
491opts.include_dirs.push_back(optarg);
492}
493else if (c == 'o')
494{
495_assert_not_dup("o", opts.out_file);
496opts.out_file = optarg;
497}
498else if (c == 'h')
499{
500_usage(argv[0]);
501}
502else if(c == 'm')
503{
504opts.main_header = true;
505}
506else if (c == 'v')
507{
508_print_version();
509if (argc == 2) exit(EXIT_SUCCESS);
510}
511}
512if (optind != argc)
513{
514for(int i = optind; i != argc; ++i)
515opts.in_files.push_back(argv[i]);
516}
517
518if (!eolian_cxx::opts_check(opts))
519{
520_usage(argv[0]);
521assert(false && "Wrong options passed in command-line");
522}
523
524return opts;
525}
526
527int main(int argc, char **argv)
528{
529try
530{
531efl::eina::eina_init eina_init;
532efl::eolian::eolian_init eolian_init;
533efl::eolian::eolian_state eolian_state;
534eolian_cxx::options_type opts = opts_get(argc, argv);
535opts.state = eolian_state.value;
536eolian_cxx::database_load(opts);
537eolian_cxx::run(opts);
538}
539catch(std::exception const& e)
540{
541std::cerr << "EOLCXX: Eolian C++ failed generation for the following reason: " << e.what() << std::endl;
542std::cout << "EOLCXX: Eolian C++ failed generation for the following reason: " << e.what() << std::endl;
543return -1;
544}
545return 0;
546}
547