efl

Форк
0
/
eolian_cxx.cc 
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

45
namespace eolian_cxx {
46

47
/// Program options.
48
struct options_type
49
{
50
   std::vector<std::string> include_dirs;
51
   std::vector<std::string> in_files;
52
   mutable Eolian_State* state;
53
   mutable Eolian_Unit const* unit;
54
   std::string out_file;
55
   bool main_header;
56

57
   options_type() : main_header(false) {}
58
};
59

60
static efl::eina::log_domain domain("eolian_cxx");
61

62
static bool
63
opts_check(eolian_cxx::options_type const& opts)
64
{
65
   if (opts.in_files.empty())
66
     {
67
        EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
68
          << "Nothing to generate?" << std::endl;
69
     }
70
   else
71
     {
72
        return true; // valid.
73
     }
74
   return false;
75
}
76

77
static bool
78
generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts,
79
         std::string const& cpp_types_header)
80
{
81
   std::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

84
   std::string header_impl_file_name = header_decl_file_name;
85
   std::size_t dot_pos = header_impl_file_name.rfind(".hh");
86
   if (dot_pos != std::string::npos)
87
     header_impl_file_name.insert(dot_pos, ".impl");
88
   else
89
     header_impl_file_name.insert(header_impl_file_name.size(), ".impl");
90

91
   efl::eolian::grammar::attributes::klass_def klass_def(klass, opts.unit);
92
   std::vector<efl::eolian::grammar::attributes::klass_def> klasses{klass_def};
93
   std::set<efl::eolian::grammar::attributes::klass_def> forward_klasses{};
94

95
   std::set<std::string> c_headers;
96
   std::set<std::string> cpp_headers;
97
   c_headers.insert(eolian_object_file_get((const Eolian_Object *)klass) + std::string(".h"));
98
        
99
   std::function<void(efl::eolian::grammar::attributes::type_def const&)>
100
     variant_function;
101
   auto klass_name_function
102
     = [&] (efl::eolian::grammar::attributes::klass_name const& name)
103
     {
104
        Eolian_Class const* klass2 = get_klass(name, opts.unit);
105
        assert(klass2);
106
        c_headers.insert(eolian_object_file_get((const Eolian_Object *)klass2) + std::string(".h"));
107
        cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)klass2) + std::string(".hh"));
108
        efl::eolian::grammar::attributes::klass_def cls{klass2, opts.unit};
109
        forward_klasses.insert(cls);
110
     };
111
   auto complex_function
112
     = [&] (efl::eolian::grammar::attributes::complex_type_def const& complex)
113
     {
114
       for(auto&& t : complex.subtypes)
115
         {
116
           variant_function(t);
117
         }
118
     };
119
   variant_function = [&] (efl::eolian::grammar::attributes::type_def const& type)
120
     {
121
       if(efl::eolian::grammar::attributes::klass_name const*
122
          name = efl::eina::get<efl::eolian::grammar::attributes::klass_name>
123
          (&type.original_type))
124
         klass_name_function(*name);
125
       else if(efl::eolian::grammar::attributes::complex_type_def const*
126
              complex = efl::eina::get<efl::eolian::grammar::attributes::complex_type_def>
127
               (&type.original_type))
128
         complex_function(*complex);
129
     };
130

131
   std::function<void(Eolian_Class const*)> klass_function
132
     = [&] (Eolian_Class const* klass2)
133
     {
134
       if(::eolian_class_parent_get(klass2))
135
         {
136
           Eolian_Class const* inherit = ::eolian_class_parent_get(klass2);
137
           c_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".h"));
138
           cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".hh"));
139
           efl::eolian::grammar::attributes::klass_def klass3{inherit, opts.unit};
140
           forward_klasses.insert(klass3);
141

142
           klass_function(inherit);
143
         }
144
       for(efl::eina::iterator<Eolian_Class const> inherit_iterator ( ::eolian_class_extensions_get(klass2))
145
             , inherit_last; inherit_iterator != inherit_last; ++inherit_iterator)
146
         {
147
           Eolian_Class const* inherit = &*inherit_iterator;
148
           c_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".h"));
149
           cpp_headers.insert(eolian_object_file_get((const Eolian_Object *)inherit) + std::string(".hh"));
150
           efl::eolian::grammar::attributes::klass_def klass3{inherit, opts.unit};
151
           forward_klasses.insert(klass3);
152

153
           klass_function(inherit);
154
         }
155

156
       efl::eolian::grammar::attributes::klass_def klass2_def(klass2, opts.unit);
157
       for(auto&& f : klass2_def.functions)
158
         {
159
           variant_function(f.return_type);
160
           for(auto&& p : f.parameters)
161
             {
162
               variant_function(p.type);
163
             }
164
         }
165
       for(auto&& e : klass2_def.events)
166
         {
167
           if(e.type)
168
             variant_function(*e.type);
169
         }
170
     };
171
   klass_function(klass);
172

173
   for(efl::eina::iterator<Eolian_Part const> parts_itr ( ::eolian_class_parts_get(klass))
174
     , parts_last; parts_itr != parts_last; ++parts_itr)
175
     {
176
        Eolian_Class const* eolian_part_klass = ::eolian_part_class_get(&*parts_itr);
177
        efl::eolian::grammar::attributes::klass_def part_klass(eolian_part_klass, opts.unit);
178
        forward_klasses.insert(part_klass);
179
     }
180

181
   cpp_headers.erase(eolian_object_file_get((const Eolian_Object *)klass) + std::string(".hh"));
182

183
   std::string guard_name;
184
   as_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

189
   std::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

198
   if(opts.out_file == "-")
199
     {
200
        std::ostream_iterator<char> iterator(std::cout);
201

202
        efl::eolian::grammar::class_header.generate(iterator, attributes, efl::eolian::grammar::context_null());
203
        std::endl(std::cout);
204

205
        efl::eolian::grammar::impl_header.generate(iterator, klasses, efl::eolian::grammar::context_null());
206

207
        std::endl(std::cout);
208
        std::flush(std::cout);
209
        std::flush(std::cerr);
210
     }
211
   else
212
     {
213
        std::ofstream header_decl;
214
        header_decl.open(header_decl_file_name);
215
        if (!header_decl.good())
216
          {
217
             EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
218
               << "Can't open output file: " << header_decl_file_name << std::endl;
219
             return false;
220
          }
221

222
        std::ofstream header_impl;
223
        header_impl.open(header_impl_file_name);
224
        if (!header_impl.good())
225
          {
226
             EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
227
               << "Can't open output file: " << header_impl_file_name << std::endl;
228
             return false;
229
          }
230

231
        efl::eolian::grammar::class_header.generate
232
          (std::ostream_iterator<char>(header_decl), attributes, efl::eolian::grammar::context_null());
233

234
        efl::eolian::grammar::impl_header.generate
235
          (std::ostream_iterator<char>(header_impl), klasses, efl::eolian::grammar::context_null());
236

237
        header_impl.close();
238
        header_decl.close();
239
     }
240
   return true;
241
}
242

243
static bool
244
types_generate(std::string const& fname, options_type const& opts,
245
               std::string& cpp_types_header)
246
{
247
   using namespace efl::eolian::grammar::attributes;
248

249
   std::vector<function_def> functions;
250
   Eina_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
254
   while(::eina_iterator_next(itr, reinterpret_cast<void**>(&decl)))
255
     {
256
        Eolian_Object_Type dt = eolian_object_type_get(decl);
257
        if (dt != EOLIAN_OBJECT_TYPEDECL)
258
          continue;
259

260
        const Eolian_Typedecl *tp = (const Eolian_Typedecl *)decl;
261

262
        if (eolian_typedecl_is_extern(tp))
263
          continue;
264

265
        if (::eolian_typedecl_type_get(tp) != EOLIAN_TYPEDECL_FUNCTION_POINTER)
266
          continue;
267

268
        const Eolian_Function *func = eolian_typedecl_function_pointer_get(tp);
269
        if (!func) return false;
270

271
        function_def def(func, EOLIAN_FUNCTION_POINTER, tp, opts.unit);
272
        def.c_name = eolian_typedecl_name_get(tp);
273
        std::replace(def.c_name.begin(), def.c_name.end(), '.', '_');
274
        functions.push_back(std::move(def));
275
     }
276
   ::eina_iterator_free(itr);
277

278
   if (functions.empty())
279
     return true;
280

281
   std::stringstream sink;
282

283
   sink.write("\n", 1);
284
   if (!efl::eolian::grammar::types_definition
285
       .generate(std::ostream_iterator<char>(sink),
286
                 functions, efl::eolian::grammar::context_null()))
287
     return false;
288

289
   cpp_types_header = sink.str();
290

291
   return true;
292
}
293

294
static void
295
run(options_type const& opts)
296
{
297
   if(!opts.main_header)
298
     {
299
       const Eolian_Class *klass = nullptr;
300
       char* dup = strdup(opts.in_files[0].c_str());
301
       std::string base (basename(dup));
302
       std::string cpp_types_header;
303
       opts.unit = (Eolian_Unit*)opts.state;
304
       klass = ::eolian_state_class_by_file_get(opts.state, base.c_str());
305
       free(dup);
306
       if (klass)
307
         {
308
           if (!types_generate(base, opts, cpp_types_header) ||
309
               !generate(klass, opts, cpp_types_header))
310
             {
311
               EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
312
                 << "Error generating: " << ::eolian_class_short_name_get(klass)
313
                 << std::endl;
314
               assert(false && "error generating class");
315
             }
316
         }
317
       else
318
         {
319
           if (!types_generate(base, opts, cpp_types_header))
320
             {
321
               EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
322
                 << "Error generating: " << ::eolian_class_short_name_get(klass)
323
                 << std::endl;
324
               assert(false && "error generating class");
325
             }
326
           else
327
             {
328
               std::ofstream header_decl;
329
               header_decl.open(opts.out_file);
330
               if (!header_decl.good())
331
               {
332
                 EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
333
                   << "Can't open output file: " << opts.out_file << std::endl;
334
                 assert(false && "error opening file");
335
               }
336
               std::copy (cpp_types_header.begin(), cpp_types_header.end()
337
                          , std::ostream_iterator<char>(header_decl));
338
             }
339
         }
340
     }
341
   else
342
     {
343
       std::set<std::string> headers;
344
       std::set<std::string> eo_files;
345

346
       for(auto&& name : opts.in_files)
347
         {
348
           Eolian_Unit const* unit = ::eolian_state_file_path_parse(opts.state, name.c_str());
349
           if(!unit)
350
             {
351
               EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
352
                 << "Failed parsing: " << name << ".";
353
             }
354
           else
355
             {
356
               if(!opts.unit)
357
                 opts.unit = unit;
358
             }
359
           char* dup = strdup(name.c_str());
360
           std::string base(basename(dup));
361
           Eolian_Class const* klass = ::eolian_state_class_by_file_get(opts.state, base.c_str());
362
           free(dup);
363
           if (klass)
364
             {
365
               std::string filename = eolian_object_file_get((const Eolian_Object *)klass);
366
               headers.insert(filename + std::string(".hh"));
367
               eo_files.insert(filename);
368
             }
369
           else
370
           {
371
             headers.insert (base + std::string(".hh"));
372
           }
373
         }
374

375
       using efl::eolian::grammar::header_include_directive;
376
       using efl::eolian::grammar::implementation_include_directive;
377

378
       auto main_header_grammar =
379
         *header_include_directive // sequence<string>
380
         << *implementation_include_directive // sequence<string>
381
         ;
382

383
       std::tuple<std::set<std::string>&, std::set<std::string>&> attributes{headers, eo_files};
384

385
       std::ofstream main_header;
386
       main_header.open(opts.out_file);
387
       if (!main_header.good())
388
         {
389
           EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
390
             << "Can't open output file: " << opts.out_file << std::endl;
391
           return;
392
         }
393
       
394
       main_header_grammar.generate(std::ostream_iterator<char>(main_header)
395
                                    , attributes, efl::eolian::grammar::context_null());
396
     }
397
}
398

399
static void
400
database_load(options_type const& opts)
401
{
402
   for (auto src : opts.include_dirs)
403
     {
404
        if (!::eolian_state_directory_add(opts.state, src.c_str()))
405
          {
406
             EINA_CXX_DOM_LOG_WARN(eolian_cxx::domain)
407
               << "Couldn't load eolian from '" << src << "'.";
408
          }
409
     }
410
   if (!::eolian_state_all_eot_files_parse(opts.state))
411
     {
412
        EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
413
          << "Eolian failed parsing eot files";
414
        assert(false && "Error parsing eot files");
415
     }
416
   if (opts.in_files.empty())
417
     {
418
       EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
419
         << "No input file.";
420
       assert(false && "Error parsing input file");
421
     }
422
   if (!opts.main_header && !::eolian_state_file_path_parse(opts.state, opts.in_files[0].c_str()))
423
     {
424
       EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
425
         << "Failed parsing: " << opts.in_files[0] << ".";
426
       assert(false && "Error parsing input file");
427
     }
428
}
429

430
} // namespace eolian_cxx {
431

432
static void
433
_print_version()
434
{
435
   std::cerr
436
     << "Eolian C++ Binding Generator (EFL "
437
     << PACKAGE_VERSION << ")" << std::endl;
438
}
439

440
static void
441
_usage(const char *progname)
442
{
443
   std::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;
457
   exit(EXIT_FAILURE);
458
}
459

460
static void
461
_assert_not_dup(std::string option, std::string value)
462
{
463
   if (value != "")
464
     {
465
        EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain) <<
466
          "Option -" + option + " already set (" + value + ")";
467
     }
468
}
469

470
static eolian_cxx::options_type
471
opts_get(int argc, char **argv)
472
{
473
   eolian_cxx::options_type opts;
474

475
   const 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
     };
484
   const char* options = "I:D:o:c::marvh";
485

486
   int c, idx;
487
   while ( (c = getopt_long(argc, argv, options, long_options, &idx)) != -1)
488
     {
489
        if (c == 'I')
490
          {
491
             opts.include_dirs.push_back(optarg);
492
          }
493
        else if (c == 'o')
494
          {
495
             _assert_not_dup("o", opts.out_file);
496
             opts.out_file = optarg;
497
          }
498
        else if (c == 'h')
499
          {
500
             _usage(argv[0]);
501
          }
502
        else if(c == 'm')
503
          {
504
            opts.main_header = true;
505
          }
506
        else if (c == 'v')
507
          {
508
             _print_version();
509
             if (argc == 2) exit(EXIT_SUCCESS);
510
          }
511
     }
512
   if (optind != argc)
513
     {
514
       for(int i = optind; i != argc; ++i)
515
         opts.in_files.push_back(argv[i]);
516
     }
517

518
   if (!eolian_cxx::opts_check(opts))
519
     {
520
        _usage(argv[0]);
521
        assert(false && "Wrong options passed in command-line");
522
     }
523

524
   return opts;
525
}
526

527
int main(int argc, char **argv)
528
{
529
   try
530
     {
531
        efl::eina::eina_init eina_init;
532
        efl::eolian::eolian_init eolian_init;
533
        efl::eolian::eolian_state eolian_state;
534
        eolian_cxx::options_type opts = opts_get(argc, argv);
535
        opts.state = eolian_state.value;
536
        eolian_cxx::database_load(opts);
537
        eolian_cxx::run(opts);
538
     }
539
   catch(std::exception const& e)
540
     {
541
       std::cerr << "EOLCXX: Eolian C++ failed generation for the following reason: " << e.what() << std::endl;
542
       std::cout << "EOLCXX: Eolian C++ failed generation for the following reason: " << e.what() << std::endl;
543
       return -1;
544
     }
545
   return 0;
546
}
547

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.