1 // =================================
   2 // Copyright (c) 2020 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 #include <cpp2cm/cpp2cm/Project.hpp>
   7 #include <cpp2cm/cpp2cm/Merge.hpp>
   8 #include <cpp2cm/cpp2cm/Converter.hpp>
   9 #include <cpp2cm/cpp2cm/PatchFileParser.hpp>
  10 #include <sngcpp/pp/Evaluator.hpp>
  11 #include <sngcpp/ast/SourceFile.hpp>
  12 #include <sngcpp/lexer/CppLexer.hpp>
  13 #include <sngcpp/parser/SourceFile.hpp>
  14 #include <sngcpp/symbols/SymbolCreator.hpp>
  15 #include <sngcpp/binder/StatementBinder.hpp>
  16 #include <sngcpp/binder/VirtualBinder.hpp>
  17 #include <sngcpp/ast/Reader.hpp>
  18 #include <sngcpp/ast/Writer.hpp>
  19 #include <sngcm/cmlexer/CmajorLexer.hpp>
  20 #include <sngcm/cmparser/CompileUnit.hpp>
  21 #include <sngcm/ast/SourceWriter.hpp>
  22 #include <sngcm/ast/Merge.hpp>
  23 #include <sngxml/dom/Parser.hpp>
  24 #include <sngxml/dom/Element.hpp>
  25 #include <sngxml/xpath/XPathEvaluate.hpp>
  26 #include <soulng/lexer/TrivialLexer.hpp>
  27 #include <soulng/util/Path.hpp>
  28 #include <soulng/util/Unicode.hpp>
  29 #include <soulng/util/Util.hpp>
  30 #include <boost/filesystem.hpp>
  31 #include <iostream>
  32 
  33 namespace cpp2cm {
  34 
  35 using namespace soulng::util;
  36 using namespace soulng::unicode;
  37 
  38 Project::Project(const std::string& systemXmlFilePath_const std::string& xmlFilePath_bool nothrowStatus_) :
  39     systemXmlFilePath(systemXmlFilePath_)systemRootDir(Path::GetDirectoryName(systemXmlFilePath))
  40     xmlFilePath(xmlFilePath_)projectRootDir(GetFullPath(Path::GetDirectoryName(xmlFilePath)))doc(sngxml::dom::ReadDocument(xmlFilePath))verbose(false)system(false)
  41     nothrowList(nothrowStatus_)
  42 {
  43     sngxml::dom::Element* projectElement = doc->DocumentElement();
  44     name = projectElement->GetAttribute(U"name");
  45     if (name.empty())
  46     {
  47         throw std::runtime_error("project name not set in '" + xmlFilePath + "'");
  48     }
  49     type = projectElement->GetAttribute(U"type");
  50     if (type.empty())
  51     {
  52         type = U"library";
  53     }
  54     targetName = projectElement->GetAttribute(U"targetName");
  55     if (targetName.empty())
  56     {
  57         targetName = name;
  58     }
  59     mergeDir = Path::Combine(projectRootDir"merge");
  60     SetStageDir();
  61     SetAstFilePath();
  62 }
  63 
  64 void Project::Process(bool verboseProcessType processType)
  65 {
  66     this->verbose = verbose;
  67     ReadFilter();
  68     ReadNothrowList();
  69     ReadVCXProjectFilePath();
  70     ReadSources();
  71     ReadTextFiles();
  72     ReadMergeDirFiles();
  73     ReadIncludePath();
  74     ReadTargetDir();
  75     ReadInstallDir();
  76     ReadMap();
  77     ReadReferences();
  78     BuildAst();
  79     WriteAst();
  80     Import();
  81     BuildSymbolTable();
  82     WriteSymbolTableXml();
  83     Convert(processType);
  84 }
  85 
  86 void Project::ReadVCXProjectFilePath()
  87 {
  88     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/vcxproject"doc.get());
  89     if (result)
  90     {
  91         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
  92         {
  93             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
  94             int n = nodeSet->Length();
  95             for (int i = 0; i < n; ++i)
  96             {
  97                 sngxml::dom::Node* node = (*nodeSet)[i];
  98                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
  99                 {
 100                     sngxml::dom::Element* vcxprojectElement = static_cast<sngxml::dom::Element*>(node);
 101                     std::u32string fileAttr = vcxprojectElement->GetAttribute(U"file");
 102                     if (!fileAttr.empty())
 103                     {
 104                         vcxprojectFilePath = GetFullPath(Path::Combine(projectRootDirPath::MakeCanonical(ToUtf8(fileAttr))));
 105                         vcxprojectRootDir = Path::GetDirectoryName(vcxprojectFilePath);
 106                     }
 107                 }
 108             }
 109         }
 110     }
 111 }
 112 
 113 void Project::ReadSources()
 114 {
 115     if (!vcxprojectFilePath.empty())
 116     {
 117         ReadVCSources();
 118         return;
 119     }
 120     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/source"doc.get());
 121     if (result)
 122     {
 123         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 124         {
 125             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 126             int n = nodeSet->Length();
 127             for (int i = 0; i < n; ++i)
 128             {
 129                 sngxml::dom::Node* node = (*nodeSet)[i];
 130                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 131                 {
 132                     sngxml::dom::Element* sourceElement = static_cast<sngxml::dom::Element*>(node);
 133                     std::u32string fileAttr = sourceElement->GetAttribute(U"file");
 134                     if (!fileAttr.empty())
 135                     {
 136                         File file(fileAttrGetFullPath(Path::Combine(projectRootDirToUtf8(fileAttr))));
 137                         filters.Apply(file);
 138                         if (file.Included())
 139                         {
 140                             files.push_back(file);
 141                         }
 142                     }
 143                 }
 144             }
 145         }
 146     }
 147 }
 148 
 149 void Project::ReadVCSources()
 150 {
 151     if (verbose)
 152     {
 153         std::cout << ToUtf8(name) + "> " << vcxprojectFilePath << std::endl;
 154     }
 155     std::unique_ptr<sngxml::dom::Document> vcxProjectDoc = sngxml::dom::ReadDocument(vcxprojectFilePath);
 156     std::unique_ptr<sngxml::xpath::XPathObject> hppResult = sngxml::xpath::Evaluate(U"/Project/ItemGroup/ClInclude"vcxProjectDoc.get());
 157     if (hppResult)
 158     {
 159         if (hppResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 160         {
 161             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(hppResult.get());
 162             int n = nodeSet->Length();
 163             for (int i = 0; i < n; ++i)
 164             {
 165                 sngxml::dom::Node* node = (*nodeSet)[i];
 166                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 167                 {
 168                     sngxml::dom::Element* includeFileElement = static_cast<sngxml::dom::Element*>(node);
 169                     std::u32string hppFileName = includeFileElement->GetAttribute(U"Include");
 170                     if (!hppFileName.empty())
 171                     {
 172                         File hppFile(hppFileNameGetFullPath(Path::Combine(vcxprojectRootDirToUtf8(hppFileName))));
 173                         filters.Apply(hppFile);
 174                         if (hppFile.Included())
 175                         {
 176                             files.push_back(hppFile);
 177                             headerFiles.AddProjectHeaderFile(hppFile.Path());
 178                         }
 179                     }
 180                 }
 181             }
 182         }
 183     }
 184     std::unique_ptr<sngxml::xpath::XPathObject> cppResult = sngxml::xpath::Evaluate(U"/Project/ItemGroup/ClCompile"vcxProjectDoc.get());
 185     if (cppResult)
 186     {
 187         if (cppResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 188         {
 189             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(cppResult.get());
 190             int n = nodeSet->Length();
 191             for (int i = 0; i < n; ++i)
 192             {
 193                 sngxml::dom::Node* node = (*nodeSet)[i];
 194                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 195                 {
 196                     sngxml::dom::Element* includeFileElement = static_cast<sngxml::dom::Element*>(node);
 197                     std::u32string cppFileName = includeFileElement->GetAttribute(U"Include");
 198                     if (!cppFileName.empty())
 199                     {
 200                         File cppFile(cppFileNameGetFullPath(Path::Combine(vcxprojectRootDirToUtf8(cppFileName))));
 201                         filters.Apply(cppFile);
 202                         if (cppFile.Included())
 203                         {
 204                             files.push_back(cppFile);
 205                         }
 206                     }
 207                 }
 208             }
 209         }
 210     }
 211 }
 212 
 213 void Project::ReadTextFiles()
 214 {
 215     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/text/file"doc.get());
 216     if (result)
 217     {
 218         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 219         {
 220             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 221             int n = nodeSet->Length();
 222             for (int i = 0; i < n; ++i)
 223             {
 224                 sngxml::dom::Node* node = (*nodeSet)[i];
 225                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 226                 {
 227                     sngxml::dom::Element* textFileElement = static_cast<sngxml::dom::Element*>(node);
 228                     std::u32string nameAttribute = textFileElement->GetAttribute(U"name");
 229                     if (!nameAttribute.empty())
 230                     {
 231                         textFiles.push_back(nameAttribute);
 232                     }
 233                 }
 234             }
 235         }
 236     }
 237 }
 238 
 239 void Project::ReadIncludePath()
 240 {
 241     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/include"doc.get());
 242     if (result)
 243     {
 244         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 245         {
 246             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 247             int n = nodeSet->Length();
 248             for (int i = 0; i < n; ++i)
 249             {
 250                 sngxml::dom::Node* node = (*nodeSet)[i];
 251                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 252                 {
 253                     sngxml::dom::Element* sourceElement = static_cast<sngxml::dom::Element*>(node);
 254                     std::u32string pathAttr = sourceElement->GetAttribute(U"path");
 255                     if (!pathAttr.empty())
 256                     {
 257                         includePath = ToUtf8(pathAttr);
 258                     }
 259                 }
 260             }
 261         }
 262     }
 263 }
 264 
 265 void Project::ReadTargetDir()
 266 {
 267     if (system) return;
 268     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/target"doc.get());
 269     if (result)
 270     {
 271         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 272         {
 273             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 274             int n = nodeSet->Length();
 275             for (int i = 0; i < n; ++i)
 276             {
 277                 sngxml::dom::Node* node = (*nodeSet)[i];
 278                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 279                 {
 280                     sngxml::dom::Element* targetElement = static_cast<sngxml::dom::Element*>(node);
 281                     std::u32string dirAttr = targetElement->GetAttribute(U"dir");
 282                     if (!dirAttr.empty())
 283                     {
 284                         targetDir = GetFullPath(Path::Combine(projectRootDirToUtf8(dirAttr)));
 285                     }
 286                 }
 287             }
 288         }
 289     }
 290     if (targetDir.empty())
 291     {
 292         throw std::runtime_error("project target directory not set");
 293     }
 294 }
 295 
 296 void Project::ReadInstallDir()
 297 {
 298     if (system) return;
 299     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/install"doc.get());
 300     if (result)
 301     {
 302         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 303         {
 304             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 305             int n = nodeSet->Length();
 306             for (int i = 0; i < n; ++i)
 307             {
 308                 sngxml::dom::Node* node = (*nodeSet)[i];
 309                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 310                 {
 311                     sngxml::dom::Element* installElement = static_cast<sngxml::dom::Element*>(node);
 312                     std::u32string dirAttr = installElement->GetAttribute(U"dir");
 313                     if (!dirAttr.empty())
 314                     {
 315                         installDir = GetFullPath(Path::Combine(projectRootDirPath::MakeCanonical(ToUtf8(dirAttr))));
 316                     }
 317                 }
 318             }
 319         }
 320     }
 321     if (installDir.empty())
 322     {
 323         throw std::runtime_error("project install directory not set");
 324     }
 325 }
 326 
 327 void Project::ReadFilter()
 328 {
 329     std::unique_ptr<sngxml::xpath::XPathObject> fileResult = sngxml::xpath::Evaluate(U"/project/filter/file"doc.get());
 330     if (fileResult)
 331     {
 332         if (fileResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 333         {
 334             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(fileResult.get());
 335             for (int i = 0; i < nodeSet->Length(); ++i)
 336             {
 337                 sngxml::dom::Node* node = (*nodeSet)[i];
 338                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 339                 {
 340                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 341                     std::u32string excludePattern = element->GetAttribute(U"exclude");
 342                     if (!excludePattern.empty())
 343                     {
 344                         Filter filter(contextFilter::Type::excludeexcludePattern);
 345                         filters.Add(filter);
 346                     }
 347                     else
 348                     {
 349                         std::u32string includePattern = element->GetAttribute(U"include");
 350                         if (!includePattern.empty())
 351                         {
 352                             Filter filter(contextFilter::Type::includeincludePattern);
 353                             filters.Add(filter);
 354                         }
 355                     }
 356                 }
 357             }
 358         }
 359     }
 360     std::unique_ptr<sngxml::xpath::XPathObject> functionResult = sngxml::xpath::Evaluate(U"/project/filter/function"doc.get());
 361     if (functionResult)
 362     {
 363         if (functionResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 364         {
 365             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(functionResult.get());
 366             for (int i = 0; i < nodeSet->Length(); ++i)
 367             {
 368                 sngxml::dom::Node* node = (*nodeSet)[i];
 369                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 370                 {
 371                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 372                     std::u32string excludeAttr = element->GetAttribute(U"exclude");
 373                     if (!excludeAttr.empty())
 374                     {
 375                         excludedFunctions.insert(excludeAttr);
 376                     }
 377                 }
 378             }
 379         }
 380     }
 381     std::unique_ptr<sngxml::xpath::XPathObject> classResult = sngxml::xpath::Evaluate(U"/project/filter/class"doc.get());
 382     if (classResult)
 383     {
 384         if (classResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 385         {
 386             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(classResult.get());
 387             for (int i = 0; i < nodeSet->Length(); ++i)
 388             {
 389                 sngxml::dom::Node* node = (*nodeSet)[i];
 390                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 391                 {
 392                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 393                     std::u32string excludeAttr = element->GetAttribute(U"exclude");
 394                     if (!excludeAttr.empty())
 395                     {
 396                         excludedClasses.insert(excludeAttr);
 397                     }
 398                 }
 399             }
 400         }
 401     }
 402 }
 403 
 404 void Project::ReadNothrowList()
 405 {
 406     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/nothrow/item"doc.get());
 407     if (result)
 408     {
 409         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 410         {
 411             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 412             for (int i = 0; i < nodeSet->Length(); ++i)
 413             {
 414                 sngxml::dom::Node* node = (*nodeSet)[i];
 415                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 416                 {
 417                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 418                     std::u32string typeText = element->GetAttribute(U"type");
 419                     Item::Type type = Item::Type::none;
 420                     if (typeText.empty())
 421                     {
 422                         throw std::runtime_error("nothrow item type not specified");
 423                     }
 424                     if (typeText == U"file")
 425                     {
 426                         type = Item::Type::file;
 427                     }
 428                     else if (typeText == U"class")
 429                     {
 430                         type = Item::Type::class_;
 431                     }
 432                     else if (typeText == U"function")
 433                     {
 434                         type = Item::Type::function;
 435                     }
 436                     else
 437                     {
 438                         throw std::runtime_error("unknonwn nothrow item type '" + ToUtf8(typeText) + "'");
 439                     }
 440                     std::u32string excludePattern = element->GetAttribute(U"exclude");
 441                     if (!excludePattern.empty())
 442                     {
 443                         NothrowPattern pattern(contextNothrowPattern::Kind::excludetypeexcludePattern);
 444                         nothrowList.AddPattern(pattern);
 445                     }
 446                     else
 447                     {
 448                         std::u32string includePattern = element->GetAttribute(U"include");
 449                         if (!includePattern.empty())
 450                         {
 451                             NothrowPattern pattern(contextNothrowPattern::Kind::includetypeincludePattern);
 452                             nothrowList.AddPattern(pattern);
 453                         }
 454                         else
 455                         {
 456                             throw std::runtime_error("nothrow pattern kind not specified");
 457                         }
 458                     }
 459                 }
 460             }
 461         }
 462     }
 463 }
 464 
 465 void Project::ReadMap()
 466 {
 467     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/map"doc.get());
 468     if (result)
 469     {
 470         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 471         {
 472             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 473             for (int i = 0; i < nodeSet->Length(); ++i)
 474             {
 475                 sngxml::dom::Node* node = (*nodeSet)[i];
 476                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 477                 {
 478                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 479                     std::u32string mapFileAttr = element->GetAttribute(U"file");
 480                     if (!mapFileAttr.empty())
 481                     {
 482                         std::string mapFilePath = GetFullPath(Path::Combine(projectRootDirToUtf8(mapFileAttr)));
 483                         if (verbose)
 484                         {
 485                             std::cout << ToUtf8(name) << "> " << mapFilePath << std::endl;
 486                         }
 487                         std::unique_ptr<sngxml::dom::Document> mapDoc = sngxml::dom::ReadDocument(mapFilePath);
 488                         MapNamespaces(mapDoc.get());
 489                     }
 490                 }
 491             }
 492         }
 493     }
 494 }
 495 
 496 void Project::ReadReferences()
 497 {
 498     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/references/reference"doc.get());
 499     if (result)
 500     {
 501         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 502         {
 503             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 504             for (int i = 0; i < nodeSet->Length(); ++i)
 505             {
 506                 sngxml::dom::Node* node = (*nodeSet)[i];
 507                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 508                 {
 509                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 510                     std::u32string projectAttr = element->GetAttribute(U"project");
 511                     if (!projectAttr.empty())
 512                     {
 513                         std::u32string kindAttr = element->GetAttribute(U"kind");
 514                         if (kindAttr == U"install")
 515                         {
 516                             installReferences.push_back(projectAttr);
 517                         }
 518                         else if (kindAttr == U"stage")
 519                         {
 520                             stageReferences.push_back(projectAttr);
 521                         }
 522                         else
 523                         {
 524                             installReferences.push_back(projectAttr);
 525                             stageReferences.push_back(projectAttr);
 526                         }
 527                     }
 528                 }
 529             }
 530         }
 531     }
 532 }
 533 
 534 void Project::MapNamespaces(sngxml::dom::Document* mapDoc)
 535 {
 536     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/map/namespace"mapDoc);
 537     if (result)
 538     {
 539         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 540         {
 541             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 542             for (int i = 0; i < nodeSet->Length(); ++i)
 543             {
 544                 sngxml::dom::Node* node = (*nodeSet)[i];
 545                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 546                 {
 547                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 548                     std::u32string source = element->GetAttribute(U"source");
 549                     std::u32string target = element->GetAttribute(U"target");
 550                     map.MapNs(sourcetarget);
 551                 }
 552             }
 553         }
 554     }
 555 }
 556 
 557 void Project::SetStageDir()
 558 {
 559     stageDir = Path::Combine(projectRootDir"stage");
 560     boost::filesystem::create_directories(stageDir);
 561 }
 562 
 563 void Project::SetAstFilePath()
 564 {
 565     astFilePath = GetFullPath(Path::Combine(stageDirToUtf8(name) + ".ast"));
 566 }
 567 
 568 void Project::BuildAst()
 569 {
 570     sngcpp::pp::EvaluationContext evaluationContext;
 571     int fileIndex = 0;
 572     for (const File& file : files)
 573     {
 574         if (file.Included())
 575         {
 576             std::unique_ptr<sngcpp::ast::SourceFileNode> sourceFile;
 577             std::unique_ptr<CppLexer> lexer;
 578             if (verbose)
 579             {
 580                 std::cout << ToUtf8(name) + "> " << file.Path() << std::endl;
 581             }
 582             sngcpp::pp::PP pp(evaluationContext);
 583             pp.root = vcxprojectRootDir;
 584             pp.includePath = Split(includePath';');
 585             pp.projectHeaderFileSet = &headerFiles;
 586             pp.Define(sngcpp::pp::ndebug);
 587             if (verbose)
 588             {
 589                 pp.verbose = true;
 590             }
 591             Preprocess(file.Path()&pp);
 592             std::unique_ptr<std::u32string> content(new std::u32string(std::move(pp.ctext)));
 593             lexer.reset(new CppLexer(content->c_str()content->c_str() + content->length()file.Path()fileIndex));
 594             lexer->SetSeparatorChar('\n');
 595             sourceFile.reset(new sngcpp::ast::SourceFileNode(lexer->GetSpan()file.Path()ToUtf8(file.Name())name));
 596             sourceFile->SetSourceFileIndex(fileIndex);
 597             SourceFileParser::Parse(*lexersourceFile.get());
 598             sourceFile->SetHeaderFilePaths(std::move(pp.headerFilePaths));
 599             sourceFile->SetText(std::move(*content));
 600             sourceFile->ComputeLineStarts();
 601             if (ast)
 602             {
 603                 soulng::lexer::Span span = ast->GetSpan();
 604                 ast.reset(new sngcpp::ast::SourceFileSequenceNode(spanast.release()sourceFile.release()));
 605             }
 606             else
 607             {
 608                 ast.reset(sourceFile.release());
 609             }
 610             ++fileIndex;
 611             lexers.push_back(std::unique_ptr<soulng::lexer::Lexer>(lexer.release()));
 612         }
 613     }
 614 }
 615 
 616 void Project::WriteAst()
 617 {
 618     if (!ast) return;
 619     std::vector<soulng::lexer::Lexer*> lx;
 620     for (const auto& lexer : lexers)
 621     {
 622         lx.push_back(lexer.get());
 623     }
 624     soulng::util::BinaryWriter binaryWriter(astFilePath);
 625     sngcpp::ast::Writer writer(lxbinaryWriter);
 626     ast->Write(writer);
 627     if (verbose)
 628     {
 629         std::cout << ToUtf8(name) << "==> " << astFilePath << std::endl;
 630     }
 631 }
 632 
 633 void Project::ReadAst()
 634 {
 635     if (!boost::filesystem::exists(astFilePath)) return;
 636     if (verbose)
 637     {
 638         std::cout << ToUtf8(name) + "> " << astFilePath << std::endl;
 639     }
 640     soulng::util::BinaryReader binaryReder(astFilePath);
 641     sngcpp::ast::Reader reader(binaryReder);
 642     ast.reset(reader.ReadNode());
 643 }
 644 
 645 void Project::Import()
 646 {
 647     ReadSystemProjects();
 648     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/imports/import"doc.get());
 649     if (result)
 650     {
 651         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 652         {
 653             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 654             int n = nodeSet->Length();
 655             for (int i = 0; i < n; ++i)
 656             {
 657                 sngxml::dom::Node* node = (*nodeSet)[i];
 658                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 659                 {
 660                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 661                     std::u32string nameAttr = element->GetAttribute(U"name");
 662                     if (!nameAttr.empty())
 663                     {
 664                         auto it = projectMap.find(nameAttr);
 665                         if (it != projectMap.cend())
 666                         {
 667                             Project* importProject = it->second;
 668                             importProject->system = true;
 669                             imports.push_back(importProject);
 670                         }
 671                         else
 672                         {
 673                             throw std::runtime_error("project '" + ToUtf8(nameAttr) + "' not found");
 674                         }
 675                     }
 676                     else
 677                     {
 678                         std::u32string projectAttr = element->GetAttribute(U"project");
 679                         if (!projectAttr.empty())
 680                         {
 681                             std::string projectXmlFilePath = GetFullPath(Path::Combine(projectRootDirToUtf8(projectAttr)));
 682                             std::unique_ptr<Project> project(new Project(systemXmlFilePathprojectXmlFilePathnothrowList.Verbose()));
 683                             if (verbose)
 684                             {
 685                                 project->verbose = true;
 686                             }
 687                             projectMap[project->name] = project.get();
 688                             imports.push_back(project.get());
 689                             projects.push_back(std::move(project));
 690                         }
 691                         else
 692                         {
 693                             throw std::runtime_error("import must have either 'name' or 'project' attribute");
 694                         }
 695                     }
 696                 }
 697             }
 698         }
 699     }
 700 }
 701 
 702 void Project::ReadSystemProjects()
 703 {
 704     if (system) return;
 705     std::unique_ptr<sngxml::dom::Document> systemXmlDoc = sngxml::dom::ReadDocument(systemXmlFilePath);
 706     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/cpp2cm/projects/project"systemXmlDoc.get());
 707     if (result)
 708     {
 709         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 710         {
 711             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 712             int n = nodeSet->Length();
 713             for (int i = 0; i < n; ++i)
 714             {
 715                 sngxml::dom::Node* node = (*nodeSet)[i];
 716                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 717                 {
 718                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 719                     std::u32string fileAttr = element->GetAttribute(U"file");
 720                     if (!fileAttr.empty())
 721                     {
 722                         std::string projectXmlFilePath = GetFullPath(Path::Combine(systemRootDirToUtf8(fileAttr)));
 723                         std::unique_ptr<Project> project(new Project(systemXmlFilePathprojectXmlFilePathnothrowList.Verbose()));
 724                         if (verbose)
 725                         {
 726                             project->verbose = true;
 727                         }
 728                         projectMap[project->name] = project.get();
 729                         projects.push_back(std::move(project));
 730                     }
 731                 }
 732             }
 733         }
 734     }
 735 }
 736 
 737 void Project::BuildSymbolTable()
 738 {
 739     if (!ast) return;
 740     sngcpp::ast::ResolveSourceFiles(ast.get()sourceFiles);
 741     for (auto& sourceFile : sourceFiles)
 742     {
 743         sourceFileMap[sourceFile->SourceFilePath()] = sourceFile;
 744     }
 745     int n = imports.size();
 746     for (int i = 0; i < n; ++i)
 747     {
 748         Project* importProject = imports[i];
 749         importProject->ReadAst();
 750         sngcpp::symbols::SymbolCreator symbolCreator(symbolTableimportProject->name);
 751         importProject->ast->Accept(symbolCreator);
 752         sngcpp::binder::TypeBinder typeBinder(symbolTablethis);
 753         importProject->ast->Accept(typeBinder);
 754     }
 755     sngcpp::symbols::SymbolCreator symbolCreator(symbolTablename);
 756     if (system)
 757     {
 758         symbolCreator.SetCpp2CmMode();
 759     }
 760     ast->Accept(symbolCreator);
 761     sngcpp::binder::TypeBinder typeBinder(symbolTablethis);
 762     ast->Accept(typeBinder);
 763     std::vector<std::std::unique_ptr<sngcpp::binder::BoundSourceFile>>boundSourceFiles=typeBinder.SourceFiles();
 764     sngcpp::binder::ResolveOverrideSets(typeBinder.ClassesHavingVirtualFunctions());
 765     std::unordered_map<sngcpp::ast::SourceFileNode*sngcpp::binder::BoundSourceFile*> boundSourceFileMap = typeBinder.SourceFileMap();
 766     sngcpp::binder::StatementBinder statementBinder(symbolTableboundSourceFileMap);
 767     ast->Accept(statementBinder);
 768 }
 769 
 770 void Project::WriteSymbolTableXml()
 771 {
 772     std::string symbolTableXmlFilePath = GetFullPath(Path::Combine(stageDirToUtf8(name) + ".xml"));
 773     std::unique_ptr<sngxml::dom::Document> symbolTableXmlDoc = symbolTable.ToDomDocument();
 774     if (system)
 775     {
 776         if (boost::filesystem::exists(symbolTableXmlFilePath))
 777         {
 778             std::unique_ptr<sngxml::dom::Document> stageSymbolTableXmlDoc = sngxml::dom::ReadDocument(symbolTableXmlFilePath);
 779             symbolTableXmlDoc = Merge(symbolTableXmlDoc.get()stageSymbolTableXmlDoc);
 780         }
 781     }
 782     std::ofstream symbolTableXmlFile(symbolTableXmlFilePath);
 783     soulng::util::CodeFormatter formatter(symbolTableXmlFile);
 784     symbolTableXmlDoc->Write(formatter);
 785     if (verbose)
 786     {
 787         std::cout << ToUtf8(name) + "==> " << symbolTableXmlFilePath << std::endl;
 788     }
 789 }
 790 
 791 void Project::ReadSymbolTableXml()
 792 {
 793     std::string symbolTableXmlFilePath = GetFullPath(Path::Combine(stageDirToUtf8(name) + ".xml"));
 794     symbolTableXmlDoc = sngxml::dom::ReadDocument(symbolTableXmlFilePath);
 795 }
 796 
 797 void Project::Convert(ProcessType processType)
 798 {
 799     if (system) return;
 800     std::string dir;
 801     if (processType == ProcessType::stage)
 802     {
 803         dir = targetDir;
 804     }
 805     else if (processType == ProcessType::install)
 806     {
 807         dir = installDir;
 808     }
 809     boost::filesystem::create_directories(dir);
 810     map.SetCurrentProjectName(name);
 811     Converter converter(verbosetargetDirsymbolTablemapexcludedClassesexcludedFunctionsnothrowList);
 812     map.SetSymbolTable(&symbolTable);
 813     for (Project* importProject : imports)
 814     {
 815         if (importProject->system)
 816         {
 817             importProject->ReadSymbolTableXml();
 818             map.AddSourceXmlDoc(importProject->symbolTableXmlDoc.get()importProject->name);
 819         }
 820     }
 821     ast->Accept(converter);
 822     converter.Write();
 823     ReadPatchFiles();
 824     SourceFiles& sourceFiles = converter.GetSourceFiles();
 825     sourceFiles.Sort();
 826     if (!patchFiles.empty())
 827     {
 828         if (verbose)
 829         {
 830             std::cout << "patching:" << std::endl;
 831         }
 832         for (const auto& patchFile : patchFiles)
 833         {
 834             if (verbose)
 835             {
 836                 std::cout << "> " << patchFile->Path() << std::endl;
 837             }
 838             sourceFiles.Apply(patchFile.get());
 839         }
 840     }
 841     sourceFiles.Write();
 842     if (boost::filesystem::exists(mergeDir))
 843     {
 844         if (verbose)
 845         {
 846             std::cout << "merging:" << std::endl;
 847         }
 848         for (const auto& sourceFile : sourceFiles.Get())
 849         {
 850             std::string sourceFilePath = GetFullPath(Path::Combine(mergeDirToUtf8(sourceFile->Name())));
 851             if (boost::filesystem::exists(sourceFilePath))
 852             {
 853                 auto it = mergeFileMap.find(sourceFile->Name());
 854                 if (it != mergeFileMap.cend())
 855                 {
 856                     File* mergeFile = it->second;
 857                     mergeFile->SetIncluded(true);
 858                 }
 859                 std::string sourceContent = ReadFile(sourceFilePath);
 860                 CmajorLexer sourceLexer(ToUtf32(sourceContent)sourceFilePath0);
 861                 ParsingContext sourceCtx;
 862                 std::unique_ptr<sngcm::ast::CompileUnitNode> sourceCu = CompileUnitParser::Parse(sourceLexer&sourceCtx);
 863                 std::string targetFilePath = sourceFile->Path();
 864                 std::string targetContent = ReadFile(targetFilePath);
 865                 CmajorLexer targetLexer(ToUtf32(targetContent)targetFilePath0);
 866                 ParsingContext targetCtx;
 867                 std::unique_ptr<sngcm::ast::CompileUnitNode> targetCu = CompileUnitParser::Parse(targetLexer&targetCtx);
 868                 sngcm::ast::Merge(*sourceCu*targetCu);
 869                 sngcm::ast::ArrangeClassMembers(*targetCu);
 870                 std::ofstream targetFile(targetFilePath);
 871                 CodeFormatter formatter(targetFile);
 872                 sngcm::ast::SourceWriter sourceWriter(formatter);
 873                 targetCu->Accept(sourceWriter);
 874                 if (verbose)
 875                 {
 876                     std::cout << "==> " << targetFilePath << " <- " << sourceFilePath << std::endl;
 877                 }
 878             }
 879         }
 880         for (const auto& mergeFile : mergeDirFiles)
 881         {
 882             if (!mergeFile->Included())
 883             {
 884                 std::string sourceFilePath = mergeFile->Path();
 885                 if (boost::filesystem::exists(sourceFilePath))
 886                 {
 887                     std::string sourceContent = ReadFile(sourceFilePath);
 888                     CmajorLexer sourceLexer(ToUtf32(sourceContent)sourceFilePath0);
 889                     ParsingContext sourceCtx;
 890                     std::unique_ptr<sngcm::ast::CompileUnitNode> cu = CompileUnitParser::Parse(sourceLexer&sourceCtx);
 891                     File* target = new File(mergeFile->Name()GetFullPath(Path::Combine(targetDirPath::GetFileName(mergeFile->Path()))));
 892                     std::string targetFilePath = target->Path();
 893                     std::ofstream targetFile(targetFilePath);
 894                     CodeFormatter formatter(targetFile);
 895                     sngcm::ast::SourceWriter sourceWriter(formatter);
 896                     cu->Accept(sourceWriter);
 897                     extraFiles.push_back(std::unique_ptr<File>(target));
 898                     if (verbose)
 899                     {
 900                         std::cout << "==> " << targetFilePath << " <- " << sourceFilePath << std::endl;
 901                     }
 902                 }
 903             }
 904         }
 905     }
 906     if (processType == ProcessType::install)
 907     {
 908         if (verbose)
 909         {
 910             std::cout << "installing:" << std::endl;
 911         }
 912         for (const auto& sourceFile : sourceFiles.Get())
 913         {
 914             std::string targetFilePath = Path::Combine(dirToUtf8(sourceFile->Name()));
 915             if (boost::filesystem::exists(targetFilePath))
 916             {
 917                 boost::filesystem::remove(targetFilePath);
 918             }
 919             boost::filesystem::copy_file(sourceFile->Path()targetFilePath);
 920             if (verbose)
 921             {
 922                 std::cout << sourceFile->Path() << " -> " << targetFilePath << std::endl;
 923             }
 924         }
 925         for (const auto& extraFile : extraFiles)
 926         {
 927             std::string targetFilePath = Path::Combine(dirToUtf8(extraFile->Name()));
 928             if (boost::filesystem::exists(targetFilePath))
 929             {
 930                 boost::filesystem::remove(targetFilePath);
 931             }
 932             boost::filesystem::copy_file(extraFile->Path()targetFilePath);
 933             if (verbose)
 934             {
 935                 std::cout << extraFile->Path() << " -> " << targetFilePath << std::endl;
 936             }
 937         }
 938     }
 939     if (verbose)
 940     {
 941         std::cout << "creating project file:" << std::endl;
 942     }
 943     std::string projectFilePath = Path::Combine(dirToUtf8(targetName) + ".cmp");
 944     std::ofstream projectFile(projectFilePath);
 945     CodeFormatter formatter(projectFile);
 946     formatter.WriteLine("project " + ToUtf8(targetName) + ";");
 947     if (type == U"library")
 948     {
 949         formatter.WriteLine("target=library;");
 950     }
 951     else if (type == U"program")
 952     {
 953         formatter.WriteLine("target=program;");
 954     }
 955     if (processType == ProcessType::stage)
 956     {
 957         for (const auto& reference : stageReferences)
 958         {
 959             formatter.WriteLine("reference <" + ToUtf8(reference) + ">;");
 960         }
 961     }
 962     else if (processType == ProcessType::install)
 963     {
 964         for (const auto& reference : installReferences)
 965         {
 966             formatter.WriteLine("reference <" + ToUtf8(reference) + ">;");
 967         }
 968     }
 969     std::vector<std::u32string> sourceFileNames;
 970     for (const auto& sourceFile : sourceFiles.Get())
 971     {
 972         sourceFileNames.push_back(sourceFile->Name());
 973     }
 974     for (const auto& extraFile : extraFiles)
 975     {
 976         sourceFileNames.push_back(extraFile->Name());
 977     }
 978     for (const auto& textFile : textFiles)
 979     {
 980         sourceFileNames.push_back(textFile);
 981     }
 982     std::sort(sourceFileNames.begin()sourceFileNames.end());
 983     for (const auto& sourceFileName : sourceFileNames)
 984     {
 985         if (Path::GetExtension(ToUtf8(sourceFileName)) == ".cm")
 986         {
 987             formatter.WriteLine("source <" + ToUtf8(sourceFileName) + ">;");
 988         }
 989         else
 990         {
 991             formatter.WriteLine("text <" + ToUtf8(sourceFileName) + ">;");
 992         }
 993     }
 994     if (verbose)
 995     {
 996         std::cout << "==> " << projectFilePath << std::endl;
 997     }
 998 }
 999 
1000 void Project::ReadPatchFiles()
1001 {
1002     int index = 0;
1003     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/patches/patch"doc.get());
1004     if (result)
1005     {
1006         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
1007         {
1008             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
1009             int n = nodeSet->Length();
1010             for (int i = 0; i < n; ++i)
1011             {
1012                 sngxml::dom::Node* node = (*nodeSet)[i];
1013                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
1014                 {
1015                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
1016                     std::u32string file = element->GetAttribute(U"file");
1017                     if (!file.empty())
1018                     {
1019                         std::string patchFilePath = GetFullPath(Path::Combine(projectRootDirToUtf8(file)));
1020                         std::string content = ReadFile(patchFilePath);
1021                         std::u32string text = ToUtf32(content);
1022                         if (!text.empty())
1023                         {
1024                             TrivialLexer lexer(textpatchFilePathindex++);
1025                             std::unique_ptr<cpp2cm::PatchFile> patchFile = PatchFileParser::Parse(lexer);
1026                             patchFiles.push_back(std::move(patchFile));
1027                         }
1028                     }
1029                 }
1030             }
1031         }
1032     }
1033 }
1034 
1035 void Project::ReadMergeDirFiles()
1036 {
1037     if (!boost::filesystem::exists(mergeDir)) return;
1038     boost::filesystem::directory_iterator it = boost::filesystem::directory_iterator(mergeDir);
1039     while (it != boost::filesystem::directory_iterator())
1040     {
1041         const boost::filesystem::directory_entry& entry = *it;
1042         if (entry.status().type() == boost::filesystem::file_type::regular_file)
1043         {
1044             std::string filePath = entry.path().generic_string();
1045             if (Path::GetExtension(filePath) == ".cm")
1046             {
1047                 File* file = new File(ToUtf32(Path::GetFileName(filePath))GetFullPath(filePath));
1048                 file->SetIncluded(false);
1049                 mergeDirFiles.push_back(std::unique_ptr<File>(file));
1050                 mergeFileMap[file->Name()] = file;
1051             }
1052         }
1053         ++it;
1054     }
1055 }
1056 
1057 sngcpp::ast::SourceFileNode* Project::GetSourceFile(const std::string& sourceFilePath) const
1058 {
1059     auto it = sourceFileMap.find(sourceFilePath);
1060     if (it != sourceFileMap.cend())
1061     {
1062         return it->second;
1063     }
1064     else
1065     {
1066         return nullptr;
1067     }
1068 }
1069 
1070 } // namespace cpp2cm