#include "Codepack.h" FilePath GetCommonFolder( const List& paths ) { auto folder = paths[0].GetFolder(); while (true) { if (From(paths).All([&](const FilePath& path) { return INVLOC.StartsWith(path.GetFullPath(), folder.GetFullPath() + WString::FromChar(folder.Delimiter), Locale::IgnoreCase); })) { return folder; } folder = folder.GetFolder(); } CHECK_FAIL(L"Cannot process files across multiple drives."); } void CollectConditions( Group& categorizedConditions, Group>& conditions, const FilePath& file, const Dictionary& inputFileToOutputFiles ) { vint index = conditions.Keys().IndexOf(file); if (index != -1) { for (auto [condition, path] : conditions.GetByIndex(index)) { auto includeFile = inputFileToOutputFiles[path]; if (!categorizedConditions.Contains(condition, includeFile)) { categorizedConditions.Add(condition, includeFile); } } } } void CombineWriteHeader( List& lines, const Dictionary& inputFileToOutputFiles, Group>& conditionOns, Group>& conditionOffs, const List& files, LazyList externalIncludes ) { lines.Add(L"/***********************************************************************"); lines.Add(L"THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY"); lines.Add(L"DEVELOPER: Zihan Chen(vczh)"); lines.Add(L"***********************************************************************/"); for (auto path : externalIncludes) { lines.Add(L"#include \"" + path + L"\""); } { Group categorizedConditionOns, categorizedConditionOffs; for (auto file : files) { CollectConditions(categorizedConditionOns, conditionOns, file, inputFileToOutputFiles); CollectConditions(categorizedConditionOffs, conditionOffs, file, inputFileToOutputFiles); } for (vint i = 0; i < categorizedConditionOns.Count(); i++) { lines.Add(L"#ifdef " + categorizedConditionOns.Keys()[i]); const auto& onFiles = categorizedConditionOns.GetByIndex(i); for (auto onFile : onFiles) { lines.Add(L"#include \"" + inputFileToOutputFiles[onFile] + L"\""); } lines.Add(L"#endif"); } for (vint i = 0; i < categorizedConditionOffs.Count(); i++) { lines.Add(L"#ifndef " + categorizedConditionOffs.Keys()[i]); const auto& offFiles = categorizedConditionOffs.GetByIndex(i); for (auto offFile : offFiles) { lines.Add(L"#include \"" + offFile + L".h\""); } lines.Add(L"#endif"); } } } void Combine( const Dictionary& inputFileToOutputFiles, // (in) const Dictionary& skippedImportFiles, // (in) Dictionary>& cachedFileToIncludes, // (cache) Group>& conditionOns, // (out) Group>& conditionOffs, // (out) const List& files, // (in) FilePath outputFilePath, // FilePath outputIncludeFilePath, // SortedList& systemIncludes, // LazyList externalIncludes // ) { auto workingDir = outputFilePath.GetFolder(); List sortedFiles; { PartialOrderingProcessor popFiles; Group depGroup; for (auto file : files) { for (auto dep : GetIncludedFiles(file, skippedImportFiles, cachedFileToIncludes, conditionOns, conditionOffs)) { if (files.Contains(dep)) { depGroup.Add(file, dep); } } } popFiles.InitWithGroup(files, depGroup); popFiles.Sort(); bool needExit = false; for (vint i = 0; i < popFiles.components.Count(); i++) { auto& component = popFiles.components[i]; sortedFiles.Add(files[component.firstNode[0]]); if (component.nodeCount > 1) { Console::SetColor(true, false, false, true); Console::WriteLine( L"Error: Cycle dependency found in categories: " + From(component.firstNode, component.firstNode + component.nodeCount) .Select([&](vint nodeIndex) { return L"\r\n" + files[nodeIndex].GetFullPath(); }) .Aggregate([](const WString& a, const WString& b) {return a + b; }) + L"\r\n."); Console::SetColor(true, true, true, false); needExit = true; } } CHECK_ERROR(!needExit, L"Cycle dependency is not allowed"); } { List lines; CombineWriteHeader(lines, inputFileToOutputFiles, conditionOns, conditionOffs, files, externalIncludes); { auto prefix = GetCommonFolder(files); for (auto file : From(sortedFiles).Intersect(files)) { lines.Add(L""); lines.Add(L"/***********************************************************************"); lines.Add(wupper(prefix.GetRelativePathFor(file))); lines.Add(L"***********************************************************************/"); StringReader reader(ReadFile(file)); bool skip = false; while (!reader.IsEnd()) { auto line = reader.ReadLine(); Ptr match; if ((match = regexInstruction.MatchHead(line))) { auto name = match->Groups()[instruction_name][0].Value(); if (name == L"BeginIgnore") { skip = true; } else if (name == L"EndIgnore") { skip = false; } } else if (!skip) { if ((match = regexSystemInclude.MatchHead(line))) { auto systemFile = match->Groups()[systemInclude_path][0].Value(); if (skippedImportFiles.Keys().Contains(systemFile)) continue; if (systemIncludes.Contains(systemFile)) continue; systemIncludes.Add(systemFile); lines.Add(line); } else if (!regexInclude.MatchHead(line)) { lines.Add(line); } } } } } File(outputFilePath).WriteAllLines(lines, true, BomEncoder::Utf8); } { List lines; CombineWriteHeader(lines, inputFileToOutputFiles, conditionOns, conditionOffs, files, externalIncludes); lines.Add(L""); { auto prefix = outputIncludeFilePath.GetFolder(); for (auto file : From(sortedFiles).Intersect(files)) { lines.Add(L"#include \"" + prefix.GetRelativePathFor(file) + L"\""); } } File(outputIncludeFilePath).WriteAllLines(lines, true, BomEncoder::Utf8); } Console::SetColor(false, true, false, true); Console::WriteLine(L"Succeeded to write: " + outputFilePath.GetFullPath()); Console::SetColor(true, true, true, false); }