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

polserver / polserver / 13143687657

04 Feb 2025 07:52PM UTC coverage: 58.057% (+0.01%) from 58.047%
13143687657

push

github

web-flow
Fix directory traversal in ecompile (#757)

* Fix directory traversal in ecompile

* Update core-changes

3 of 5 new or added lines in 1 file covered. (60.0%)

3 existing lines in 1 file now uncovered.

41404 of 71316 relevant lines covered (58.06%)

378974.41 hits per line

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

60.19
/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() {}
1,976✔
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
      "       -a           compile *.asp pages also\n"
70
      "       -A           automatically compile scripts in main and enabled packages\n"
71
      "       -Au          (as '-A' but only compile updated files)\n"
72
      "       -b           keep building other scripts after errors\n"
73
      "       -c           treat wrong capitalization in include directives as error\n"
74
      "       -C cfgpath   path to configuration (replaces ecompile.cfg)\n"
75
      "       -d           display confusing compiler parse information\n"
76
      "       -D           write dependency information\n"
77
      "       -e           report error on successful compilation (used for testing)\n"
78
#ifdef WIN32
79
      "       -Ecfgpath    set or change the ECOMPILE_CFG_PATH evironment variable\n"
80
#endif
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

236
  std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
578✔
237

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

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

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

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

266
  auto& dependencies = owner_dependencies[filepath_src];
120✔
267
  auto previous_dependencies = dependencies;
120✔
268

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

285
  dependencies.clear();
120✔
286

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

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

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

326

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

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

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

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

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

367
  if ( compilercfg.OnlyCompileUpdatedScripts && !force_update )
1,622✔
368
  {
369
    bool all_old = true;
1,516✔
370
    unsigned int ecl_timestamp = Clib::GetFileTimestamp( filename_ecl.c_str() );
1,516✔
371
    if ( Clib::GetFileTimestamp( path.c_str() ) >= ecl_timestamp )
1,516✔
372
    {
373
      if ( compilercfg.VerbosityLevel > 0 )
1,505✔
374
        INFO_PRINTLN( "{} is newer than {}", path, filename_ecl );
109✔
375
      all_old = false;
1,505✔
376
    }
377

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

411

412
  {
413
    if ( !quiet )
1,622✔
414
      INFO_PRINTLN( "Compiling: {}", path );
226✔
415

416
    std::unique_ptr<Compiler::Compiler> compiler = create_compiler();
1,622✔
417

418
    bool success = compiler->compile_file( path.c_str() );
1,622✔
419

420
    em_parse_tree_cache.keep_some();
1,622✔
421
    inc_parse_tree_cache.keep_some();
1,622✔
422

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

437
    if ( !success )
1,622✔
438
      throw std::runtime_error( "Error compiling file" );
246✔
439

440

441
    if ( !quiet )
1,376✔
442
      INFO_PRINTLN( "Writing:   {}", filename_ecl );
222✔
443

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

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

462
    if ( compilercfg.GenerateDebugInfo )
1,376✔
463
    {
464
      if ( !quiet )
1,260✔
465
      {
466
        INFO_PRINTLN( "Writing:   {}", filename_dbg );
106✔
467
        if ( compilercfg.GenerateDebugTextInfo )
106✔
468
          INFO_PRINTLN( "Writing:   {}.txt", filename_dbg );
×
469
      }
470
      compiler->write_dbg( filename_dbg, compilercfg.GenerateDebugTextInfo );
1,260✔
471
    }
472
    else if ( Clib::FileExists( filename_dbg.c_str() ) )
116✔
473
    {
474
      if ( !quiet )
106✔
475
        INFO_PRINTLN( "Deleting:  {}", filename_dbg );
106✔
476
      Clib::RemoveFile( filename_dbg );
106✔
477
    }
478

479
    if ( compilercfg.GenerateDependencyInfo )
1,376✔
480
    {
481
      if ( !quiet )
116✔
482
        INFO_PRINTLN( "Writing:   {}", filename_dep );
116✔
483
      compiler->write_included_filenames( filename_dep );
116✔
484
    }
485
    else if ( Clib::FileExists( filename_dep.c_str() ) )
1,260✔
486
    {
487
      if ( !quiet )
×
488
        INFO_PRINTLN( "Deleting:  {}", filename_dep );
×
489
      Clib::RemoveFile( filename_dep );
×
490
    }
491
  }
1,622✔
492
  return true;
1,376✔
493
}
2,852✔
494

495
bool process_file( const std::string& path )
2,200✔
496
{
497
  if ( format_source )
2,200✔
498
    return format_file( path );
578✔
499
  return compile_file( path );
1,622✔
500
}
501

502
void process_file_wrapper( const std::string& path,
2,200✔
503
                           std::set<fs::path>* removed_dependencies = nullptr,
504
                           std::set<fs::path>* new_dependencies = nullptr )
505
{
506
  try
507
  {
508
    if ( process_file( path ) )
2,200✔
509
      ++summary.CompiledScripts;
1,954✔
510
    else
511
      ++summary.UpToDateScripts;
×
512
  }
513
  catch ( std::exception& )
246✔
514
  {
515
    ++summary.CompiledScripts;
246✔
516
    ++summary.ScriptsWithCompileErrors;
246✔
517
    if ( !keep_building )
246✔
518
      throw;
242✔
519
  }
246✔
520

521
  if ( watch_source )
1,958✔
522
  {
523
    fs::path filepath = fs::canonical( fs::path( path ) );
120✔
524
    auto ext = filepath.extension().generic_string();
120✔
525
    if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
120✔
526
    {
527
      add_dependency_info( filepath, removed_dependencies, new_dependencies );
120✔
528
    }
529
  }
120✔
530
}
1,958✔
531

532
bool setting_value( const char* arg )
1,397✔
533
{
534
  // format of arg is -C or -C-
535
  if ( arg[2] == '\0' )
1,397✔
536
    return true;
1,397✔
537
  else if ( arg[2] == '-' )
×
538
    return false;
×
539
  else if ( arg[2] == '+' )
×
540
    return true;
×
541
  else
542
    return true;
×
543
}
544

545

546
int readargs( int argc, char** argv )
1,976✔
547
{
548
  bool unknown_opt = false;
1,976✔
549

550
  for ( int i = 1; i < argc; i++ )
9,877✔
551
  {
552
    const char* arg = argv[i];
7,901✔
553
#ifdef __linux__
554
    if ( arg[0] == '-' )
7,901✔
555
#else
556
    if ( arg[0] == '/' || arg[0] == '-' )
557
#endif
558
    {
559
      switch ( arg[1] )
5,927✔
560
      {
561
      case 'W':
1✔
562
        watch_source = true;
1✔
563
        compilercfg.GenerateDependencyInfo = true;
1✔
564
        keep_building = true;
1✔
565
        compilercfg.ThreadedCompilation =
1✔
566
            false;  // limitation since dependency gathering is not thread-safe
567
        break;
1✔
568

569
      case 'A':  // skip it at this point.
2✔
570
        break;
2✔
571

572
      case 'c':
×
573
        compilercfg.ErrorOnFileCaseMissmatch = setting_value( arg );
×
574
        break;
×
575

576
      case 'C':  // skip it at this point.
1,974✔
577
        ++i;     // and skip its parameter.
1,974✔
578
        break;
1,974✔
579

580
      case 'd':
×
581
        compilercfg.DisplayDebugs = setting_value( arg );
×
582
        break;
×
583

584
      case 'D':
×
585
        compilercfg.GenerateDependencyInfo = setting_value( arg );
×
586
        break;
×
587

588
      case 'e':
×
589
        expect_compile_failure = true;
×
590
        break;
×
591

592
#ifdef WIN32
593
      case 'E':
594
      {
595
        std::string path = &argv[i][2];
596
        CfgPathEnv = "ECOMPILE_CFG_PATH=" + path;
597
        _putenv( CfgPathEnv.c_str() );
598
      }
599
      break;
600
#endif
601

602
      case 'g':
×
603
      {
604
        auto value = setting_value( arg );
×
605
        if ( !value )
×
606
        {
607
          INFO_PRINTLN( "The OG Compiler has been removed." );
×
608
          unknown_opt = true;
×
609
        }
610
        break;
×
611
      }
612

613
      case 'q':
1,974✔
614
        quiet = true;
1,974✔
615
        break;
1,974✔
616

617
      case 'w':
×
618
        compilercfg.DisplayWarnings = setting_value( arg );
×
619
        if ( argv[i][2] == 'P' )
×
620
          compilercfg.ParanoiaWarnings = true;
×
621
        break;
×
622
      case 'y':
×
623
        compilercfg.ErrorOnWarning = setting_value( arg );
×
624
        break;
×
625

626
      case 'l':
1,396✔
627
        compilercfg.GenerateListing = setting_value( arg );
1,396✔
628
        break;
1,396✔
629

630
      case 'i':
×
631
        include_debug = 1;
×
632
        break;
×
633

634
      case 'r':  // -r[i] [dir] is okay
×
635
        // Only suboption allowed is '-ri' (.inc recurse)
636
        if ( argv[i][2] && argv[i][2] != 'i' )
×
637
        {
638
          // BZZZZT! *error*
639
          unknown_opt = true;
×
640
          break;
×
641
        }
642

643
// Only skip next parameter if it's not an option!!
644
#ifdef __linux__
645
        if ( i + 1 < argc && argv[i + 1][0] != '-' )
×
646
#else
647
        if ( i + 1 < argc && argv[i + 1][0] != '/' && argv[i + 1][0] != '-' )
648
#endif
649
          ++i;
×
650
        break;
×
651

652
      case 't':
×
653
        show_timing_details = true;
×
654
        if ( argv[i][2] == 'v' )
×
655
          timing_quiet_override = true;
×
656
        break;
×
657

658
      case 's':
×
659
        // show_source = true;
660
        compilercfg.DisplaySummary = true;
×
661
        break;
×
662

663
      case 'a':
×
664
        compilercfg.CompileAspPages = setting_value( arg );
×
665
        break;
×
666

667
      case 'm':
×
668
        compilercfg.OptimizeObjectMembers = false;
×
669
        break;
×
670

671
      case 'b':
×
672
        keep_building = true;
×
673
        break;
×
674

675
      case 'u':
×
676
        compilercfg.OnlyCompileUpdatedScripts = setting_value( arg );
×
677
        if ( compilercfg.OnlyCompileUpdatedScripts )
×
678
          compilercfg.GenerateDependencyInfo = true;
×
679
        break;
×
680

681
      case 'F':
578✔
682
      {
683
        if ( argv[i][2] && argv[i][2] == 'i' )
578✔
684
          format_source_inplace = true;
578✔
685
        format_source = true;
578✔
686
        break;
578✔
687
      }
688

689
      case 'f':
×
690
        force_update = true;
×
691
        break;
×
692

693
      case 'v':
1✔
694
        int vlev;
695
        vlev = atoi( &argv[i][2] );
1✔
696
        if ( !vlev )
1✔
697
          vlev = 1;
1✔
698
        compilercfg.VerbosityLevel = vlev;
1✔
699
        break;
1✔
700

701
      case 'x':
1✔
702
        compilercfg.GenerateDebugInfo = setting_value( arg );
1✔
703
        compilercfg.GenerateDebugTextInfo = ( argv[i][2] == 't' );
1✔
704
        break;
1✔
705

706
#ifdef WIN32
707
      case 'P':
708
      {
709
        std::string path = &argv[i][2];
710
        EmPathEnv = "ECOMPILE_PATH_EM=" + path;
711
        IncPathEnv = "ECOMPILE_PATH_INC=" + path;
712
        _putenv( EmPathEnv.c_str() );
713
        _putenv( IncPathEnv.c_str() );
714
      }
715
      break;
716
#endif
717
      case 'T':
×
718
      {
719
        compilercfg.ThreadedCompilation = true;
×
720
        int count = atoi( &argv[i][2] );
×
721
        compilercfg.NumberOfThreads = count;
×
722
        break;
×
723
      }
724
      default:
×
725
        unknown_opt = true;
×
726
        break;
×
727
      }
728
    }
729

730
    if ( unknown_opt )
7,901✔
731
    {
732
      ERROR_PRINTLN( "Unknown option: {}", argv[i] );
×
733
      return 1;
×
734
    }
735
  }
736
  return 0;
1,976✔
737
}
738

739
void apply_configuration()
1,976✔
740
{
741
  em_parse_tree_cache.configure( compilercfg.EmParseTreeCacheSize );
1,976✔
742
  inc_parse_tree_cache.configure( compilercfg.IncParseTreeCacheSize );
1,976✔
743
}
1,976✔
744

745
void recurse_call( const std::vector<fs::path>& basedirs, bool inc_files,
2✔
746
                   const std::function<void( const std::string& )>& callback )
747
{
748
  std::set<std::string> files;
2✔
749
  for ( const auto& basedir : basedirs )
8✔
750
  {
751
    if ( !fs::is_directory( basedir ) )
6✔
752
      continue;
×
753
    std::error_code ec;
6✔
754
    for ( auto dir_itr = fs::recursive_directory_iterator( basedir, ec );
6✔
755
          dir_itr != fs::recursive_directory_iterator(); ++dir_itr )
1,594✔
756
    {
757
      if ( Clib::exit_signalled )
794✔
758
        return;
×
759
      if ( auto fn = dir_itr->path().filename().u8string(); !fn.empty() && *fn.begin() == '.' )
794✔
760
      {
761
        if ( dir_itr->is_directory() )
4✔
762
          dir_itr.disable_recursion_pending();
2✔
763
        continue;
4✔
764
      }
765
      else if ( !dir_itr->is_regular_file() )
790✔
766
        continue;
794✔
767
      const auto ext = dir_itr->path().extension();
693✔
768
      const auto file = dir_itr->path().generic_u8string();
693✔
769
      if ( inc_files )
693✔
770
      {
771
        if ( !ext.compare( ".inc" ) )
×
NEW
772
          if ( files.insert( file ).second )
×
NEW
773
            callback( file );
×
774
      }
775
      else if ( !ext.compare( ".src" ) || !ext.compare( ".hsr" ) ||
1,172✔
776
                ( compilercfg.CompileAspPages && !ext.compare( ".asp" ) ) )
479✔
777
      {
778
        if ( files.insert( file ).second )
214✔
779
          callback( file );
212✔
780
      }
781
    }
699✔
782
  }
783
}
2✔
784

785
void process_dirs( const std::vector<fs::path>& dirs, bool compile_inc )
2✔
786
{
787
  if ( !compilercfg.ThreadedCompilation )
2✔
788
  {
789
    recurse_call( dirs, compile_inc,
2✔
790
                  []( const std::string& file ) { process_file_wrapper( file ); } );
212✔
791
    return;
2✔
792
  }
793
  std::atomic<unsigned> compiled_scripts( 0 );
×
794
  std::atomic<unsigned> uptodate_scripts( 0 );
×
795
  std::atomic<unsigned> error_scripts( 0 );
×
796
  std::atomic<bool> par_keep_building( true );
×
797
  {
798
    unsigned int thread_count = std::max( 2u, std::thread::hardware_concurrency() * 2 );
×
799
    if ( compilercfg.NumberOfThreads )
×
800
      thread_count = static_cast<unsigned>( compilercfg.NumberOfThreads );
×
801
    threadhelp::TaskThreadPool pool( thread_count, "ecompile" );
×
802
    summary.ThreadCount = pool.size();
×
803
    auto callback = [&]( const std::string& file )
×
804
    {
805
      pool.push(
×
806
          [&, file]()
×
807
          {
808
            if ( !par_keep_building || Clib::exit_signalled )
×
809
              return;
×
810
            try
811
            {
812
              if ( process_file( file ) )
×
813
                ++compiled_scripts;
×
814
              else
815
                ++uptodate_scripts;
×
816
            }
817
            catch ( std::exception& e )
×
818
            {
819
              ++compiled_scripts;
×
820
              ++error_scripts;
×
821
              ERROR_PRINTLN( "failed to compile {}: {}", file, e.what() );
×
822
              if ( !keep_building )
×
823
                par_keep_building = false;
×
824
            }
×
825
            catch ( ... )
×
826
            {
827
              par_keep_building = false;
×
828
              Clib::force_backtrace();
×
829
            }
×
830
          } );
831
    };
×
832
    recurse_call( dirs, compile_inc, callback );
×
833
  }
×
834
  summary.CompiledScripts = compiled_scripts;
×
835
  summary.UpToDateScripts = uptodate_scripts;
×
836
  summary.ScriptsWithCompileErrors = error_scripts;
×
837
}
838

839
void DisplaySummary( const Tools::Timer<>& timer )
7✔
840
{
841
  std::string tmp = "Compilation Summary:\n";
7✔
842
  if ( summary.ThreadCount )
7✔
843
    tmp += fmt::format( "    Used {} threads\n", summary.ThreadCount );
×
844
  if ( summary.CompiledScripts )
7✔
845
    tmp += fmt::format( "    Compiled {} script{} in {} ms.\n", summary.CompiledScripts,
7✔
846
                        ( summary.CompiledScripts == 1 ? "" : "s" ), timer.ellapsed() );
21✔
847

848
  if ( summary.ScriptsWithCompileErrors )
7✔
849
    tmp += fmt::format( "    {} of those script{} had errors.\n", summary.ScriptsWithCompileErrors,
1✔
850
                        ( summary.ScriptsWithCompileErrors == 1 ? "" : "s" ) );
3✔
851

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

856
  if ( show_timing_details )
7✔
857
  {
858
    tmp += fmt::format( "    build workspace: {}\n",
×
859
                        (long long)summary.profile.build_workspace_micros / 1000 );
×
860
    tmp += fmt::format( "        - load *.em:   {}\n",
×
861
                        (long long)summary.profile.load_em_micros / 1000 );
×
862
    tmp += fmt::format( "       - parse *.em:   {} ({})\n",
×
863
                        (long long)summary.profile.parse_em_micros / 1000,
×
864
                        (long)summary.profile.parse_em_count );
×
865
    tmp += fmt::format( "         - ast *.em:   {}\n",
×
866
                        (long long)summary.profile.ast_em_micros / 1000 );
×
867
    tmp += fmt::format( "      - parse *.inc:   {} ({})\n",
×
868
                        (long long)summary.profile.parse_inc_micros / 1000,
×
869
                        (long)summary.profile.parse_inc_count );
×
870
    tmp += fmt::format( "        - ast *.inc:   {}\n",
×
871
                        (long long)summary.profile.ast_inc_micros / 1000 );
×
872
    tmp += fmt::format( "      - parse *.src:   {} ({})\n",
×
873
                        (long long)summary.profile.parse_src_micros / 1000,
×
874
                        (long)summary.profile.parse_src_count );
×
875
    tmp += fmt::format( "        - ast *.src:   {}\n",
×
876
                        (long long)summary.profile.ast_src_micros / 1000 );
×
877
    tmp += fmt::format( "  resolve functions:   {}\n",
×
878
                        (long long)summary.profile.ast_resolve_functions_micros / 1000 );
×
879
    tmp += fmt::format( " register constants: {}\n",
×
880
                        (long long)summary.profile.register_const_declarations_micros / 1000 );
×
881
    tmp += fmt::format( "            analyze: {}\n",
×
882
                        (long long)summary.profile.analyze_micros / 1000 );
×
883
    tmp += fmt::format( "           optimize: {}\n",
×
884
                        (long long)summary.profile.optimize_micros / 1000 );
×
885
    tmp += fmt::format( "       disambiguate: {}\n",
×
886
                        (long long)summary.profile.disambiguate_micros / 1000 );
×
887
    tmp += fmt::format( "      generate code: {}\n",
×
888
                        (long long)summary.profile.codegen_micros / 1000 );
×
889
    tmp += fmt::format( "  prune cache (sel): {}\n",
×
890
                        (long long)summary.profile.prune_cache_select_micros / 1000 );
×
891
    tmp += fmt::format( "  prune cache (del): {}\n",
×
892
                        (long long)summary.profile.prune_cache_delete_micros / 1000 );
×
893
    tmp += "\n";
×
894
    tmp += fmt::format( "      - ambiguities: {}\n", (long)summary.profile.ambiguities );
×
895
    tmp += fmt::format( "       - cache hits: {}\n", (long)summary.profile.cache_hits );
×
896
    tmp += fmt::format( "     - cache misses: {}\n", (long)summary.profile.cache_misses );
×
897
  }
898

899
  INFO_PRINTLN( tmp );
7✔
900
}
7✔
901

902
void EnterWatchMode()
1✔
903
{
904
  std::list<WatchFileMessage> watch_messages;
1✔
905
  std::set<fs::path> to_compile;
1✔
906
  efsw::FileWatcher fileWatcher;
1✔
907
  EfswFileWatchListener listener( fileWatcher,
908
                                  std::set<fs::path>{ ".src", ".hsr", ".asp", ".inc", ".em" } );
6✔
909

910
  auto handle_compile_file = [&]( const fs::path& filepath )
5✔
911
  {
912
    // Existing file, compile all owners dependent on it.
913
    if ( auto itr = dependency_owners.find( filepath ); itr != dependency_owners.end() )
5✔
914
    {
915
      to_compile.insert( itr->second.begin(), itr->second.end() );
4✔
916
    }
917
    // New file, compile only this file (if it is compilable).
918
    else
919
    {
920
      auto ext = filepath.extension();
1✔
921
      if ( ext.compare( ".src" ) == 0 || ext.compare( ".hsr" ) == 0 || ext.compare( ".asp" ) == 0 )
1✔
922
      {
923
        to_compile.emplace( filepath );
1✔
924
      }
925
    }
1✔
926
  };
5✔
927

928
  auto handle_delete_file = [&]( const fs::path& filepath )
1✔
929
  {
930
    to_compile.erase( filepath );
1✔
931
    if ( auto itr = owner_dependencies.find( filepath ); itr != owner_dependencies.end() )
1✔
932
    {
933
      // For all dependencies, remove the deleted file from set of owners for
934
      // that dependency.
935
      for ( const auto& depnamepath : itr->second )
4✔
936
      {
937
        // If dependency exists in map of dependency -> owners
938
        if ( auto owners_itr = dependency_owners.find( depnamepath );
3✔
939
             owners_itr != dependency_owners.end() )
3✔
940
        {
941
          // Erase filepath from set of owners
942
          owners_itr->second.erase( filepath );
3✔
943

944
          // If owners set is empty, this dependency is no longer used by
945
          // anything.
946
          if ( owners_itr->second.empty() )
3✔
947
          {
948
            //  Erase from map of dependency -> owners.
949
            dependency_owners.erase( owners_itr );
1✔
950
            listener.remove_file( depnamepath );
1✔
951
            VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
1✔
952
          }
953
        }
954
      }
955
      owner_dependencies.erase( itr );
1✔
956
    }
957
  };
1✔
958

959
  auto add_dir = [&]( const std::string& elem )
4✔
960
  {
961
    fs::path dir( elem );
4✔
962
    if ( fs::exists( dir ) )
4✔
963
    {
964
      auto dirpath = fs::canonical( dir );
4✔
965
      if ( listener.add_watch_dir( dirpath ) )
4✔
966
      {
967
        VERBOSE_PRINTLN( "Add watch dir {}", dirpath );
4✔
968
      }
969
    }
4✔
970
  };
4✔
971

972
  for ( const auto& dep_owners : dependency_owners )
127✔
973
  {
974
    listener.add_file( dep_owners.first );
126✔
975
  }
976

977
  for ( const auto& directory : compiled_dirs )
4✔
978
  {
979
    listener.add_dir( directory );
3✔
980
  }
981

982
  for ( const auto& elem : compilercfg.PackageRoot )
2✔
983
  {
984
    add_dir( elem );
1✔
985
  }
986

987
  add_dir( compilercfg.ModuleDirectory );
1✔
988
  add_dir( compilercfg.IncludeDirectory );
1✔
989
  add_dir( compilercfg.PolScriptRoot );
1✔
990

991
  fileWatcher.watch();
1✔
992

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

996
  while ( !Clib::exit_signalled )
8✔
997
  {
998
    std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
6✔
999
    listener.take_messages( watch_messages );
6✔
1000
    if ( !watch_messages.empty() )
6✔
1001
    {
1002
      for ( auto const& message : watch_messages )
11✔
1003
      {
1004
        const auto& filepath = message.filepath;
6✔
1005
        const auto& old_filepath = message.old_filepath;
6✔
1006
        VERBOSE_PRINTLN( "Event: filename={}, old_filename={}", filepath, old_filepath );
6✔
1007

1008
        // created or modified
1009
        if ( old_filepath.empty() )
6✔
1010
        {
1011
          handle_compile_file( filepath );
5✔
1012
        }
1013
        // deleted
1014
        else if ( filepath.empty() )
1✔
1015
        {
1016
          handle_delete_file( old_filepath );
1✔
1017
        }
1018
        // moved
1019
        else
1020
        {
1021
          handle_delete_file( old_filepath );
×
1022
          handle_compile_file( filepath );
×
1023
        }
1024
      }
1025
      watch_messages.clear();
5✔
1026

1027
      if ( !to_compile.empty() )
5✔
1028
      {
1029
        summary.CompiledScripts = 0;
5✔
1030
        summary.UpToDateScripts = 0;
5✔
1031
        summary.ScriptsWithCompileErrors = 0;
5✔
1032
        em_parse_tree_cache.clear();
5✔
1033
        inc_parse_tree_cache.clear();
5✔
1034

1035
        VERBOSE_PRINTLN( "To compile: {}", to_compile );
5✔
1036

1037
        Tools::Timer<> timer;
5✔
1038
        for ( const auto& filepath : to_compile )
19✔
1039
        {
1040
          if ( Clib::exit_signalled )
14✔
1041
          {
1042
            break;
×
1043
          }
1044

1045
          std::set<fs::path> removed_dependencies;
14✔
1046
          std::set<fs::path> new_dependencies;
14✔
1047
          try
1048
          {
1049
            process_file_wrapper( filepath.generic_string(), &removed_dependencies,
14✔
1050
                                  &new_dependencies );
1051
          }
1052
          catch ( ... )
×
1053
          {
1054
          }
×
1055

1056
          VERBOSE_PRINTLN( "New dependencies: {} Removed dependencies: {}", new_dependencies,
14✔
1057
                           removed_dependencies );
1058

1059
          for ( const auto& depnamepath : removed_dependencies )
14✔
1060
          {
1061
            // If a removed dependency has no owner set is empty, we can remove
1062
            // the listener for that dependency.
1063
            if ( dependency_owners.find( depnamepath ) == dependency_owners.end() )
×
1064
            {
1065
              if ( listener.remove_file( depnamepath ) )
×
1066
              {
1067
                VERBOSE_PRINTLN( "Remove watch file {}", depnamepath );
×
1068
              }
1069
            }
1070
          }
1071

1072
          for ( const auto& depnamepath : new_dependencies )
17✔
1073
          {
1074
            if ( listener.add_file( depnamepath ) )
3✔
1075
            {
1076
              VERBOSE_PRINTLN( "Add watch file {}", depnamepath );
1✔
1077
            }
1078
          }
1079
        }
14✔
1080

1081
        timer.stop();
5✔
1082
        if ( compilercfg.DisplaySummary && !quiet )
5✔
1083
        {
1084
          DisplaySummary( timer );
5✔
1085
        }
1086
        to_compile.clear();
5✔
1087
      }
5✔
1088
    }
1089
  }
1090
}
2✔
1091

1092
/**
1093
 * Runs the compilation threads
1094
 */
1095
void AutoCompile()
2✔
1096
{
1097
  bool save = compilercfg.OnlyCompileUpdatedScripts;
2✔
1098
  compilercfg.OnlyCompileUpdatedScripts = compilercfg.UpdateOnlyOnAutoCompile;
2✔
1099
  std::vector<fs::path> dirs;
2✔
1100
  compiled_dirs.emplace( fs::canonical( compilercfg.PolScriptRoot ) );
2✔
1101

1102
  dirs.emplace_back( compilercfg.PolScriptRoot );
2✔
1103
  for ( const auto& pkg : Plib::systemstate.packages )
6✔
1104
  {
1105
    compiled_dirs.emplace( fs::canonical( pkg->dir() ) );
4✔
1106
    dirs.emplace_back( pkg->dir() );
4✔
1107
  }
1108
  process_dirs( dirs, false );
2✔
1109
  compilercfg.OnlyCompileUpdatedScripts = save;
2✔
1110
}
2✔
1111

1112
/**
1113
 * Takes decisions, runs, the compilation, prints summary and cleans after
1114
 */
1115
bool run( int argc, char** argv, int* res )
1,976✔
1116
{
1117
  Clib::enable_exit_signaller();
1,976✔
1118

1119
  load_packages();
1,976✔
1120

1121
  // Determine the run mode and do the compile itself
1122
  Tools::Timer<> timer;
1,976✔
1123
  bool any = false;
1,976✔
1124

1125
  for ( int i = 1; i < argc; i++ )
9,635✔
1126
  {
1127
#ifdef __linux__
1128
    if ( argv[i][0] == '-' )
7,901✔
1129
#else
1130
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
1131
#endif
1132
    {
1133
      if ( argv[i][1] == 'A' )
5,927✔
1134
      {
1135
        compilercfg.UpdateOnlyOnAutoCompile = ( argv[i][2] == 'u' );
2✔
1136
        any = true;
2✔
1137

1138
        AutoCompile();
2✔
1139
      }
1140
      else if ( argv[i][1] == 'r' )
5,925✔
1141
      {
1142
        any = true;
×
1143
        std::string dir( "." );
×
1144
        bool compile_inc = ( argv[i][2] == 'i' );  // compile .inc files
×
1145

1146
        ++i;
×
1147
        if ( i < argc && argv[i] && argv[i][0] != '-' )
×
1148
          dir.assign( argv[i] );
×
1149

1150
        compiled_dirs.emplace( fs::canonical( dir ) );
×
1151
        process_dirs( { dir }, compile_inc );
×
1152
      }
×
1153
      else if ( argv[i][1] == 'C' )
5,925✔
1154
      {
1155
        ++i;  // skip the config file pathname
1,974✔
1156
      }
1157
      // and skip any other option.
1158
    }
1159
    else
1160
    {
1161
      any = true;
1,974✔
1162
#ifdef _WIN32
1163
      Clib::forspec( argv[i], []( const char* pathname ) { process_file_wrapper( pathname ); } );
1164
#else
1165
      process_file_wrapper( argv[i] );
2,458✔
1166
#endif
1167
    }
1168
  }
1169

1170
  if ( !any && compilercfg.AutoCompileByDefault )
1,734✔
1171
  {
1172
    any = true;
×
1173
    AutoCompile();
×
1174
  }
1175

1176
  // Execution is completed: start final/cleanup tasks
1177
  timer.stop();
1,734✔
1178

1179
  if ( any && compilercfg.DisplaySummary && !quiet )
1,734✔
1180
  {
1181
    DisplaySummary( timer );
2✔
1182
  }
1183

1184
  if ( watch_source )
1,734✔
1185
  {
1186
    EnterWatchMode();
1✔
1187
  }
1188

1189
  Plib::systemstate.deinitialize();
1,734✔
1190

1191
  if ( summary.ScriptsWithCompileErrors )
1,734✔
1192
    *res = 1;
×
1193
  else
1194
    *res = 0;
1,734✔
1195
  return any;
1,734✔
1196
}
1,976✔
1197

1198
void read_config_file( int argc, char* argv[] )
1,976✔
1199
{
1200
  for ( int i = 1; i < argc; i++ )
5,929✔
1201
  {
1202
    if ( argv[i][0] == '/' || argv[i][0] == '-' )
5,927✔
1203
    {
1204
      // -C cfgpath
1205
      if ( argv[i][1] == 'C' )
5,927✔
1206
      {
1207
        ++i;
1,974✔
1208
        if ( i == argc )
1,974✔
1209
          throw std::runtime_error( "-C specified without pathname" );
×
1210

1211
        compilercfg.Read( std::string( argv[i] ) );
1,974✔
1212
        return;
1,974✔
1213
      }
1214
    }
1215
  }
1216

1217
  // check ECOMPILE_CFG_PATH environment variable
1218
  const char* env_ecompile_cfg_path = getenv( "ECOMPILE_CFG_PATH" );
2✔
1219
  if ( env_ecompile_cfg_path != nullptr )
2✔
1220
  {
1221
    compilercfg.Read( std::string( env_ecompile_cfg_path ) );
×
1222
    return;
×
1223
  }
1224

1225
  // no -C arg, so use binary path (hope it's right..sigh.)
1226
  std::string cfgpath = PROG_CONFIG::programDir() + "ecompile.cfg";
2✔
1227
  if ( Clib::FileExists( "ecompile.cfg" ) )
2✔
1228
  {
1229
    compilercfg.Read( "ecompile.cfg" );
2✔
1230
  }
1231
  else if ( Clib::FileExists( cfgpath ) )
×
1232
  {
1233
    compilercfg.Read( cfgpath );
×
1234
  }
1235
  else
1236
  {
1237
    ERROR_PRINTLN( "Could not find {}; using defaults.", cfgpath );
×
1238
    compilercfg.SetDefaults();
×
1239
  }
1240
}
2✔
1241

1242
/**
1243
 * This is the main entry point for ecompile program
1244
 */
1245
int ECompileMain::main()
1,976✔
1246
{
1247
  Clib::Logging::global_logger->disableFileLog();
1,976✔
1248

1249
  const std::vector<std::string>& binArgs = programArgs();
1,976✔
1250

1251
/**********************************************
1252
 * TODO: rework the following cruft from former uotool.cpp
1253
 **********************************************/
1254
#ifdef _WIN32
1255
  Clib::MiniDumper::Initialize();
1256
#endif
1257

1258
  ECompile::read_config_file( s_argc, s_argv );
1,976✔
1259

1260
  /**********************************************
1261
   * show help
1262
   **********************************************/
1263
  if ( binArgs.size() == 1 && !compilercfg.AutoCompileByDefault )
1,976✔
1264
  {
1265
    showHelp();
×
1266
    return 0;  // return "okay"
×
1267
  }
1268

1269
  watch_source = compilercfg.WatchModeByDefault;
1,976✔
1270
  if ( watch_source )
1,976✔
1271
  {
1272
    compilercfg.GenerateDependencyInfo = true;
×
1273
    keep_building = true;
×
1274
    compilercfg.ThreadedCompilation =
×
1275
        false;  // limitation since dependency gathering is not thread-safe
1276
  }
1277

1278
  int res = ECompile::readargs( s_argc, s_argv );
1,976✔
1279
  if ( res )
1,976✔
1280
  {
1281
    showHelp();
×
1282
    return res;
×
1283
  }
1284

1285
  ECompile::apply_configuration();
1,976✔
1286

1287
  if ( !ECompile::quiet )
1,976✔
1288
  {
1289
    // vX.YY
1290
    INFO_PRINTLN( "EScript Compiler v1.{}\n{}\n", ESCRIPT_FILE_VER_CURRENT, POL_COPYRIGHT );
2✔
1291
  }
1292

1293
  int prog_res = 1;
1,976✔
1294
  bool didanything = ECompile::run( s_argc, s_argv, &prog_res );
1,976✔
1295

1296
  if ( !didanything )
1,734✔
1297
  {
1298
    showHelp();
×
1299
    return 1;
×
1300
  }
1301
  return prog_res;
1,734✔
1302
}
1303
}  // namespace ECompile
1304
}  // namespace Pol
1305

1306
///////////////////////////////////////////////////////////////////////////////
1307
///////////////////////////////////////////////////////////////////////////////
1308
///////////////////////////////////////////////////////////////////////////////
1309

1310
int main( int argc, char* argv[] )
1,976✔
1311
{
1312
  Pol::ECompile::s_argc = argc;
1,976✔
1313
  Pol::ECompile::s_argv = argv;
1,976✔
1314

1315
  Pol::ECompile::ECompileMain* ECompileMain = new Pol::ECompile::ECompileMain();
1,976✔
1316
  ECompileMain->start( argc, argv );
1,976✔
1317
}
×
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