• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 16481710579

23 Jul 2025 09:00PM UTC coverage: 59.58% (+0.04%) from 59.537%
16481710579

push

github

web-flow
Add ecompile flag -Z to print AST (#796)

* Add -Z option to print string tree

* Simple test to print an AST

* Make CompiledScript owner of the tree

* some refactoring
- rename ecompile.cfg option
- rename AST generator class

* core-changes, docs

* fix warnings

34 of 38 new or added lines in 7 files covered. (89.47%)

2 existing lines in 2 files now uncovered.

43382 of 72813 relevant lines covered (59.58%)

457932.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.31
/pol-core/ecompile/ECompileMain.cpp
1
#include "ECompileMain.h"
2

3
#include <cstdio>
4
#include <exception>
5
#include <filesystem>
6
#include <iosfwd>
7
#include <memory>
8
#include <stdlib.h>
9
#include <string>
10
#include <system_error>
11
#include <time.h>
12

13
#include "EfswFileWatchListener.h"
14

15
#include "bscript/compiler/Compiler.h"
16
#include "bscript/compiler/Profile.h"
17
#include "bscript/compiler/file/SourceFileCache.h"
18
#include "bscript/compilercfg.h"
19
#include "bscript/escriptv.h"
20
#include "bscript/executor.h"
21
#include "bscript/executortype.h"
22
#include "bscript/filefmt.h"
23
#include "clib/Program/ProgramConfig.h"
24
#include "clib/Program/ProgramMain.h"
25
#include "clib/esignal.h"
26
#include "clib/fileutil.h"
27
#include "clib/logfacility.h"
28
#include "clib/mdump.h"
29
#include "clib/passert.h"
30
#include "clib/threadhelp.h"
31
#include "clib/timer.h"
32
#include "plib/pkg.h"
33
#include "plib/systemstate.h"
34

35
#define VERBOSE_PRINTLN                 \
36
  if ( compilercfg.VerbosityLevel > 0 ) \
37
  INFO_PRINTLN
38

39
namespace Pol
40
{
41
namespace ECompile
42
{
43
namespace fs = std::filesystem;
44
using namespace Pol::Core;
45
using namespace Pol::Plib;
46
using namespace Pol::Bscript;
47

48
///////////////////////////////////////////////////////////////////////////////
49

50

51
///////////////////////////////////////////////////////////////////////////////
52

53
ECompileMain::ECompileMain() : Pol::Clib::ProgramMain() {}
2,091✔
54
ECompileMain::~ECompileMain() {}
×
55
///////////////////////////////////////////////////////////////////////////////
56

57
void ECompileMain::showHelp()
×
58
{
59
  INFO_PRINTLN(
×
60
      "Usage:\n"
61
      "    \n"
62
      "  ECOMPILE [options] filespec [filespec ...]\n"
63
      "    \n"
64
      "  Output is : filespec.ecl\n"
65
      "  Options:\n"
66
      "   Options: \n"
67
      "       -F           format filespec (print result)\n"
68
      "       -Fi          format filespec (inplace)\n"
69
      "       -Z           generate abstract syntax tree (AST)\n"
70
      "       -a           compile *.asp pages also\n"
71
      "       -A           automatically compile scripts in main and enabled packages\n"
72
      "       -Au          (as '-A' but only compile updated files)\n"
73
      "       -b           keep building other scripts after errors\n"
74
      "       -c           treat wrong capitalization in include directives as error\n"
75
      "       -C cfgpath   path to configuration (replaces ecompile.cfg)\n"
76
      "       -d           display confusing compiler parse information\n"
77
      "       -D           write dependency information\n"
78
      "       -e           report error on successful compilation (used for testing)\n"
79
#ifdef WIN32
80
      "       -Ecfgpath    set or change the ECOMPILE_CFG_PATH evironment variable\n"
81
#endif
82

83
      "       -i           include intrusive debug info in .ecl file\n"
84
      "       -l           generate listfile\n"
85
      "       -m           don't optimize object members\n"
86
#ifdef WIN32
87
      "       -Pdir        set or change the EM and INC files Environment Variables\n"
88
#endif
89
      "       -q           quiet mode (suppress normal output)\n"
90
      "       -r [dir]     recurse folder [from 'dir'] (defaults to current folder)\n"
91
      "       -ri [dir]    (as '-r' but only compile .inc files)\n"
92
      "       -t[v]        show timing/profiling information [override quiet mode]\n"
93
      "       -u           compile only updated scripts (.src newer than .ecl)\n"
94
      "       -f           force compile even if up-to-date\n"
95
      "       -s           display summary if -q is not set\n"
96
      "       -T[N]        use threaded compilation, force N threads to run\n"
97
      "       -vN          verbosity level\n"
98
      "       -w           display warnings\n"
99
      "       -W           watch mode\n"
100
      "       -y           treat warnings as errors\n"
101
      "       -x           write external .dbg file\n"
102
      "       -xt          write external .dbg.txt info file\n"
103
      "\n"
104
      " NOTE:\n"
105
      "   If <filespec> are required after an empty -r[i] option, you MUST specify\n"
106
      "   a literal [dir] of '.' (no quotes) or options will not parse correctly." );
107
}
×
108
static int s_argc;
109
static char** s_argv;
110

111
int debug = 0;
112
bool quiet = false;
113
bool keep_building = false;
114
bool force_update = false;
115
bool format_source = false;
116
bool format_source_inplace = false;
117
bool show_timing_details = false;
118
bool timing_quiet_override = false;
119
bool expect_compile_failure = false;
120
bool dont_optimize_object_members = false;
121
bool watch_source = false;
122
std::string EmPathEnv;   // "ECOMPILE_PATH_EM=xxx"
123
std::string IncPathEnv;  // ECOMPILE_PATH_INC=yyy"
124
std::string CfgPathEnv;  // ECOMPILE_CFG_PATH=zzz"
125

126
struct Summary
127
{
128
  unsigned UpToDateScripts = 0;
129
  unsigned CompiledScripts = 0;
130
  unsigned ScriptsWithCompileErrors = 0;
131
  size_t ThreadCount = 0;
132
  Compiler::Profile profile;
133
} summary;
134

135
struct Comparison
136
{
137
  std::atomic<long long> CompileTimeV1Micros{};
138
  std::atomic<long long> CompileTimeV2Micros{};
139
  std::atomic<long> MatchingResult{};
140
  std::atomic<long> NonMatchingResult{};
141
  std::atomic<long> MatchingOutput{};
142
  std::atomic<long> NonMatchingOutput{};
143
} comparison;
144

145
Compiler::SourceFileCache em_parse_tree_cache( summary.profile );
146
Compiler::SourceFileCache inc_parse_tree_cache( summary.profile );
147

148
using DependencyInfo = std::map<fs::path, std::set<fs::path>>;
149
// Map owner (sources) -> dependencies
150
DependencyInfo owner_dependencies;
151
// Map dependency -> owners (sources)
152
DependencyInfo dependency_owners;
153

154
std::set<fs::path> compiled_dirs;
155

156
std::unique_ptr<Compiler::Compiler> create_compiler()
2,341✔
157
{
158
  auto compiler = std::make_unique<Compiler::Compiler>( em_parse_tree_cache, inc_parse_tree_cache,
159
                                                        summary.profile );
2,341✔
160
  return compiler;
2,341✔
161
}
162

163
void load_packages()
2,091✔
164
{
165
  // No need to load packages if only formatting sources
166
  if ( !format_source )
2,091✔
167
  {
168
    // Load and analyze the package structure
169
    for ( const auto& elem : compilercfg.PackageRoot )
2,954✔
170
    {
171
      Plib::load_packages( elem, true /* quiet */ );
1,477✔
172
    }
173
    Plib::replace_packages();
1,477✔
174
    Plib::check_package_deps();
1,477✔
175
  }
176
}
2,091✔
177

178
void compile_inc( const std::string& path )
×
179
{
180
  if ( !quiet )
×
181
    INFO_PRINTLN( "Compiling: {}", path );
×
182

183
  std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
×
184

185
  compiler->set_include_compile_mode();
×
186
  bool res = compiler->compile_file( path );
×
187

188
  if ( !res )
×
189
    throw std::runtime_error( "Error compiling file" );
×
190
}
×
191

192
std::vector<unsigned char> file_contents( const std::string& pathname, std::ios::openmode openmode )
×
193
{
194
  std::ifstream ifs( pathname, openmode );
×
195
  return std::vector<unsigned char>( std::istreambuf_iterator<char>( ifs ), {} );
×
196
}
×
197

198
std::vector<unsigned char> binary_contents( const std::string& pathname )
×
199
{
200
  std::ifstream input1( pathname, std::ios::binary );
×
201
  std::vector<unsigned char> buffer( std::istreambuf_iterator<char>( input1 ), {} );
×
202
  return buffer;
×
203
}
×
204

205
std::vector<std::string> instruction_filenames( const std::vector<unsigned>& ins_filenums,
×
206
                                                const std::vector<std::string>& filenames )
207
{
208
  std::vector<std::string> result;
×
209
  result.reserve( ins_filenums.size() );
×
210

211
  for ( auto& ins_filenum : ins_filenums )
×
212
  {
213
    result.push_back( filenames.at( ins_filenum ) );
×
214
  }
215
  return result;
×
216
}
×
217

218
bool format_file( const std::string& path )
614✔
219
{
220
  std::string ext( "" );
614✔
221

222
  std::string::size_type pos = path.rfind( '.' );
614✔
223
  if ( pos != std::string::npos )
614✔
224
    ext = path.substr( pos );
614✔
225

226
  if ( ext.compare( ".src" ) != 0 && ext.compare( ".inc" ) != 0 && ext.compare( ".em" ) != 0 )
614✔
227
  {
228
    ERROR_PRINTLN(
×
229
        "Didn't find '.src', '.inc', or '.em' extension on source filename '{}'! ..Ignoring",
230
        path );
231
    return true;
×
232
  }
233

234
  if ( !quiet )
614✔
235
    INFO_PRINTLN( "Formatting: {}", path );
×
236

237
  std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
614✔
238

239
  bool success =
240
      compiler->format_file( path.c_str(), ext.compare( ".em" ) == 0, format_source_inplace );
614✔
241

242
  if ( expect_compile_failure )
614✔
243
  {
244
    if ( !success )  // good, it failed
×
245
    {
246
      if ( !quiet )
×
247
        INFO_PRINTLN( "Formatting failed as expected." );
×
248
      return true;
×
249
    }
250
    else
251
    {
252
      throw std::runtime_error( "Formatting succeeded (-e indicates failure was expected)" );
×
253
    }
254
  }
255

256
  if ( !success )
614✔
257
    throw std::runtime_error( "Error formatting file" );
×
258
  return true;
614✔
259
}
614✔
260

261
void add_dependency_info( const fs::path& filepath_src,
133✔
262
                          std::set<fs::path>* removed_dependencies = nullptr,
263
                          std::set<fs::path>* new_dependencies = nullptr )
264
{
265
  auto filename_dep = fs::path( filepath_src ).replace_extension( ".dep" );
133✔
266

267
  auto& dependencies = owner_dependencies[filepath_src];
133✔
268
  auto previous_dependencies = dependencies;
133✔
269

270
  VERBOSE_PRINTLN( "Dependencies for: {}", filepath_src );
133✔
271
  for ( const auto& dependency : previous_dependencies )
245✔
272
  {
273
    if ( auto itr = dependency_owners.find( dependency ); itr != dependency_owners.end() )
112✔
274
    {
275
      // Remove deleted file from owners
276
      itr->second.erase( filepath_src );
112✔
277
      auto remaining = itr->second.size();
112✔
278
      VERBOSE_PRINTLN( "  - Removed {}, remaining owners={}", dependency, remaining );
112✔
279
      if ( remaining == 0 )
112✔
280
      {
281
        dependency_owners.erase( itr );
13✔
282
      }
283
    }
284
  }
285

286
  dependencies.clear();
133✔
287

288
  {
289
    std::ifstream ifs( filename_dep.c_str() );
133✔
290
    if ( ifs.is_open() )
133✔
291
    {
292
      std::string depname;
133✔
293
      while ( getline( ifs, depname ) )
1,093✔
294
      {
295
        fs::path depnamepath = fs::canonical( fs::path( depname ) );
960✔
296
        auto& owners = dependency_owners[depnamepath];
960✔
297
        // Add this source as a dependency by:
298
        // (1) placing `filename_src` in the set of owners for `depnamepath`.
299
        owners.emplace( filepath_src );
960✔
300
        // (2) placing `depnamepath` in the set of dependencies for this `filename_src`.
301
        dependencies.emplace( depnamepath );
960✔
302
        VERBOSE_PRINTLN( "  - Added {}, owners={}", depnamepath, owners.size() );
960✔
303
      }
960✔
304
    }
133✔
305

306
    // Could not load dependency information -- maybe failed compilation? We
307
    // still add this source as a dependency of itself, such that it will be
308
    // recompiled on next save.
309
    //
310
    // On failed compilation, the existing `.dep`
311
    // remains, so we will use that information for recompilation (eg. a source
312
    // failed to compile due to a bad include, changing the include should still
313
    // recompile the source.)
314
    else
315
    {
316
      dependency_owners[filepath_src] = std::set<fs::path>{ filepath_src };
×
317
      dependencies.emplace( filepath_src );
×
318
    }
319
  }
133✔
320

321
  if ( removed_dependencies && new_dependencies )
133✔
322
  {
323
    std::set_difference( previous_dependencies.begin(), previous_dependencies.end(),
14✔
324
                         dependencies.begin(), dependencies.end(),
325
                         std::inserter( *removed_dependencies, removed_dependencies->begin() ) );
326

327

328
    std::set_difference( dependencies.begin(), dependencies.end(), previous_dependencies.begin(),
14✔
329
                         previous_dependencies.end(),
330
                         std::inserter( *new_dependencies, new_dependencies->begin() ) );
331
  }
332
}
133✔
333

334
/**
335
 * Compiles the single given file (inc, src, hsr, asp), if needed
336
 *
337
 * Takes into account compilercfg.OnlyCompileUpdatedScripts and force_update
338
 *
339
 * @param path path of the file to be compiled
340
 * @return TRUE if the file was compiled, FALSE otherwise (eg. the file is up-to-date)
341
 */
342
bool compile_file( const std::string& path )
1,727✔
343
{
344
  std::string ext( "" );
1,727✔
345

346
  std::string::size_type pos = path.rfind( '.' );
1,727✔
347
  if ( pos != std::string::npos )
1,727✔
348
    ext = path.substr( pos );
1,727✔
349

350
  if ( !ext.compare( ".inc" ) )
1,727✔
351
  {
352
    compile_inc( path );
×
353
    return true;
×
354
  }
355

356
  if ( ext.compare( ".src" ) != 0 && ext.compare( ".hsr" ) != 0 && ext.compare( ".asp" ) != 0 )
1,727✔
357
  {
358
    ERROR_PRINTLN( "Didn't find '.src', '.hsr', or '.asp' extension on source filename '{}'!",
×
359
                   path );
360
    throw std::runtime_error( "Error in source filename" );
×
361
  }
362
  std::string fname = path;
1,727✔
363
  std::string filename_ecl = fname.replace( pos, 4, ".ecl" );
1,727✔
364
  std::string filename_lst = fname.replace( pos, 4, ".lst" );
1,727✔
365
  std::string filename_ast = fname.replace( pos, 4, ".ast" );
1,727✔
366
  std::string filename_dep = fname.replace( pos, 4, ".dep" );
1,727✔
367
  std::string filename_dbg = fname.replace( pos, 4, ".dbg" );
1,727✔
368

369
  if ( compilercfg.OnlyCompileUpdatedScripts && !force_update )
1,727✔
370
  {
371
    bool all_old = true;
1,608✔
372
    unsigned int ecl_timestamp = Clib::GetFileTimestamp( filename_ecl.c_str() );
1,608✔
373
    if ( Clib::GetFileTimestamp( path.c_str() ) >= ecl_timestamp )
1,608✔
374
    {
375
      if ( compilercfg.VerbosityLevel > 0 )
1,596✔
376
        INFO_PRINTLN( "{} is newer than {}", path, filename_ecl );
121✔
377
      all_old = false;
1,596✔
378
    }
379

380
    if ( all_old )
1,608✔
381
    {
382
      std::ifstream ifs( filename_dep.c_str() );
12✔
383
      // if the file doesn't exist, gotta build.
384
      if ( ifs.is_open() )
12✔
385
      {
386
        std::string depname;
11✔
387
        while ( getline( ifs, depname ) )
95✔
388
        {
389
          if ( Clib::GetFileTimestamp( depname.c_str() ) >= ecl_timestamp )
95✔
390
          {
391
            if ( compilercfg.VerbosityLevel > 0 )
11✔
392
              INFO_PRINTLN( "{} is newer than {}", depname, filename_ecl );
11✔
393
            all_old = false;
11✔
394
            break;
11✔
395
          }
396
        }
397
      }
11✔
398
      else
399
      {
400
        if ( compilercfg.VerbosityLevel > 0 )
1✔
401
          INFO_PRINTLN( "{} does not exist.", filename_dep );
1✔
402
        all_old = false;
1✔
403
      }
404
    }
12✔
405
    if ( all_old )
1,608✔
406
    {
407
      if ( !quiet && compilercfg.DisplayUpToDateScripts )
×
408
        INFO_PRINTLN( "{} is up-to-date.", filename_ecl );
×
409
      return false;
×
410
    }
411
  }
412

413

414
  {
415
    if ( !quiet )
1,727✔
416
      INFO_PRINTLN( "Compiling: {}", path );
252✔
417

418
    std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
1,727✔
419

420
    bool success = compiler->compile_file( path.c_str() );
1,727✔
421

422
    em_parse_tree_cache.keep_some();
1,727✔
423
    inc_parse_tree_cache.keep_some();
1,727✔
424

425
    if ( expect_compile_failure )
1,727✔
426
    {
427
      if ( !success )  // good, it failed
×
428
      {
429
        if ( !quiet )
×
430
          INFO_PRINTLN( "Compilation failed as expected." );
×
431
        return true;
×
432
      }
433
      else
434
      {
435
        throw std::runtime_error( "Compilation succeeded (-e indicates failure was expected)" );
×
436
      }
437
    }
438

439
    if ( !success )
1,727✔
440
      throw std::runtime_error( "Error compiling file" );
252✔
441

442

443
    if ( !quiet )
1,475✔
444
      INFO_PRINTLN( "Writing:   {}", filename_ecl );
248✔
445

446
    if ( !compiler->write_ecl( filename_ecl ) )
1,475✔
447
    {
448
      throw std::runtime_error( "Error writing output file" );
×
449
    }
450

451
    if ( compilercfg.GenerateListing )
1,475✔
452
    {
453
      if ( !quiet )
1,227✔
454
        INFO_PRINTLN( "Writing:   {}", filename_lst );
×
455
      compiler->write_listing( filename_lst );
1,227✔
456
    }
457
    else if ( Clib::FileExists( filename_lst.c_str() ) )
248✔
458
    {
459
      if ( !quiet )
×
460
        INFO_PRINTLN( "Deleting:  {}", filename_lst );
×
461
      Clib::RemoveFile( filename_lst );
×
462
    }
463

464
    if ( compilercfg.GenerateAbstractSyntaxTree )
1,475✔
465
    {
466
      if ( !quiet )
1✔
NEW
467
        INFO_PRINTLN( "Writing:   {}", filename_ast );
×
468
      compiler->write_string_tree( filename_ast );
1✔
469
    }
470
    else if ( Clib::FileExists( filename_ast.c_str() ) )
1,474✔
471
    {
NEW
472
      if ( !quiet )
×
NEW
473
        INFO_PRINTLN( "Deleting:  {}", filename_ast );
×
NEW
474
      Clib::RemoveFile( filename_ast );
×
475
    }
476

477
    if ( compilercfg.GenerateDebugInfo )
1,475✔
478
    {
479
      if ( !quiet )
1,346✔
480
      {
481
        INFO_PRINTLN( "Writing:   {}", filename_dbg );
119✔
482
        if ( compilercfg.GenerateDebugTextInfo )
119✔
483
          INFO_PRINTLN( "Writing:   {}.txt", filename_dbg );
×
484
      }
485
      compiler->write_dbg( filename_dbg, compilercfg.GenerateDebugTextInfo );
1,346✔
486
    }
487
    else if ( Clib::FileExists( filename_dbg.c_str() ) )
129✔
488
    {
489
      if ( !quiet )
119✔
490
        INFO_PRINTLN( "Deleting:  {}", filename_dbg );
119✔
491
      Clib::RemoveFile( filename_dbg );
119✔
492
    }
493

494
    if ( compilercfg.GenerateDependencyInfo )
1,475✔
495
    {
496
      if ( !quiet )
129✔
497
        INFO_PRINTLN( "Writing:   {}", filename_dep );
129✔
498
      compiler->write_included_filenames( filename_dep );
129✔
499
    }
500
    else if ( Clib::FileExists( filename_dep.c_str() ) )
1,346✔
501
    {
502
      if ( !quiet )
×
503
        INFO_PRINTLN( "Deleting:  {}", filename_dep );
×
504
      Clib::RemoveFile( filename_dep );
×
505
    }
506
  }
1,727✔
507
  return true;
1,475✔
508
}
3,239✔
509

510
bool process_file( const std::string& path )
2,341✔
511
{
512
  if ( format_source )
2,341✔
513
    return format_file( path );
614✔
514
  return compile_file( path );
1,727✔
515
}
516

517
void process_file_wrapper( const std::string& path,
2,341✔
518
                           std::set<fs::path>* removed_dependencies = nullptr,
519
                           std::set<fs::path>* new_dependencies = nullptr )
520
{
521
  try
522
  {
523
    if ( process_file( path ) )
2,341✔
524
      ++summary.CompiledScripts;
2,089✔
525
    else
526
      ++summary.UpToDateScripts;
×
527
  }
528
  catch ( std::exception& )
252✔
529
  {
530
    ++summary.CompiledScripts;
252✔
531
    ++summary.ScriptsWithCompileErrors;
252✔
532
    if ( !keep_building )
252✔
533
      throw;
248✔
534
  }
252✔
535

536
  if ( watch_source )
2,093✔
537
  {
538
    fs::path filepath = fs::canonical( fs::path( path ) );
133✔
539
    auto ext = filepath.extension().generic_string();
133✔
540
    if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
133✔
541
    {
542
      add_dependency_info( filepath, removed_dependencies, new_dependencies );
133✔
543
    }
544
  }
133✔
545
}
2,093✔
546

547
bool setting_value( const char* arg )
1,477✔
548
{
549
  // format of arg is -C or -C-
550
  if ( arg[2] == '\0' )
1,477✔
551
    return true;
1,477✔
552
  else if ( arg[2] == '-' )
×
553
    return false;
×
554
  else if ( arg[2] == '+' )
×
555
    return true;
×
556
  else
557
    return true;
×
558
}
559

560

561
int readargs( int argc, char** argv )
2,091✔
562
{
563
  bool unknown_opt = false;
2,091✔
564

565
  for ( int i = 1; i < argc; i++ )
10,453✔
566
  {
567
    const char* arg = argv[i];
8,362✔
568
#ifdef __linux__
569
    if ( arg[0] == '-' )
8,362✔
570
#else
571
    if ( arg[0] == '/' || arg[0] == '-' )
572
#endif
573
    {
574
      switch ( arg[1] )
6,273✔
575
      {
576
      case 'W':
1✔
577
        watch_source = true;
1✔
578
        compilercfg.GenerateDependencyInfo = true;
1✔
579
        keep_building = true;
1✔
580
        compilercfg.ThreadedCompilation =
1✔
581
            false;  // limitation since dependency gathering is not thread-safe
582
        break;
1✔
583

584
      case 'A':  // skip it at this point.
2✔
585
        break;
2✔
586

587
      case 'c':
×
588
        compilercfg.ErrorOnFileCaseMissmatch = setting_value( arg );
×
589
        break;
×
590

591
      case 'C':  // skip it at this point.
2,089✔
592
        ++i;     // and skip its parameter.
2,089✔
593
        break;
2,089✔
594

595
      case 'd':
×
596
        compilercfg.DisplayDebugs = setting_value( arg );
×
597
        break;
×
598

599
      case 'D':
×
600
        compilercfg.GenerateDependencyInfo = setting_value( arg );
×
601
        break;
×
602

603
      case 'e':
×
604
        expect_compile_failure = true;
×
605
        break;
×
606

607
#ifdef WIN32
608
      case 'E':
609
      {
610
        std::string path = &argv[i][2];
611
        CfgPathEnv = "ECOMPILE_CFG_PATH=" + path;
612
        _putenv( CfgPathEnv.c_str() );
613
      }
614
      break;
615
#endif
616

617
      case 'g':
×
618
      {
619
        auto value = setting_value( arg );
×
620
        if ( !value )
×
621
        {
622
          INFO_PRINTLN( "The OG Compiler has been removed." );
×
623
          unknown_opt = true;
×
624
        }
625
        break;
×
626
      }
627

628
      case 'q':
2,089✔
629
        quiet = true;
2,089✔
630
        break;
2,089✔
631

632
      case 'w':
×
633
        compilercfg.DisplayWarnings = setting_value( arg );
×
634
        if ( argv[i][2] == 'P' )
×
635
          compilercfg.ParanoiaWarnings = true;
×
636
        break;
×
637
      case 'y':
×
638
        compilercfg.ErrorOnWarning = setting_value( arg );
×
639
        break;
×
640

641
      case 'l':
1,475✔
642
        compilercfg.GenerateListing = setting_value( arg );
1,475✔
643
        break;
1,475✔
644

645
      case 'i':
×
646
        include_debug = 1;
×
647
        break;
×
648

649
      case 'r':  // -r[i] [dir] is okay
×
650
        // Only suboption allowed is '-ri' (.inc recurse)
651
        if ( argv[i][2] && argv[i][2] != 'i' )
×
652
        {
653
          // BZZZZT! *error*
654
          unknown_opt = true;
×
655
          break;
×
656
        }
657

658
// Only skip next parameter if it's not an option!!
659
#ifdef __linux__
660
        if ( i + 1 < argc && argv[i + 1][0] != '-' )
×
661
#else
662
        if ( i + 1 < argc && argv[i + 1][0] != '/' && argv[i + 1][0] != '-' )
663
#endif
664
          ++i;
×
665
        break;
×
666

667
      case 't':
×
668
        show_timing_details = true;
×
669
        if ( argv[i][2] == 'v' )
×
670
          timing_quiet_override = true;
×
671
        break;
×
672

673
      case 's':
×
674
        // show_source = true;
675
        compilercfg.DisplaySummary = true;
×
676
        break;
×
677

678
      case 'a':
×
679
        compilercfg.CompileAspPages = setting_value( arg );
×
680
        break;
×
681

682
      case 'm':
×
683
        compilercfg.OptimizeObjectMembers = false;
×
684
        break;
×
685

686
      case 'b':
×
687
        keep_building = true;
×
688
        break;
×
689

690
      case 'u':
×
691
        compilercfg.OnlyCompileUpdatedScripts = setting_value( arg );
×
692
        if ( compilercfg.OnlyCompileUpdatedScripts )
×
693
          compilercfg.GenerateDependencyInfo = true;
×
694
        break;
×
695

696
      case 'F':
614✔
697
      {
698
        if ( argv[i][2] && argv[i][2] == 'i' )
614✔
699
          format_source_inplace = true;
614✔
700
        format_source = true;
614✔
701
        break;
614✔
702
      }
703

704
      case 'f':
×
705
        force_update = true;
×
706
        break;
×
707

708
      case 'v':
1✔
709
        int vlev;
710
        vlev = atoi( &argv[i][2] );
1✔
711
        if ( !vlev )
1✔
712
          vlev = 1;
1✔
713
        compilercfg.VerbosityLevel = vlev;
1✔
714
        break;
1✔
715

716
      case 'x':
1✔
717
        compilercfg.GenerateDebugInfo = setting_value( arg );
1✔
718
        compilercfg.GenerateDebugTextInfo = ( argv[i][2] == 't' );
1✔
719
        break;
1✔
720

721
      case 'Z':
1✔
722
        compilercfg.GenerateAbstractSyntaxTree = setting_value( arg );
1✔
723
        break;
1✔
724

725
#ifdef WIN32
726
      case 'P':
727
      {
728
        std::string path = &argv[i][2];
729
        EmPathEnv = "ECOMPILE_PATH_EM=" + path;
730
        IncPathEnv = "ECOMPILE_PATH_INC=" + path;
731
        _putenv( EmPathEnv.c_str() );
732
        _putenv( IncPathEnv.c_str() );
733
      }
734
      break;
735
#endif
736
      case 'T':
×
737
      {
738
        compilercfg.ThreadedCompilation = true;
×
739
        int count = atoi( &argv[i][2] );
×
740
        compilercfg.NumberOfThreads = count;
×
741
        break;
×
742
      }
743
      default:
×
744
        unknown_opt = true;
×
745
        break;
×
746
      }
747
    }
748

749
    if ( unknown_opt )
8,362✔
750
    {
751
      ERROR_PRINTLN( "Unknown option: {}", argv[i] );
×
752
      return 1;
×
753
    }
754
  }
755
  return 0;
2,091✔
756
}
757

758
void apply_configuration()
2,091✔
759
{
760
  em_parse_tree_cache.configure( compilercfg.EmParseTreeCacheSize );
2,091✔
761
  inc_parse_tree_cache.configure( compilercfg.IncParseTreeCacheSize );
2,091✔
762
}
2,091✔
763

764
void recurse_call( const std::vector<fs::path>& basedirs, bool inc_files,
2✔
765
                   const std::function<void( const std::string& )>& callback )
766
{
767
  std::set<std::string> files;
2✔
768
  for ( const auto& basedir : basedirs )
8✔
769
  {
770
    if ( !fs::is_directory( basedir ) )
6✔
771
      continue;
×
772
    std::error_code ec;
6✔
773
    for ( auto dir_itr = fs::recursive_directory_iterator( basedir, ec );
6✔
774
          dir_itr != fs::recursive_directory_iterator(); ++dir_itr )
1,724✔
775
    {
776
      if ( Clib::exit_signalled )
859✔
777
        return;
×
778
      if ( auto fn = dir_itr->path().filename().u8string(); !fn.empty() && *fn.begin() == '.' )
859✔
779
      {
780
        if ( dir_itr->is_directory() )
4✔
781
          dir_itr.disable_recursion_pending();
2✔
782
        continue;
4✔
783
      }
784
      else if ( !dir_itr->is_regular_file() )
855✔
785
        continue;
859✔
786
      const auto ext = dir_itr->path().extension();
756✔
787
      const auto file = dir_itr->path().generic_u8string();
756✔
788
      if ( inc_files )
756✔
789
      {
790
        if ( !ext.compare( ".inc" ) )
×
791
          if ( files.insert( file ).second )
×
792
            callback( file );
×
793
      }
794
      else if ( !ext.compare( ".src" ) || !ext.compare( ".hsr" ) ||
1,272✔
795
                ( compilercfg.CompileAspPages && !ext.compare( ".asp" ) ) )
516✔
796
      {
797
        if ( files.insert( file ).second )
240✔
798
          callback( file );
238✔
799
      }
800
    }
762✔
801
  }
802
}
2✔
803

804
void process_dirs( const std::vector<fs::path>& dirs, bool compile_inc )
2✔
805
{
806
  if ( !compilercfg.ThreadedCompilation )
2✔
807
  {
808
    recurse_call( dirs, compile_inc,
2✔
809
                  []( const std::string& file ) { process_file_wrapper( file ); } );
238✔
810
    return;
2✔
811
  }
812
  std::atomic<unsigned> compiled_scripts( 0 );
×
813
  std::atomic<unsigned> uptodate_scripts( 0 );
×
814
  std::atomic<unsigned> error_scripts( 0 );
×
815
  std::atomic<bool> par_keep_building( true );
×
816
  {
817
    unsigned int thread_count = std::max( 2u, std::thread::hardware_concurrency() * 2 );
×
818
    if ( compilercfg.NumberOfThreads )
×
819
      thread_count = static_cast<unsigned>( compilercfg.NumberOfThreads );
×
820
    threadhelp::TaskThreadPool pool( thread_count, "ecompile" );
×
821
    summary.ThreadCount = pool.size();
×
822
    auto callback = [&]( const std::string& file )
×
823
    {
824
      pool.push(
×
825
          [&, file]()
×
826
          {
827
            if ( !par_keep_building || Clib::exit_signalled )
×
828
              return;
×
829
            try
830
            {
831
              if ( process_file( file ) )
×
832
                ++compiled_scripts;
×
833
              else
834
                ++uptodate_scripts;
×
835
            }
836
            catch ( std::exception& e )
×
837
            {
838
              ++compiled_scripts;
×
839
              ++error_scripts;
×
840
              ERROR_PRINTLN( "failed to compile {}: {}", file, e.what() );
×
841
              if ( !keep_building )
×
842
                par_keep_building = false;
×
843
            }
×
844
            catch ( ... )
×
845
            {
846
              par_keep_building = false;
×
847
              Clib::force_backtrace();
×
848
            }
×
849
          } );
850
    };
×
851
    recurse_call( dirs, compile_inc, callback );
×
852
  }
×
853
  summary.CompiledScripts = compiled_scripts;
×
854
  summary.UpToDateScripts = uptodate_scripts;
×
855
  summary.ScriptsWithCompileErrors = error_scripts;
×
856
}
857

858
void DisplaySummary( const Tools::Timer<>& timer )
7✔
859
{
860
  std::string tmp = "Compilation Summary:\n";
7✔
861
  if ( summary.ThreadCount )
7✔
862
    tmp += fmt::format( "    Used {} threads\n", summary.ThreadCount );
×
863
  if ( summary.CompiledScripts )
7✔
864
    tmp += fmt::format( "    Compiled {} script{} in {} ms.\n", summary.CompiledScripts,
7✔
865
                        ( summary.CompiledScripts == 1 ? "" : "s" ), timer.ellapsed() );
21✔
866

867
  if ( summary.ScriptsWithCompileErrors )
7✔
868
    tmp += fmt::format( "    {} of those script{} had errors.\n", summary.ScriptsWithCompileErrors,
1✔
869
                        ( summary.ScriptsWithCompileErrors == 1 ? "" : "s" ) );
3✔
870

871
  if ( summary.UpToDateScripts )
7✔
872
    tmp += fmt::format( "    {} script{} already up-to-date.\n", summary.UpToDateScripts,
×
873
                        ( summary.UpToDateScripts == 1 ? " was" : "s were" ) );
×
874

875
  if ( show_timing_details )
7✔
876
  {
877
    tmp += fmt::format( "    build workspace: {}\n",
×
878
                        (long long)summary.profile.build_workspace_micros / 1000 );
×
879
    tmp += fmt::format( "        - load *.em:   {}\n",
×
880
                        (long long)summary.profile.load_em_micros / 1000 );
×
881
    tmp += fmt::format( "       - parse *.em:   {} ({})\n",
×
882
                        (long long)summary.profile.parse_em_micros / 1000,
×
883
                        (long)summary.profile.parse_em_count );
×
884
    tmp += fmt::format( "         - ast *.em:   {}\n",
×
885
                        (long long)summary.profile.ast_em_micros / 1000 );
×
886
    tmp += fmt::format( "      - parse *.inc:   {} ({})\n",
×
887
                        (long long)summary.profile.parse_inc_micros / 1000,
×
888
                        (long)summary.profile.parse_inc_count );
×
889
    tmp += fmt::format( "        - ast *.inc:   {}\n",
×
890
                        (long long)summary.profile.ast_inc_micros / 1000 );
×
891
    tmp += fmt::format( "      - parse *.src:   {} ({})\n",
×
892
                        (long long)summary.profile.parse_src_micros / 1000,
×
893
                        (long)summary.profile.parse_src_count );
×
894
    tmp += fmt::format( "        - ast *.src:   {}\n",
×
895
                        (long long)summary.profile.ast_src_micros / 1000 );
×
896
    tmp += fmt::format( "  resolve functions:   {}\n",
×
897
                        (long long)summary.profile.ast_resolve_functions_micros / 1000 );
×
898
    tmp += fmt::format( " register constants: {}\n",
×
899
                        (long long)summary.profile.register_const_declarations_micros / 1000 );
×
900
    tmp += fmt::format( "            analyze: {}\n",
×
901
                        (long long)summary.profile.analyze_micros / 1000 );
×
902
    tmp += fmt::format( "           optimize: {}\n",
×
903
                        (long long)summary.profile.optimize_micros / 1000 );
×
904
    tmp += fmt::format( "       disambiguate: {}\n",
×
905
                        (long long)summary.profile.disambiguate_micros / 1000 );
×
906
    tmp += fmt::format( "      generate code: {}\n",
×
907
                        (long long)summary.profile.codegen_micros / 1000 );
×
908
    tmp += fmt::format( "  prune cache (sel): {}\n",
×
909
                        (long long)summary.profile.prune_cache_select_micros / 1000 );
×
910
    tmp += fmt::format( "  prune cache (del): {}\n",
×
911
                        (long long)summary.profile.prune_cache_delete_micros / 1000 );
×
912
    tmp += "\n";
×
913
    tmp += fmt::format( "      - ambiguities: {}\n", (long)summary.profile.ambiguities );
×
914
    tmp += fmt::format( "       - cache hits: {}\n", (long)summary.profile.cache_hits );
×
915
    tmp += fmt::format( "     - cache misses: {}\n", (long)summary.profile.cache_misses );
×
916
  }
917

918
  INFO_PRINTLN( tmp );
7✔
919
}
7✔
920

921
void EnterWatchMode()
1✔
922
{
923
  std::list<WatchFileMessage> watch_messages;
1✔
924
  std::set<fs::path> to_compile;
1✔
925
  efsw::FileWatcher fileWatcher;
1✔
926
  EfswFileWatchListener listener( fileWatcher,
927
                                  std::set<fs::path>{ ".src", ".hsr", ".asp", ".inc", ".em" } );
6✔
928

929
  auto handle_compile_file = [&]( const fs::path& filepath )
5✔
930
  {
931
    // Existing file, compile all owners dependent on it.
932
    if ( auto itr = dependency_owners.find( filepath ); itr != dependency_owners.end() )
5✔
933
    {
934
      to_compile.insert( itr->second.begin(), itr->second.end() );
4✔
935
    }
936
    // New file, compile only this file (if it is compilable).
937
    else
938
    {
939
      auto ext = filepath.extension();
1✔
940
      if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
1✔
941
      {
942
        to_compile.emplace( filepath );
1✔
943
      }
944
    }
1✔
945
  };
5✔
946

947
  auto handle_delete_file = [&]( const fs::path& filepath )
1✔
948
  {
949
    to_compile.erase( filepath );
1✔
950
    if ( auto itr = owner_dependencies.find( filepath ); itr != owner_dependencies.end() )
1✔
951
    {
952
      // For all dependencies, remove the deleted file from set of owners for
953
      // that dependency.
954
      for ( const auto& depnamepath : itr->second )
4✔
955
      {
956
        // If dependency exists in map of dependency -> owners
957
        if ( auto owners_itr = dependency_owners.find( depnamepath );
3✔
958
             owners_itr != dependency_owners.end() )
3✔
959
        {
960
          // Erase filepath from set of owners
961
          owners_itr->second.erase( filepath );
3✔
962

963
          // If owners set is empty, this dependency is no longer used by
964
          // anything.
965
          if ( owners_itr->second.empty() )
3✔
966
          {
967
            //  Erase from map of dependency -> owners.
968
            dependency_owners.erase( owners_itr );
1✔
969
            listener.remove_file( depnamepath );
1✔
970
            VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
1✔
971
          }
972
        }
973
      }
974
      owner_dependencies.erase( itr );
1✔
975
    }
976
  };
1✔
977

978
  auto add_dir = [&]( const std::string& elem )
4✔
979
  {
980
    fs::path dir( elem );
4✔
981
    if ( fs::exists( dir ) )
4✔
982
    {
983
      auto dirpath = fs::canonical( dir );
4✔
984
      if ( listener.add_watch_dir( dirpath ) )
4✔
985
      {
986
        VERBOSE_PRINTLN( "Add watch dir {}", dirpath );
4✔
987
      }
988
    }
4✔
989
  };
4✔
990

991
  for ( const auto& dep_owners : dependency_owners )
140✔
992
  {
993
    listener.add_file( dep_owners.first );
139✔
994
  }
995

996
  for ( const auto& directory : compiled_dirs )
4✔
997
  {
998
    listener.add_dir( directory );
3✔
999
  }
1000

1001
  for ( const auto& elem : compilercfg.PackageRoot )
2✔
1002
  {
1003
    add_dir( elem );
1✔
1004
  }
1005

1006
  add_dir( compilercfg.ModuleDirectory );
1✔
1007
  add_dir( compilercfg.IncludeDirectory );
1✔
1008
  add_dir( compilercfg.PolScriptRoot );
1✔
1009

1010
  fileWatcher.watch();
1✔
1011

1012
  INFO_PRINTLN( "Entering watch mode. Watching {} files and {} directories. Ctrl-C to stop...",
1✔
1013
                dependency_owners.size(), compiled_dirs.size() );
1✔
1014

1015
  while ( !Clib::exit_signalled )
8✔
1016
  {
1017
    std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
6✔
1018
    listener.take_messages( watch_messages );
6✔
1019
    if ( !watch_messages.empty() )
6✔
1020
    {
1021
      for ( auto const& message : watch_messages )
11✔
1022
      {
1023
        const auto& filepath = message.filepath;
6✔
1024
        const auto& old_filepath = message.old_filepath;
6✔
1025
        VERBOSE_PRINTLN( "Event: filename={}, old_filename={}", filepath, old_filepath );
6✔
1026

1027
        // created or modified
1028
        if ( old_filepath.empty() )
6✔
1029
        {
1030
          handle_compile_file( filepath );
5✔
1031
        }
1032
        // deleted
1033
        else if ( filepath.empty() )
1✔
1034
        {
1035
          handle_delete_file( old_filepath );
1✔
1036
        }
1037
        // moved
1038
        else
1039
        {
1040
          handle_delete_file( old_filepath );
×
1041
          handle_compile_file( filepath );
×
1042
        }
1043
      }
1044
      watch_messages.clear();
5✔
1045

1046
      if ( !to_compile.empty() )
5✔
1047
      {
1048
        summary.CompiledScripts = 0;
5✔
1049
        summary.UpToDateScripts = 0;
5✔
1050
        summary.ScriptsWithCompileErrors = 0;
5✔
1051
        em_parse_tree_cache.clear();
5✔
1052
        inc_parse_tree_cache.clear();
5✔
1053

1054
        VERBOSE_PRINTLN( "To compile: {}", to_compile );
5✔
1055

1056
        Tools::Timer<> timer;
5✔
1057
        for ( const auto& filepath : to_compile )
19✔
1058
        {
1059
          if ( Clib::exit_signalled )
14✔
1060
          {
1061
            break;
×
1062
          }
1063

1064
          std::set<fs::path> removed_dependencies;
14✔
1065
          std::set<fs::path> new_dependencies;
14✔
1066
          try
1067
          {
1068
            process_file_wrapper( filepath.generic_string(), &removed_dependencies,
14✔
1069
                                  &new_dependencies );
1070
          }
1071
          catch ( ... )
×
1072
          {
1073
          }
×
1074

1075
          VERBOSE_PRINTLN( "New dependencies: {} Removed dependencies: {}", new_dependencies,
14✔
1076
                           removed_dependencies );
1077

1078
          for ( const auto& depnamepath : removed_dependencies )
14✔
1079
          {
1080
            // If a removed dependency has no owner set is empty, we can remove
1081
            // the listener for that dependency.
1082
            if ( dependency_owners.find( depnamepath ) == dependency_owners.end() )
×
1083
            {
1084
              if ( listener.remove_file( depnamepath ) )
×
1085
              {
1086
                VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
×
1087
              }
1088
            }
1089
          }
1090

1091
          for ( const auto& depnamepath : new_dependencies )
17✔
1092
          {
1093
            if ( listener.add_file( depnamepath ) )
3✔
1094
            {
1095
              VERBOSE_PRINTLN( "Add watch file {}", depnamepath );
1✔
1096
            }
1097
          }
1098
        }
14✔
1099

1100
        timer.stop();
5✔
1101
        if ( compilercfg.DisplaySummary && !quiet )
5✔
1102
        {
1103
          DisplaySummary( timer );
5✔
1104
        }
1105
        to_compile.clear();
5✔
1106
      }
5✔
1107
    }
1108
  }
1109
}
2✔
1110

1111
/**
1112
 * Runs the compilation threads
1113
 */
1114
void AutoCompile()
2✔
1115
{
1116
  bool save = compilercfg.OnlyCompileUpdatedScripts;
2✔
1117
  compilercfg.OnlyCompileUpdatedScripts = compilercfg.UpdateOnlyOnAutoCompile;
2✔
1118
  std::vector<fs::path> dirs;
2✔
1119
  compiled_dirs.emplace( fs::canonical( compilercfg.PolScriptRoot ) );
2✔
1120

1121
  dirs.emplace_back( compilercfg.PolScriptRoot );
2✔
1122
  for ( const auto& pkg : Plib::systemstate.packages )
6✔
1123
  {
1124
    compiled_dirs.emplace( fs::canonical( pkg->dir() ) );
4✔
1125
    dirs.emplace_back( pkg->dir() );
4✔
1126
  }
1127
  process_dirs( dirs, false );
2✔
1128
  compilercfg.OnlyCompileUpdatedScripts = save;
2✔
1129
}
2✔
1130

1131
/**
1132
 * Takes decisions, runs, the compilation, prints summary and cleans after
1133
 */
1134
bool run( int argc, char** argv, int* res )
2,091✔
1135
{
1136
  Clib::enable_exit_signaller();
2,091✔
1137

1138
  load_packages();
2,091✔
1139

1140
  // Determine the run mode and do the compile itself
1141
  Tools::Timer<> timer;
2,091✔
1142
  bool any = false;
2,091✔
1143

1144
  for ( int i = 1; i < argc; i++ )
10,205✔
1145
  {
1146
#ifdef __linux__
1147
    if ( argv[i][0] == '-' )
8,362✔
1148
#else
1149
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
1150
#endif
1151
    {
1152
      if ( argv[i][1] == 'A' )
6,273✔
1153
      {
1154
        compilercfg.UpdateOnlyOnAutoCompile = ( argv[i][2] == 'u' );
2✔
1155
        any = true;
2✔
1156

1157
        AutoCompile();
2✔
1158
      }
1159
      else if ( argv[i][1] == 'r' )
6,271✔
1160
      {
1161
        any = true;
×
1162
        std::string dir( "." );
×
1163
        bool compile_inc = ( argv[i][2] == 'i' );  // compile .inc files
×
1164

1165
        ++i;
×
1166
        if ( i < argc && argv[i] && argv[i][0] != '-' )
×
1167
          dir.assign( argv[i] );
×
1168

1169
        compiled_dirs.emplace( fs::canonical( dir ) );
×
1170
        process_dirs( { dir }, compile_inc );
×
1171
      }
×
1172
      else if ( argv[i][1] == 'C' )
6,271✔
1173
      {
1174
        ++i;  // skip the config file pathname
2,089✔
1175
      }
1176
      // and skip any other option.
1177
    }
1178
    else
1179
    {
1180
      any = true;
2,089✔
1181
#ifdef _WIN32
1182
      Clib::forspec( argv[i], []( const char* pathname ) { process_file_wrapper( pathname ); } );
1183
#else
1184
      process_file_wrapper( argv[i] );
2,585✔
1185
#endif
1186
    }
1187
  }
1188

1189
  if ( !any && compilercfg.AutoCompileByDefault )
1,843✔
1190
  {
1191
    any = true;
×
1192
    AutoCompile();
×
1193
  }
1194

1195
  // Execution is completed: start final/cleanup tasks
1196
  timer.stop();
1,843✔
1197

1198
  if ( any && compilercfg.DisplaySummary && !quiet )
1,843✔
1199
  {
1200
    DisplaySummary( timer );
2✔
1201
  }
1202

1203
  if ( watch_source )
1,843✔
1204
  {
1205
    EnterWatchMode();
1✔
1206
  }
1207

1208
  Plib::systemstate.deinitialize();
1,843✔
1209

1210
  if ( summary.ScriptsWithCompileErrors )
1,843✔
1211
    *res = 1;
×
1212
  else
1213
    *res = 0;
1,843✔
1214
  return any;
1,843✔
1215
}
2,091✔
1216

1217
void read_config_file( int argc, char* argv[] )
2,091✔
1218
{
1219
  for ( int i = 1; i < argc; i++ )
6,275✔
1220
  {
1221
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
6,273✔
1222
    {
1223
      // -C cfgpath
1224
      if ( argv[i][1] == 'C' )
6,273✔
1225
      {
1226
        ++i;
2,089✔
1227
        if ( i == argc )
2,089✔
1228
          throw std::runtime_error( "-C specified without pathname" );
×
1229

1230
        compilercfg.Read( std::string( argv[i] ) );
2,089✔
1231
        return;
2,089✔
1232
      }
1233
    }
1234
  }
1235

1236
  // check ECOMPILE_CFG_PATH environment variable
1237
  const char* env_ecompile_cfg_path = getenv( "ECOMPILE_CFG_PATH" );
2✔
1238
  if ( env_ecompile_cfg_path != nullptr )
2✔
1239
  {
1240
    compilercfg.Read( std::string( env_ecompile_cfg_path ) );
×
1241
    return;
×
1242
  }
1243

1244
  // no -C arg, so use binary path (hope it's right..sigh.)
1245
  std::string cfgpath = PROG_CONFIG::programDir() + "ecompile.cfg";
2✔
1246
  if ( Clib::FileExists( "ecompile.cfg" ) )
2✔
1247
  {
1248
    compilercfg.Read( "ecompile.cfg" );
2✔
1249
  }
1250
  else if ( Clib::FileExists( cfgpath ) )
×
1251
  {
1252
    compilercfg.Read( cfgpath );
×
1253
  }
1254
  else
1255
  {
1256
    ERROR_PRINTLN( "Could not find {}; using defaults.", cfgpath );
×
1257
    compilercfg.SetDefaults();
×
1258
  }
1259
}
2✔
1260

1261
/**
1262
 * This is the main entry point for ecompile program
1263
 */
1264
int ECompileMain::main()
2,091✔
1265
{
1266
  Clib::Logging::global_logger->disableFileLog();
2,091✔
1267

1268
  const std::vector<std::string>& binArgs = programArgs();
2,091✔
1269

1270
/**********************************************
1271
 * TODO: rework the following cruft from former uotool.cpp
1272
 **********************************************/
1273
#ifdef _WIN32
1274
  Clib::MiniDumper::Initialize();
1275
#endif
1276

1277
  ECompile::read_config_file( s_argc, s_argv );
2,091✔
1278

1279
  /**********************************************
1280
   * show help
1281
   **********************************************/
1282
  if ( binArgs.size() == 1 && !compilercfg.AutoCompileByDefault )
2,091✔
1283
  {
1284
    showHelp();
×
1285
    return 0;  // return "okay"
×
1286
  }
1287

1288
  watch_source = compilercfg.WatchModeByDefault;
2,091✔
1289
  if ( watch_source )
2,091✔
1290
  {
1291
    compilercfg.GenerateDependencyInfo = true;
×
1292
    keep_building = true;
×
1293
    compilercfg.ThreadedCompilation =
×
1294
        false;  // limitation since dependency gathering is not thread-safe
1295
  }
1296

1297
  int res = ECompile::readargs( s_argc, s_argv );
2,091✔
1298
  if ( res )
2,091✔
1299
  {
1300
    showHelp();
×
1301
    return res;
×
1302
  }
1303

1304
  ECompile::apply_configuration();
2,091✔
1305

1306
  if ( !ECompile::quiet )
2,091✔
1307
  {
1308
    // vX.YY
1309
    INFO_PRINTLN( "EScript Compiler v1.{}\n{}\n", ESCRIPT_FILE_VER_CURRENT, POL_COPYRIGHT );
2✔
1310
  }
1311

1312
  int prog_res = 1;
2,091✔
1313
  bool didanything = ECompile::run( s_argc, s_argv, &prog_res );
2,091✔
1314

1315
  if ( !didanything )
1,843✔
1316
  {
1317
    showHelp();
×
1318
    return 1;
×
1319
  }
1320
  return prog_res;
1,843✔
1321
}
1322
}  // namespace ECompile
1323
}  // namespace Pol
1324

1325
///////////////////////////////////////////////////////////////////////////////
1326
///////////////////////////////////////////////////////////////////////////////
1327
///////////////////////////////////////////////////////////////////////////////
1328

1329
int main( int argc, char* argv[] )
2,091✔
1330
{
1331
  Pol::ECompile::s_argc = argc;
2,091✔
1332
  Pol::ECompile::s_argv = argv;
2,091✔
1333

1334
  Pol::ECompile::ECompileMain* ECompileMain = new Pol::ECompile::ECompileMain();
2,091✔
1335
  ECompileMain->start( argc, argv );
2,091✔
1336
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc