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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

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

62.36
/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/clib.h"
26
#include "clib/esignal.h"
27
#include "clib/fileutil.h"
28
#include "clib/logfacility.h"
29
#include "clib/mdump.h"
30
#include "clib/passert.h"
31
#include "clib/threadhelp.h"
32
#include "clib/timer.h"
33
#include "plib/pkg.h"
34
#include "plib/systemstate.h"
35

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

40

41
namespace Pol::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,956✔
54
ECompileMain::~ECompileMain() = default;
×
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
      "       -S           perform short-circuit evaluation\n"
104
      "\n"
105
      " NOTE:\n"
106
      "   If <filespec> are required after an empty -r[i] option, you MUST specify\n"
107
      "   a literal [dir] of '.' (no quotes) or options will not parse correctly." );
108
}
×
109
static int s_argc;
110
static char** s_argv;
111

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

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

136
Compiler::SourceFileCache em_parse_tree_cache( summary.profile );
137
Compiler::SourceFileCache inc_parse_tree_cache( summary.profile );
138

139
using DependencyInfo = std::map<fs::path, std::set<fs::path>>;
140
// Map owner (sources) -> dependencies
141
DependencyInfo owner_dependencies;
142
// Map dependency -> owners (sources)
143
DependencyInfo dependency_owners;
144

145
std::set<fs::path> compiled_dirs;
146

147
std::unique_ptr<Compiler::Compiler> create_compiler()
3,208✔
148
{
149
  auto compiler = std::make_unique<Compiler::Compiler>( em_parse_tree_cache, inc_parse_tree_cache,
150
                                                        summary.profile );
3,208✔
151
  return compiler;
3,208✔
152
}
153

154
void load_packages()
2,956✔
155
{
156
  // No need to load packages if only formatting sources
157
  if ( !format_source )
2,956✔
158
  {
159
    // Load and analyze the package structure
160
    for ( const auto& elem : compilercfg.PackageRoot )
4,592✔
161
    {
162
      Plib::load_packages( elem, true /* quiet */ );
2,296✔
163
    }
164
    Plib::replace_packages();
2,296✔
165
    Plib::check_package_deps();
2,296✔
166
  }
167
}
2,956✔
168

169
void compile_inc( const std::string& path )
×
170
{
171
  if ( !quiet )
×
172
    INFO_PRINTLN( "Compiling: {}", path );
×
173

174
  std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
×
175

176
  compiler->set_include_compile_mode();
×
177
  bool res = compiler->compile_file( path );
×
178

179
  if ( !res )
×
180
    throw std::runtime_error( "Error compiling file" );
×
181
}
×
182

183
bool format_file( const std::string& path )
660✔
184
{
185
  std::string ext( "" );
660✔
186

187
  std::string::size_type pos = path.rfind( '.' );
660✔
188
  if ( pos != std::string::npos )
660✔
189
    ext = path.substr( pos );
660✔
190

191
  if ( ext.compare( ".src" ) != 0 && ext.compare( ".inc" ) != 0 && ext.compare( ".em" ) != 0 )
660✔
192
  {
193
    ERROR_PRINTLN(
×
194
        "Didn't find '.src', '.inc', or '.em' extension on source filename '{}'! ..Ignoring",
195
        path );
196
    return true;
×
197
  }
198

199
  if ( !quiet )
660✔
200
    INFO_PRINTLN( "Formatting: {}", path );
×
201

202
  std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
660✔
203

204
  bool success =
205
      compiler->format_file( path.c_str(), ext.compare( ".em" ) == 0, format_source_inplace );
660✔
206

207
  if ( expect_compile_failure )
660✔
208
  {
209
    if ( !success )  // good, it failed
×
210
    {
211
      if ( !quiet )
×
212
        INFO_PRINTLN( "Formatting failed as expected." );
×
213
      return true;
×
214
    }
215

NEW
216
    throw std::runtime_error( "Formatting succeeded (-e indicates failure was expected)" );
×
217
  }
218

219
  if ( !success )
660✔
220
    throw std::runtime_error( "Error formatting file" );
×
221
  return true;
660✔
222
}
660✔
223

224
void add_dependency_info( const fs::path& filepath_src,
134✔
225
                          std::set<fs::path>* removed_dependencies = nullptr,
226
                          std::set<fs::path>* new_dependencies = nullptr )
227
{
228
  auto filename_dep = fs::path( filepath_src ).replace_extension( ".dep" );
134✔
229

230
  auto& dependencies = owner_dependencies[filepath_src];
134✔
231
  auto previous_dependencies = dependencies;
134✔
232

233
  VERBOSE_PRINTLN( "Dependencies for: {}", filepath_src );
134✔
234
  for ( const auto& dependency : previous_dependencies )
246✔
235
  {
236
    if ( auto itr = dependency_owners.find( dependency ); itr != dependency_owners.end() )
112✔
237
    {
238
      // Remove deleted file from owners
239
      itr->second.erase( filepath_src );
112✔
240
      auto remaining = itr->second.size();
112✔
241
      VERBOSE_PRINTLN( "  - Removed {}, remaining owners={}", dependency, remaining );
112✔
242
      if ( remaining == 0 )
112✔
243
      {
244
        dependency_owners.erase( itr );
13✔
245
      }
246
    }
247
  }
248

249
  dependencies.clear();
134✔
250

251
  {
252
    std::ifstream ifs( filename_dep.c_str() );
134✔
253
    if ( ifs.is_open() )
134✔
254
    {
255
      std::string depname;
134✔
256
      while ( getline( ifs, depname ) )
1,102✔
257
      {
258
        fs::path depnamepath = fs::canonical( fs::path( depname ) );
968✔
259
        auto& owners = dependency_owners[depnamepath];
968✔
260
        // Add this source as a dependency by:
261
        // (1) placing `filename_src` in the set of owners for `depnamepath`.
262
        owners.emplace( filepath_src );
968✔
263
        // (2) placing `depnamepath` in the set of dependencies for this `filename_src`.
264
        dependencies.emplace( depnamepath );
968✔
265
        VERBOSE_PRINTLN( "  - Added {}, owners={}", depnamepath, owners.size() );
968✔
266
      }
968✔
267
    }
134✔
268

269
    // Could not load dependency information -- maybe failed compilation? We
270
    // still add this source as a dependency of itself, such that it will be
271
    // recompiled on next save.
272
    //
273
    // On failed compilation, the existing `.dep`
274
    // remains, so we will use that information for recompilation (eg. a source
275
    // failed to compile due to a bad include, changing the include should still
276
    // recompile the source.)
277
    else
278
    {
279
      dependency_owners[filepath_src] = std::set<fs::path>{ filepath_src };
×
280
      dependencies.emplace( filepath_src );
×
281
    }
282
  }
134✔
283

284
  if ( removed_dependencies && new_dependencies )
134✔
285
  {
286
    std::set_difference( previous_dependencies.begin(), previous_dependencies.end(),
14✔
287
                         dependencies.begin(), dependencies.end(),
288
                         std::inserter( *removed_dependencies, removed_dependencies->begin() ) );
289

290

291
    std::set_difference( dependencies.begin(), dependencies.end(), previous_dependencies.begin(),
14✔
292
                         previous_dependencies.end(),
293
                         std::inserter( *new_dependencies, new_dependencies->begin() ) );
294
  }
295
}
134✔
296

297
/**
298
 * Compiles the single given file (inc, src, hsr, asp), if needed
299
 *
300
 * Takes into account compilercfg.OnlyCompileUpdatedScripts and force_update
301
 *
302
 * @param path path of the file to be compiled
303
 * @return TRUE if the file was compiled, FALSE otherwise (eg. the file is up-to-date)
304
 */
305
bool compile_file( const std::string& path )
2,548✔
306
{
307
  std::string ext( "" );
2,548✔
308

309
  std::string::size_type pos = path.rfind( '.' );
2,548✔
310
  if ( pos != std::string::npos )
2,548✔
311
    ext = path.substr( pos );
2,548✔
312

313
  if ( !ext.compare( ".inc" ) )
2,548✔
314
  {
315
    compile_inc( path );
×
316
    return true;
×
317
  }
318

319
  if ( ext.compare( ".src" ) != 0 && ext.compare( ".hsr" ) != 0 && ext.compare( ".asp" ) != 0 )
2,548✔
320
  {
321
    ERROR_PRINTLN( "Didn't find '.src', '.hsr', or '.asp' extension on source filename '{}'!",
×
322
                   path );
323
    throw std::runtime_error( "Error in source filename" );
×
324
  }
325
  std::string fname = path;
2,548✔
326
  std::string filename_ecl = fname.replace( pos, 4, ".ecl" );
2,548✔
327
  std::string filename_lst = fname.replace( pos, 4, ".lst" );
2,548✔
328
  std::string filename_ast = fname.replace( pos, 4, ".ast" );
2,548✔
329
  std::string filename_dep = fname.replace( pos, 4, ".dep" );
2,548✔
330
  std::string filename_dbg = fname.replace( pos, 4, ".dbg" );
2,548✔
331

332
  if ( compilercfg.OnlyCompileUpdatedScripts && !force_update )
2,548✔
333
  {
334
    bool all_old = true;
2,428✔
335
    unsigned int ecl_timestamp = Clib::GetFileTimestamp( filename_ecl.c_str() );
2,428✔
336
    if ( Clib::GetFileTimestamp( path.c_str() ) >= ecl_timestamp )
2,428✔
337
    {
338
      if ( compilercfg.VerbosityLevel > 0 )
2,415✔
339
        INFO_PRINTLN( "{} is newer than {}", path, filename_ecl );
121✔
340
      all_old = false;
2,415✔
341
    }
342

343
    if ( all_old )
2,428✔
344
    {
345
      std::ifstream ifs( filename_dep.c_str() );
13✔
346
      // if the file doesn't exist, gotta build.
347
      if ( ifs.is_open() )
13✔
348
      {
349
        std::string depname;
12✔
350
        while ( getline( ifs, depname ) )
108✔
351
        {
352
          if ( Clib::GetFileTimestamp( depname.c_str() ) >= ecl_timestamp )
108✔
353
          {
354
            if ( compilercfg.VerbosityLevel > 0 )
12✔
355
              INFO_PRINTLN( "{} is newer than {}", depname, filename_ecl );
12✔
356
            all_old = false;
12✔
357
            break;
12✔
358
          }
359
        }
360
      }
12✔
361
      else
362
      {
363
        if ( compilercfg.VerbosityLevel > 0 )
1✔
364
          INFO_PRINTLN( "{} does not exist.", filename_dep );
1✔
365
        all_old = false;
1✔
366
      }
367
    }
13✔
368
    if ( all_old )
2,428✔
369
    {
370
      if ( !quiet && compilercfg.DisplayUpToDateScripts )
×
371
        INFO_PRINTLN( "{} is up-to-date.", filename_ecl );
×
372
      return false;
×
373
    }
374
  }
375

376

377
  {
378
    if ( !quiet )
2,548✔
379
      INFO_PRINTLN( "Compiling: {}", path );
2,548✔
380

381
    std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
2,548✔
382

383
    bool success = compiler->compile_file( path.c_str() );
2,548✔
384

385
    em_parse_tree_cache.keep_some();
2,548✔
386
    inc_parse_tree_cache.keep_some();
2,548✔
387

388
    if ( expect_compile_failure )
2,548✔
389
    {
390
      if ( !success )  // good, it failed
×
391
      {
392
        if ( !quiet )
×
393
          INFO_PRINTLN( "Compilation failed as expected." );
×
394
        return true;
×
395
      }
396

NEW
397
      throw std::runtime_error( "Compilation succeeded (-e indicates failure was expected)" );
×
398
    }
399

400
    if ( !success )
2,548✔
401
      throw std::runtime_error( "Error compiling file" );
313✔
402

403

404
    if ( !quiet )
2,235✔
405
      INFO_PRINTLN( "Writing:   {}", filename_ecl );
2,235✔
406

407
    if ( !compiler->write_ecl( filename_ecl ) )
2,235✔
408
    {
409
      throw std::runtime_error( "Error writing output file" );
×
410
    }
411

412
    if ( compilercfg.GenerateListing )
2,235✔
413
    {
414
      if ( !quiet )
1,985✔
415
        INFO_PRINTLN( "Writing:   {}", filename_lst );
1,985✔
416
      compiler->write_listing( filename_lst );
1,985✔
417
    }
418
    else if ( Clib::FileExists( filename_lst.c_str() ) )
250✔
419
    {
420
      if ( !quiet )
×
421
        INFO_PRINTLN( "Deleting:  {}", filename_lst );
×
422
      Clib::RemoveFile( filename_lst );
×
423
    }
424

425
    if ( compilercfg.GenerateAbstractSyntaxTree )
2,235✔
426
    {
427
      if ( !quiet )
8✔
428
        INFO_PRINTLN( "Writing:   {}", filename_ast );
8✔
429
      compiler->write_string_tree( filename_ast );
8✔
430
    }
431
    else if ( Clib::FileExists( filename_ast.c_str() ) )
2,227✔
432
    {
433
      if ( !quiet )
×
434
        INFO_PRINTLN( "Deleting:  {}", filename_ast );
×
435
      Clib::RemoveFile( filename_ast );
×
436
    }
437

438
    if ( compilercfg.GenerateDebugInfo )
2,235✔
439
    {
440
      if ( !quiet )
2,105✔
441
      {
442
        INFO_PRINTLN( "Writing:   {}", filename_dbg );
2,105✔
443
        if ( compilercfg.GenerateDebugTextInfo )
2,105✔
444
          INFO_PRINTLN( "Writing:   {}.txt", filename_dbg );
×
445
      }
446
      compiler->write_dbg( filename_dbg, compilercfg.GenerateDebugTextInfo );
2,105✔
447
    }
448
    else if ( Clib::FileExists( filename_dbg.c_str() ) )
130✔
449
    {
450
      if ( !quiet )
120✔
451
        INFO_PRINTLN( "Deleting:  {}", filename_dbg );
120✔
452
      Clib::RemoveFile( filename_dbg );
120✔
453
    }
454

455
    if ( compilercfg.GenerateDependencyInfo )
2,235✔
456
    {
457
      if ( !quiet )
130✔
458
        INFO_PRINTLN( "Writing:   {}", filename_dep );
130✔
459
      compiler->write_included_filenames( filename_dep );
130✔
460
    }
461
    else if ( Clib::FileExists( filename_dep.c_str() ) )
2,105✔
462
    {
463
      if ( !quiet )
×
464
        INFO_PRINTLN( "Deleting:  {}", filename_dep );
×
465
      Clib::RemoveFile( filename_dep );
×
466
    }
467
  }
2,548✔
468
  return true;
2,235✔
469
}
4,426✔
470

471
bool process_file( const std::string& path )
3,208✔
472
{
473
  if ( format_source )
3,208✔
474
    return format_file( path );
660✔
475
  return compile_file( path );
2,548✔
476
}
477

478
void process_file_wrapper( const std::string& path,
3,208✔
479
                           std::set<fs::path>* removed_dependencies = nullptr,
480
                           std::set<fs::path>* new_dependencies = nullptr )
481
{
482
  try
483
  {
484
    if ( process_file( path ) )
3,208✔
485
      ++summary.CompiledScripts;
2,895✔
486
    else
487
      ++summary.UpToDateScripts;
×
488
  }
489
  catch ( std::exception& )
313✔
490
  {
491
    ++summary.CompiledScripts;
313✔
492
    ++summary.ScriptsWithCompileErrors;
313✔
493
    if ( !keep_building )
313✔
494
      throw;
309✔
495
  }
313✔
496

497
  if ( watch_source )
2,899✔
498
  {
499
    fs::path filepath = fs::canonical( fs::path( path ) );
134✔
500
    auto ext = filepath.extension().generic_string();
134✔
501
    if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
134✔
502
    {
503
      add_dependency_info( filepath, removed_dependencies, new_dependencies );
134✔
504
    }
505
  }
134✔
506
}
2,899✔
507

508
bool setting_value( const char* arg )
3,120✔
509
{
510
  // format of arg is -C or -C-
511
  if ( arg[2] == '\0' )
3,120✔
512
    return true;
3,120✔
NEW
513
  if ( arg[2] == '-' )
×
514
    return false;
×
NEW
515
  if ( arg[2] == '+' )
×
516
    return true;
×
NEW
517
  return true;
×
518
}
519

520

521
int readargs( int argc, char** argv )
2,956✔
522
{
523
  bool unknown_opt = false;
2,956✔
524

525
  for ( int i = 1; i < argc; i++ )
13,308✔
526
  {
527
    const char* arg = argv[i];
10,352✔
528
#ifdef __linux__
529
    if ( arg[0] == '-' )
10,352✔
530
#else
531
    if ( arg[0] == '/' || arg[0] == '-' )
532
#endif
533
    {
534
      switch ( arg[1] )
7,398✔
535
      {
536
      case 'W':
1✔
537
        watch_source = true;
1✔
538
        compilercfg.GenerateDependencyInfo = true;
1✔
539
        keep_building = true;
1✔
540
        compilercfg.ThreadedCompilation =
1✔
541
            false;  // limitation since dependency gathering is not thread-safe
542
        break;
1✔
543

544
      case 'A':  // skip it at this point.
2✔
545
        break;
2✔
546

547
      case 'c':
×
548
        compilercfg.ErrorOnFileCaseMissmatch = setting_value( arg );
×
549
        break;
×
550

551
      case 'C':  // skip it at this point.
2,954✔
552
        ++i;     // and skip its parameter.
2,954✔
553
        break;
2,954✔
554

555
      case 'd':
×
556
        compilercfg.DisplayDebugs = setting_value( arg );
×
557
        break;
×
558

559
      case 'D':
×
560
        compilercfg.GenerateDependencyInfo = setting_value( arg );
×
561
        break;
×
562

563
      case 'e':
×
564
        expect_compile_failure = true;
×
565
        break;
×
566

567
#ifdef WIN32
568
      case 'E':
569
      {
570
        std::string path = &argv[i][2];
571
        CfgPathEnv = "ECOMPILE_CFG_PATH=" + path;
572
        _putenv( CfgPathEnv.c_str() );
573
      }
574
      break;
575
#endif
576

577
      case 'g':
×
578
      {
579
        auto value = setting_value( arg );
×
580
        if ( !value )
×
581
        {
582
          INFO_PRINTLN( "The OG Compiler has been removed." );
×
583
          unknown_opt = true;
×
584
        }
585
        break;
×
586
      }
587

588
      case 'q':
660✔
589
        quiet = true;
660✔
590
        break;
660✔
591

592
      case 'w':
×
593
        compilercfg.DisplayWarnings = setting_value( arg );
×
594
        if ( argv[i][2] == 'P' )
×
595
          compilercfg.ParanoiaWarnings = true;
×
596
        break;
×
597
      case 'y':
×
598
        compilercfg.ErrorOnWarning = setting_value( arg );
×
599
        break;
×
600

601
      case 'l':
2,294✔
602
        compilercfg.GenerateListing = setting_value( arg );
2,294✔
603
        break;
2,294✔
604

605
      case 'i':
×
606
        include_debug = 1;
×
607
        break;
×
608

609
      case 'r':  // -r[i] [dir] is okay
×
610
        // Only suboption allowed is '-ri' (.inc recurse)
611
        if ( argv[i][2] && argv[i][2] != 'i' )
×
612
        {
613
          // BZZZZT! *error*
614
          unknown_opt = true;
×
615
          break;
×
616
        }
617

618
// Only skip next parameter if it's not an option!!
619
#ifdef __linux__
620
        if ( i + 1 < argc && argv[i + 1][0] != '-' )
×
621
#else
622
        if ( i + 1 < argc && argv[i + 1][0] != '/' && argv[i + 1][0] != '-' )
623
#endif
624
          ++i;
×
625
        break;
×
626

627
      case 't':
×
628
        show_timing_details = true;
×
629
        if ( argv[i][2] == 'v' )
×
630
          timing_quiet_override = true;
×
631
        break;
×
632

633
      case 's':
×
634
        // show_source = true;
635
        compilercfg.DisplaySummary = true;
×
636
        break;
×
637

638
      case 'a':
×
639
        compilercfg.CompileAspPages = setting_value( arg );
×
640
        break;
×
641

642
      case 'm':
×
643
        compilercfg.OptimizeObjectMembers = false;
×
644
        break;
×
645

646
      case 'b':
×
647
        keep_building = true;
×
648
        break;
×
649

650
      case 'u':
×
651
        compilercfg.OnlyCompileUpdatedScripts = setting_value( arg );
×
652
        if ( compilercfg.OnlyCompileUpdatedScripts )
×
653
          compilercfg.GenerateDependencyInfo = true;
×
654
        break;
×
655

656
      case 'F':
660✔
657
      {
658
        if ( argv[i][2] && argv[i][2] == 'i' )
660✔
659
          format_source_inplace = true;
660✔
660
        format_source = true;
660✔
661
        break;
660✔
662
      }
663

664
      case 'f':
×
665
        force_update = true;
×
666
        break;
×
667

668
      case 'v':
1✔
669
        int vlev;
670
        vlev = atoi( &argv[i][2] );
1✔
671
        if ( !vlev )
1✔
672
          vlev = 1;
1✔
673
        compilercfg.VerbosityLevel = vlev;
1✔
674
        break;
1✔
675

676
      case 'x':
1✔
677
        compilercfg.GenerateDebugInfo = setting_value( arg );
1✔
678
        compilercfg.GenerateDebugTextInfo = ( argv[i][2] == 't' );
1✔
679
        break;
1✔
680

681
      case 'Z':
8✔
682
        compilercfg.GenerateAbstractSyntaxTree = setting_value( arg );
8✔
683
        break;
8✔
684

685
#ifdef WIN32
686
      case 'P':
687
      {
688
        std::string path = &argv[i][2];
689
        EmPathEnv = "ECOMPILE_PATH_EM=" + path;
690
        IncPathEnv = "ECOMPILE_PATH_INC=" + path;
691
        _putenv( EmPathEnv.c_str() );
692
        _putenv( IncPathEnv.c_str() );
693
      }
694
      break;
695
#endif
696
      case 'T':
×
697
      {
698
        compilercfg.ThreadedCompilation = true;
×
699
        int count = atoi( &argv[i][2] );
×
700
        compilercfg.NumberOfThreads = count;
×
701
        break;
×
702
      }
703
      case 'S':
817✔
704
        compilercfg.ShortCircuitEvaluation = setting_value( arg );
817✔
705
        break;
817✔
706
      default:
×
707
        unknown_opt = true;
×
708
        break;
×
709
      }
710
    }
711

712
    if ( unknown_opt )
10,352✔
713
    {
714
      ERROR_PRINTLN( "Unknown option: {}", argv[i] );
×
715
      return 1;
×
716
    }
717
  }
718
  return 0;
2,956✔
719
}
720

721
void apply_configuration()
2,956✔
722
{
723
  em_parse_tree_cache.configure( compilercfg.EmParseTreeCacheSize );
2,956✔
724
  inc_parse_tree_cache.configure( compilercfg.IncParseTreeCacheSize );
2,956✔
725
}
2,956✔
726

727
void recurse_call( const std::vector<fs::path>& basedirs, bool inc_files,
2✔
728
                   const std::function<void( const std::string& )>& callback )
729
{
730
  std::set<std::string> files;
2✔
731
  for ( const auto& basedir : basedirs )
8✔
732
  {
733
    if ( !fs::is_directory( basedir ) )
6✔
734
      continue;
×
735
    std::error_code ec;
6✔
736
    for ( auto dir_itr = fs::recursive_directory_iterator( basedir, ec );
6✔
737
          dir_itr != fs::recursive_directory_iterator(); ++dir_itr )
1,744✔
738
    {
739
      if ( Clib::exit_signalled )
869✔
740
        return;
×
741
      if ( auto fn = dir_itr->path().filename().string(); !fn.empty() && *fn.begin() == '.' )
869✔
742
      {
743
        if ( dir_itr->is_directory() )
4✔
744
          dir_itr.disable_recursion_pending();
2✔
745
        continue;
4✔
746
      }
869✔
747
      if ( !dir_itr->is_regular_file() )
865✔
748
        continue;
99✔
749
      const auto ext = dir_itr->path().extension();
766✔
750
      const auto file = dir_itr->path().generic_string();
766✔
751
      if ( inc_files )
766✔
752
      {
753
        if ( !ext.compare( ".inc" ) )
×
754
          if ( files.insert( file ).second )
×
755
            callback( file );
×
756
      }
757
      else if ( !ext.compare( ".src" ) || !ext.compare( ".hsr" ) ||
1,290✔
758
                ( compilercfg.CompileAspPages && !ext.compare( ".asp" ) ) )
524✔
759
      {
760
        if ( files.insert( file ).second )
242✔
761
          callback( file );
240✔
762
      }
763
    }
772✔
764
  }
765
}
2✔
766

767
void process_dirs( const std::vector<fs::path>& dirs, bool compile_inc )
2✔
768
{
769
  if ( !compilercfg.ThreadedCompilation )
2✔
770
  {
771
    recurse_call( dirs, compile_inc,
2✔
772
                  []( const std::string& file ) { process_file_wrapper( file ); } );
240✔
773
    return;
2✔
774
  }
775
  std::atomic<unsigned> compiled_scripts( 0 );
×
776
  std::atomic<unsigned> uptodate_scripts( 0 );
×
777
  std::atomic<unsigned> error_scripts( 0 );
×
778
  std::atomic<bool> par_keep_building( true );
×
779
  {
780
    unsigned int thread_count = std::max( 2u, std::thread::hardware_concurrency() * 2 );
×
781
    if ( compilercfg.NumberOfThreads )
×
782
      thread_count = static_cast<unsigned>( compilercfg.NumberOfThreads );
×
783
    threadhelp::TaskThreadPool pool( thread_count, "ecompile" );
×
784
    summary.ThreadCount = pool.size();
×
785
    auto callback = [&]( const std::string& file )
×
786
    {
787
      pool.push(
×
788
          [&, file]()
×
789
          {
790
            if ( !par_keep_building || Clib::exit_signalled )
×
791
              return;
×
792
            try
793
            {
794
              if ( process_file( file ) )
×
795
                ++compiled_scripts;
×
796
              else
797
                ++uptodate_scripts;
×
798
            }
799
            catch ( std::exception& e )
×
800
            {
801
              ++compiled_scripts;
×
802
              ++error_scripts;
×
803
              ERROR_PRINTLN( "failed to compile {}: {}", file, e.what() );
×
804
              if ( !keep_building )
×
805
                par_keep_building = false;
×
806
            }
×
807
            catch ( ... )
×
808
            {
809
              par_keep_building = false;
×
810
              Clib::force_backtrace();
×
811
            }
×
812
          } );
813
    };
×
814
    recurse_call( dirs, compile_inc, callback );
×
815
  }
×
816
  summary.CompiledScripts = compiled_scripts;
×
817
  summary.UpToDateScripts = uptodate_scripts;
×
818
  summary.ScriptsWithCompileErrors = error_scripts;
×
819
}
820

821
void DisplaySummary( const Tools::Timer<>& timer )
7✔
822
{
823
  std::string tmp = "Compilation Summary:\n";
7✔
824
  if ( summary.ThreadCount )
7✔
825
    tmp += fmt::format( "    Used {} threads\n", summary.ThreadCount );
×
826
  if ( summary.CompiledScripts )
7✔
827
    tmp += fmt::format( "    Compiled {} script{} in {} ms.\n", summary.CompiledScripts,
7✔
828
                        ( summary.CompiledScripts == 1 ? "" : "s" ), timer.ellapsed() );
21✔
829

830
  if ( summary.ScriptsWithCompileErrors )
7✔
831
    tmp += fmt::format( "    {} of those script{} had errors.\n", summary.ScriptsWithCompileErrors,
1✔
832
                        ( summary.ScriptsWithCompileErrors == 1 ? "" : "s" ) );
3✔
833

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

838
  if ( show_timing_details )
7✔
839
  {
840
    tmp += fmt::format( "    build workspace: {}\n",
×
841
                        (long long)summary.profile.build_workspace_micros / 1000 );
×
842
    tmp += fmt::format( "        - load *.em:   {}\n",
×
843
                        (long long)summary.profile.load_em_micros / 1000 );
×
844
    tmp += fmt::format( "       - parse *.em:   {} ({})\n",
×
845
                        (long long)summary.profile.parse_em_micros / 1000,
×
846
                        (long)summary.profile.parse_em_count );
×
847
    tmp += fmt::format( "         - ast *.em:   {}\n",
×
848
                        (long long)summary.profile.ast_em_micros / 1000 );
×
849
    tmp += fmt::format( "      - parse *.inc:   {} ({})\n",
×
850
                        (long long)summary.profile.parse_inc_micros / 1000,
×
851
                        (long)summary.profile.parse_inc_count );
×
852
    tmp += fmt::format( "        - ast *.inc:   {}\n",
×
853
                        (long long)summary.profile.ast_inc_micros / 1000 );
×
854
    tmp += fmt::format( "      - parse *.src:   {} ({})\n",
×
855
                        (long long)summary.profile.parse_src_micros / 1000,
×
856
                        (long)summary.profile.parse_src_count );
×
857
    tmp += fmt::format( "        - ast *.src:   {}\n",
×
858
                        (long long)summary.profile.ast_src_micros / 1000 );
×
859
    tmp += fmt::format( "  resolve functions:   {}\n",
×
860
                        (long long)summary.profile.ast_resolve_functions_micros / 1000 );
×
861
    tmp += fmt::format( " register constants: {}\n",
×
862
                        (long long)summary.profile.register_const_declarations_micros / 1000 );
×
863
    tmp += fmt::format( "            analyze: {}\n",
×
864
                        (long long)summary.profile.analyze_micros / 1000 );
×
865
    tmp += fmt::format( "           optimize: {}\n",
×
866
                        (long long)summary.profile.optimize_micros / 1000 );
×
867
    tmp += fmt::format( "       disambiguate: {}\n",
×
868
                        (long long)summary.profile.disambiguate_micros / 1000 );
×
869
    tmp += fmt::format( "      generate code: {}\n",
×
870
                        (long long)summary.profile.codegen_micros / 1000 );
×
871
    tmp += fmt::format( "  prune cache (sel): {}\n",
×
872
                        (long long)summary.profile.prune_cache_select_micros / 1000 );
×
873
    tmp += fmt::format( "  prune cache (del): {}\n",
×
874
                        (long long)summary.profile.prune_cache_delete_micros / 1000 );
×
875
    tmp += "\n";
×
876
    tmp += fmt::format( "      - ambiguities: {}\n", (long)summary.profile.ambiguities );
×
877
    tmp += fmt::format( "       - cache hits: {}\n", (long)summary.profile.cache_hits );
×
878
    tmp += fmt::format( "     - cache misses: {}\n", (long)summary.profile.cache_misses );
×
879
  }
880

881
  INFO_PRINTLN( tmp );
7✔
882
}
7✔
883

884
void EnterWatchMode()
1✔
885
{
886
  std::list<WatchFileMessage> watch_messages;
1✔
887
  std::set<fs::path> to_compile;
1✔
888
  efsw::FileWatcher fileWatcher;
1✔
889
  EfswFileWatchListener listener( fileWatcher,
890
                                  std::set<fs::path>{ ".src", ".hsr", ".asp", ".inc", ".em" } );
6✔
891

892
  auto handle_compile_file = [&]( const fs::path& filepath )
5✔
893
  {
894
    // Existing file, compile all owners dependent on it.
895
    if ( auto itr = dependency_owners.find( filepath ); itr != dependency_owners.end() )
5✔
896
    {
897
      to_compile.insert( itr->second.begin(), itr->second.end() );
4✔
898
    }
899
    // New file, compile only this file (if it is compilable).
900
    else
901
    {
902
      auto ext = filepath.extension();
1✔
903
      if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
1✔
904
      {
905
        to_compile.emplace( filepath );
1✔
906
      }
907
    }
1✔
908
  };
5✔
909

910
  auto handle_delete_file = [&]( const fs::path& filepath )
1✔
911
  {
912
    to_compile.erase( filepath );
1✔
913
    if ( auto itr = owner_dependencies.find( filepath ); itr != owner_dependencies.end() )
1✔
914
    {
915
      // For all dependencies, remove the deleted file from set of owners for
916
      // that dependency.
917
      for ( const auto& depnamepath : itr->second )
4✔
918
      {
919
        // If dependency exists in map of dependency -> owners
920
        if ( auto owners_itr = dependency_owners.find( depnamepath );
3✔
921
             owners_itr != dependency_owners.end() )
3✔
922
        {
923
          // Erase filepath from set of owners
924
          owners_itr->second.erase( filepath );
3✔
925

926
          // If owners set is empty, this dependency is no longer used by
927
          // anything.
928
          if ( owners_itr->second.empty() )
3✔
929
          {
930
            //  Erase from map of dependency -> owners.
931
            dependency_owners.erase( owners_itr );
1✔
932
            listener.remove_file( depnamepath );
1✔
933
            VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
1✔
934
          }
935
        }
936
      }
937
      owner_dependencies.erase( itr );
1✔
938
    }
939
  };
1✔
940

941
  auto add_dir = [&]( const std::string& elem )
4✔
942
  {
943
    fs::path dir( elem );
4✔
944
    if ( fs::exists( dir ) )
4✔
945
    {
946
      auto dirpath = fs::canonical( dir );
4✔
947
      if ( listener.add_watch_dir( dirpath ) )
4✔
948
      {
949
        VERBOSE_PRINTLN( "Add watch dir {}", dirpath );
4✔
950
      }
951
    }
4✔
952
  };
4✔
953

954
  for ( const auto& dep_owners : dependency_owners )
141✔
955
  {
956
    listener.add_file( dep_owners.first );
140✔
957
  }
958

959
  for ( const auto& directory : compiled_dirs )
4✔
960
  {
961
    listener.add_dir( directory );
3✔
962
  }
963

964
  for ( const auto& elem : compilercfg.PackageRoot )
2✔
965
  {
966
    add_dir( elem );
1✔
967
  }
968

969
  add_dir( compilercfg.ModuleDirectory );
1✔
970
  add_dir( compilercfg.IncludeDirectory );
1✔
971
  add_dir( compilercfg.PolScriptRoot );
1✔
972

973
  fileWatcher.watch();
1✔
974

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

978
  while ( !Clib::exit_signalled )
8✔
979
  {
980
    std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
6✔
981
    listener.take_messages( watch_messages );
6✔
982
    if ( !watch_messages.empty() )
6✔
983
    {
984
      for ( auto const& message : watch_messages )
11✔
985
      {
986
        const auto& filepath = message.filepath;
6✔
987
        const auto& old_filepath = message.old_filepath;
6✔
988
        VERBOSE_PRINTLN( "Event: filename={}, old_filename={}", filepath, old_filepath );
6✔
989

990
        // created or modified
991
        if ( old_filepath.empty() )
6✔
992
        {
993
          handle_compile_file( filepath );
5✔
994
        }
995
        // deleted
996
        else if ( filepath.empty() )
1✔
997
        {
998
          handle_delete_file( old_filepath );
1✔
999
        }
1000
        // moved
1001
        else
1002
        {
1003
          handle_delete_file( old_filepath );
×
1004
          handle_compile_file( filepath );
×
1005
        }
1006
      }
1007
      watch_messages.clear();
5✔
1008

1009
      if ( !to_compile.empty() )
5✔
1010
      {
1011
        summary.CompiledScripts = 0;
5✔
1012
        summary.UpToDateScripts = 0;
5✔
1013
        summary.ScriptsWithCompileErrors = 0;
5✔
1014
        em_parse_tree_cache.clear();
5✔
1015
        inc_parse_tree_cache.clear();
5✔
1016

1017
        VERBOSE_PRINTLN( "To compile: {}", to_compile );
5✔
1018

1019
        Tools::Timer<> timer;
5✔
1020
        for ( const auto& filepath : to_compile )
19✔
1021
        {
1022
          if ( Clib::exit_signalled )
14✔
1023
          {
1024
            break;
×
1025
          }
1026

1027
          std::set<fs::path> removed_dependencies;
14✔
1028
          std::set<fs::path> new_dependencies;
14✔
1029
          try
1030
          {
1031
            process_file_wrapper( filepath.generic_string(), &removed_dependencies,
14✔
1032
                                  &new_dependencies );
1033
          }
1034
          catch ( ... )
×
1035
          {
1036
          }
×
1037

1038
          VERBOSE_PRINTLN( "New dependencies: {} Removed dependencies: {}", new_dependencies,
14✔
1039
                           removed_dependencies );
1040

1041
          for ( const auto& depnamepath : removed_dependencies )
14✔
1042
          {
1043
            // If a removed dependency has no owner set is empty, we can remove
1044
            // the listener for that dependency.
1045
            if ( dependency_owners.find( depnamepath ) == dependency_owners.end() )
×
1046
            {
1047
              if ( listener.remove_file( depnamepath ) )
×
1048
              {
1049
                VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
×
1050
              }
1051
            }
1052
          }
1053

1054
          for ( const auto& depnamepath : new_dependencies )
17✔
1055
          {
1056
            if ( listener.add_file( depnamepath ) )
3✔
1057
            {
1058
              VERBOSE_PRINTLN( "Add watch file {}", depnamepath );
1✔
1059
            }
1060
          }
1061
        }
14✔
1062

1063
        timer.stop();
5✔
1064
        if ( compilercfg.DisplaySummary && !quiet )
5✔
1065
        {
1066
          DisplaySummary( timer );
5✔
1067
        }
1068
        to_compile.clear();
5✔
1069
      }
5✔
1070
    }
1071
  }
1072
}
2✔
1073

1074
/**
1075
 * Runs the compilation threads
1076
 */
1077
void AutoCompile()
2✔
1078
{
1079
  bool save = compilercfg.OnlyCompileUpdatedScripts;
2✔
1080
  compilercfg.OnlyCompileUpdatedScripts = compilercfg.UpdateOnlyOnAutoCompile;
2✔
1081
  std::vector<fs::path> dirs;
2✔
1082
  compiled_dirs.emplace( fs::canonical( compilercfg.PolScriptRoot ) );
2✔
1083

1084
  dirs.emplace_back( compilercfg.PolScriptRoot );
2✔
1085
  for ( const auto& pkg : Plib::systemstate.packages )
6✔
1086
  {
1087
    compiled_dirs.emplace( fs::canonical( pkg->dir() ) );
4✔
1088
    dirs.emplace_back( pkg->dir() );
4✔
1089
  }
1090
  process_dirs( dirs, false );
2✔
1091
  compilercfg.OnlyCompileUpdatedScripts = save;
2✔
1092
}
2✔
1093

1094
/**
1095
 * Takes decisions, runs, the compilation, prints summary and cleans after
1096
 */
1097
bool run( int argc, char** argv, int* res )
2,956✔
1098
{
1099
  Clib::enable_exit_signaller();
2,956✔
1100

1101
  load_packages();
2,956✔
1102

1103
  // Determine the run mode and do the compile itself
1104
  Tools::Timer<> timer;
2,956✔
1105
  bool any = false;
2,956✔
1106

1107
  for ( int i = 1; i < argc; i++ )
12,999✔
1108
  {
1109
#ifdef __linux__
1110
    if ( argv[i][0] == '-' )
10,352✔
1111
#else
1112
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
1113
#endif
1114
    {
1115
      if ( argv[i][1] == 'A' )
7,398✔
1116
      {
1117
        compilercfg.UpdateOnlyOnAutoCompile = ( argv[i][2] == 'u' );
2✔
1118
        any = true;
2✔
1119

1120
        AutoCompile();
2✔
1121
      }
1122
      else if ( argv[i][1] == 'r' )
7,396✔
1123
      {
1124
        any = true;
×
1125
        std::string dir( "." );
×
1126
        bool compile_inc = ( argv[i][2] == 'i' );  // compile .inc files
×
1127

1128
        ++i;
×
1129
        if ( i < argc && argv[i] && argv[i][0] != '-' )
×
1130
          dir.assign( argv[i] );
×
1131

1132
        compiled_dirs.emplace( fs::canonical( dir ) );
×
1133
        process_dirs( { dir }, compile_inc );
×
1134
      }
×
1135
      else if ( argv[i][1] == 'C' )
7,396✔
1136
      {
1137
        ++i;  // skip the config file pathname
2,954✔
1138
      }
1139
      // and skip any other option.
1140
    }
1141
    else
1142
    {
1143
      any = true;
2,954✔
1144
#ifdef _WIN32
1145
      Clib::forspec( argv[i], []( const char* pathname ) { process_file_wrapper( pathname ); } );
1146
#else
1147
      process_file_wrapper( argv[i] );
3,572✔
1148
#endif
1149
    }
1150
  }
1151

1152
  if ( !any && compilercfg.AutoCompileByDefault )
2,647✔
1153
  {
1154
    any = true;
×
1155
    AutoCompile();
×
1156
  }
1157

1158
  // Execution is completed: start final/cleanup tasks
1159
  timer.stop();
2,647✔
1160

1161
  if ( any && compilercfg.DisplaySummary && !quiet )
2,647✔
1162
  {
1163
    DisplaySummary( timer );
2✔
1164
  }
1165

1166
  if ( watch_source )
2,647✔
1167
  {
1168
    EnterWatchMode();
1✔
1169
  }
1170

1171
  Plib::systemstate.deinitialize();
2,647✔
1172

1173
  if ( summary.ScriptsWithCompileErrors )
2,647✔
1174
    *res = 1;
×
1175
  else
1176
    *res = 0;
2,647✔
1177
  return any;
2,647✔
1178
}
2,956✔
1179

1180
void read_config_file( int argc, char* argv[] )
2,956✔
1181
{
1182
  for ( int i = 1; i < argc; i++ )
7,400✔
1183
  {
1184
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
7,398✔
1185
    {
1186
      // -C cfgpath
1187
      if ( argv[i][1] == 'C' )
7,398✔
1188
      {
1189
        ++i;
2,954✔
1190
        if ( i == argc )
2,954✔
1191
          throw std::runtime_error( "-C specified without pathname" );
×
1192

1193
        compilercfg.Read( std::string( argv[i] ) );
2,954✔
1194
        return;
2,954✔
1195
      }
1196
    }
1197
  }
1198

1199
  // check ECOMPILE_CFG_PATH environment variable
1200
  const char* env_ecompile_cfg_path = getenv( "ECOMPILE_CFG_PATH" );
2✔
1201
  if ( env_ecompile_cfg_path != nullptr )
2✔
1202
  {
1203
    compilercfg.Read( std::string( env_ecompile_cfg_path ) );
×
1204
    return;
×
1205
  }
1206

1207
  // no -C arg, so use binary path (hope it's right..sigh.)
1208
  std::string cfgpath = PROG_CONFIG::programDir() + "ecompile.cfg";
2✔
1209
  if ( Clib::FileExists( "ecompile.cfg" ) )
2✔
1210
  {
1211
    compilercfg.Read( "ecompile.cfg" );
2✔
1212
  }
1213
  else if ( Clib::FileExists( cfgpath ) )
×
1214
  {
1215
    compilercfg.Read( cfgpath );
×
1216
  }
1217
  else
1218
  {
1219
    ERROR_PRINTLN( "Could not find {}; using defaults.", cfgpath );
×
1220
    compilercfg.SetDefaults();
×
1221
  }
1222
}
2✔
1223

1224
/**
1225
 * This is the main entry point for ecompile program
1226
 */
1227
int ECompileMain::main()
2,956✔
1228
{
1229
  Clib::Logging::global_logger->disableFileLog();
2,956✔
1230

1231
  const std::vector<std::string>& binArgs = programArgs();
2,956✔
1232

1233
/**********************************************
1234
 * TODO: rework the following cruft from former uotool.cpp
1235
 **********************************************/
1236
#ifdef _WIN32
1237
  Clib::MiniDumper::Initialize();
1238
#endif
1239

1240
  ECompile::read_config_file( s_argc, s_argv );
2,956✔
1241

1242
  /**********************************************
1243
   * show help
1244
   **********************************************/
1245
  if ( binArgs.size() == 1 && !compilercfg.AutoCompileByDefault )
2,956✔
1246
  {
1247
    showHelp();
×
1248
    return 0;  // return "okay"
×
1249
  }
1250

1251
  watch_source = compilercfg.WatchModeByDefault;
2,956✔
1252
  if ( watch_source )
2,956✔
1253
  {
1254
    compilercfg.GenerateDependencyInfo = true;
×
1255
    keep_building = true;
×
1256
    compilercfg.ThreadedCompilation =
×
1257
        false;  // limitation since dependency gathering is not thread-safe
1258
  }
1259

1260
  int res = ECompile::readargs( s_argc, s_argv );
2,956✔
1261
  if ( res )
2,956✔
1262
  {
1263
    showHelp();
×
1264
    return res;
×
1265
  }
1266

1267
  ECompile::apply_configuration();
2,956✔
1268

1269
  if ( !ECompile::quiet )
2,956✔
1270
  {
1271
    // vX.YY
1272
    INFO_PRINTLN( "EScript Compiler v1.{}\n{}\n", ESCRIPT_FILE_VER_CURRENT, POL_COPYRIGHT );
2,296✔
1273
  }
1274

1275
  int prog_res = 1;
2,956✔
1276
  bool didanything = ECompile::run( s_argc, s_argv, &prog_res );
2,956✔
1277

1278
  if ( !didanything )
2,647✔
1279
  {
1280
    showHelp();
×
1281
    return 1;
×
1282
  }
1283
  return prog_res;
2,647✔
1284
}
1285
}  // namespace Pol::ECompile
1286

1287

1288
///////////////////////////////////////////////////////////////////////////////
1289
///////////////////////////////////////////////////////////////////////////////
1290
///////////////////////////////////////////////////////////////////////////////
1291

1292
int main( int argc, char* argv[] )
2,956✔
1293
{
1294
  Pol::ECompile::s_argc = argc;
2,956✔
1295
  Pol::ECompile::s_argv = argv;
2,956✔
1296

1297
  Pol::ECompile::ECompileMain* ECompileMain = new Pol::ECompile::ECompileMain();
2,956✔
1298
  ECompileMain->start( argc, argv );
2,956✔
1299
}
×
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