813 {
814 InitLLVM InitLLVM(argc, argv);
815
816
817 g_originalCwd = fs::current_path();
818
819
820 cl::HideUnrelatedOptions(ToolCategory);
821
822 cl::ParseCommandLineOptions(argc, argv, "ascii-defer transformation tool\n");
823
824 fs::path outputDir = fs::path(OutputDirectoryOption.getValue());
825
826 if (!outputDir.is_absolute()) {
827 outputDir = g_originalCwd / outputDir;
828 }
829 fs::path inputRoot;
830 if (!InputRootOption.getValue().empty()) {
831 inputRoot = fs::path(InputRootOption.getValue());
832 } else {
833 inputRoot = fs::current_path();
834 }
835
836
837 if (!inputRoot.is_absolute()) {
838 std::error_code ec;
839 inputRoot = fs::absolute(inputRoot, ec);
840 if (ec) {
841 llvm::errs() << "Failed to resolve input root path: " << ec.message() << "\n";
842 return 1;
843 }
844 }
845
846 std::vector<std::string> sourcePaths;
847 for (const auto &path : SourcePaths) {
848 if (!path.empty()) {
849 sourcePaths.push_back(path);
850 }
851 }
852
853 if (sourcePaths.empty()) {
854 llvm::errs() << "No translation units specified for transformation. Provide positional source paths.\n";
855 return 1;
856 }
857
858 if (fs::exists(outputDir)) {
859 if (!fs::is_directory(outputDir)) {
860 llvm::errs() << "Output path exists and is not a directory: " << outputDir.c_str() << "\n";
861 return 1;
862 }
863 } else {
864 std::error_code errorCode;
865 fs::create_directories(outputDir, errorCode);
866 if (errorCode) {
867 llvm::errs() << "Failed to create output directory: " << outputDir.c_str() << " - " << errorCode.message()
868 << "\n";
869 return 1;
870 }
871 }
872
873
874 std::string buildPath = BuildPath.getValue();
875 if (buildPath.empty()) {
876 buildPath = ".";
877 }
878 std::string errorMessage;
879 std::unique_ptr<tooling::CompilationDatabase> compilations =
880 tooling::CompilationDatabase::loadFromDirectory(buildPath, errorMessage);
881 if (!compilations) {
882 llvm::errs() << "Error loading compilation database from '" << buildPath << "': " << errorMessage << "\n";
883 return 1;
884 }
885
886 tooling::ClangTool tool(*compilations, sourcePaths);
887
888
889
890
891
892 std::vector<std::string> prependArgs;
893
894
895
896 std::string resourceDir;
897
898#ifdef CLANG_RESOURCE_DIR
899 if (llvm::sys::fs::exists(CLANG_RESOURCE_DIR)) {
900 resourceDir = CLANG_RESOURCE_DIR;
901
902 } else {
903 if (!QuietMode) {
904 llvm::errs() << "Embedded clang resource directory not found: " << CLANG_RESOURCE_DIR << "\n";
905 }
906 }
907#endif
908
909
910 if (resourceDir.empty()) {
911
912 std::vector<std::string> searchPaths;
913
914#ifdef __APPLE__
915
916 searchPaths.push_back("/opt/homebrew/opt/llvm/lib/clang");
917
918 searchPaths.push_back("/usr/local/opt/llvm/lib/clang");
919
920 searchPaths.push_back(
921 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang");
922
923 searchPaths.push_back("/Library/Developer/CommandLineTools/usr/lib/clang");
924#endif
925#ifdef __linux__
926
927 searchPaths.push_back("/usr/lib/llvm-22/lib/clang");
928 searchPaths.push_back("/usr/lib/llvm-21/lib/clang");
929 searchPaths.push_back("/usr/lib/llvm-20/lib/clang");
930 searchPaths.push_back("/usr/lib/clang");
931 searchPaths.push_back("/usr/local/lib/clang");
932#endif
933
934 searchPaths.push_back("/usr/local/lib/clang");
935
936 for (const auto &basePath : searchPaths) {
937 if (!llvm::sys::fs::exists(basePath)) {
938 continue;
939 }
940
941
942 std::error_code ec;
943 std::string bestVersion;
944 int bestMajor = 0;
945
946 for (llvm::sys::fs::directory_iterator dir(basePath, ec), dirEnd; !ec && dir != dirEnd; dir.increment(ec)) {
947 std::string name = llvm::sys::path::filename(dir->path()).str();
948
949 int major = 0;
950 if (sscanf(name.c_str(), "%d", &major) == 1 && major > bestMajor) {
951 bestMajor = major;
952 bestVersion = dir->path();
953 }
954 }
955
956 if (!bestVersion.empty()) {
957 resourceDir = bestVersion;
958 if (!QuietMode) {
959 llvm::errs() << "Found clang resource directory at runtime: " << resourceDir << "\n";
960 }
961 break;
962 }
963 }
964
965 if (resourceDir.empty() && !QuietMode) {
966 llvm::errs() << "Warning: Could not find clang resource directory\n";
967 }
968 }
969
970 if (!resourceDir.empty()) {
971 prependArgs.push_back(std::string("-resource-dir=") + resourceDir);
972 }
973
974
975
976#ifdef __APPLE__
977#ifdef __arm64__
978 prependArgs.push_back("-target");
979 prependArgs.push_back("arm64-apple-darwin");
980
981#else
982 prependArgs.push_back("-target");
983 prependArgs.push_back("x86_64-apple-darwin");
984
985#endif
986#elif defined(__linux__)
987#ifdef __aarch64__
988 prependArgs.push_back("-target");
989 prependArgs.push_back("aarch64-linux-gnu");
990
991#else
992 prependArgs.push_back("-target");
993 prependArgs.push_back("x86_64-linux-gnu");
994
995#endif
996#endif
997
998
999
1000
1001 std::string selectedSDK;
1002#ifdef __APPLE__
1003 {
1004 const char *sdkPaths[] = {
1005
1006 "/Applications/Xcode.app/Contents/Developer/Platforms/"
1007 "MacOSX.platform/Developer/SDKs/MacOSX.sdk",
1008
1009 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
1010 };
1011
1012 for (const char *sdk : sdkPaths) {
1013 if (llvm::sys::fs::exists(sdk)) {
1014 selectedSDK = sdk;
1015 break;
1016 }
1017 }
1018
1019 if (!selectedSDK.empty()) {
1020 prependArgs.push_back("-isysroot");
1021 prependArgs.push_back(selectedSDK);
1022
1023 } else {
1024 llvm::errs() << "Warning: No macOS SDK found, system headers may not be available\n";
1025 }
1026 }
1027#endif
1028
1029
1030
1031
1032
1033
1034 std::vector<std::string> appendArgs;
1035 if (!resourceDir.empty()) {
1036 std::string builtinInclude = resourceDir + "/include";
1037 if (llvm::sys::fs::exists(builtinInclude)) {
1038 appendArgs.push_back("-isystem");
1039 appendArgs.push_back(builtinInclude);
1040
1041 }
1042 }
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062 std::string inputRootStr = inputRoot.string();
1063 auto consolidatedAdjuster = [prependArgs, appendArgs, inputRootStr](
const tooling::CommandLineArguments &
args,
1064 StringRef) {
1065
1066 auto isProjectPath = [&inputRootStr](const std::string &path) -> bool {
1067 if (inputRootStr.empty())
1068 return false;
1069
1070 std::error_code ec;
1071 auto normalizedPath = fs::canonical(path, ec);
1072 if (ec)
1073 return false;
1074 auto normalizedRoot = fs::canonical(inputRootStr, ec);
1075 if (ec)
1076 return false;
1077
1078 std::string pathStr = normalizedPath.string();
1079 std::string rootStr = normalizedRoot.string();
1080 if (pathStr.find(rootStr) != 0)
1081 return false;
1082
1083
1084 if (pathStr.find("/.deps-cache/") != std::string::npos)
1085 return false;
1086 return true;
1087 };
1088 tooling::CommandLineArguments result;
1089
1091 return result;
1092 }
1093
1094
1095 result.push_back(
args[0]);
1096
1097
1098 for (const auto &arg : prependArgs) {
1099 result.push_back(arg);
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 std::vector<std::string> collectedIsystemPaths;
1117 bool foundSeparator = false;
1118 size_t separatorIndex = 0;
1119 for (
size_t i = 1; i <
args.size(); ++i) {
1120 const std::string &arg =
args[i];
1121
1122
1123 if (arg == "--") {
1124 foundSeparator = true;
1125 separatorIndex = i;
1126 break;
1127 }
1128
1129
1130 if (arg.find("-fsanitize") != std::string::npos)
1131 continue;
1132 if (arg.find("-fno-sanitize") != std::string::npos)
1133 continue;
1134
1135 if (arg == "-g" || arg == "-g2" || arg == "-g3")
1136 continue;
1137 if (arg == "-fno-eliminate-unused-debug-types")
1138 continue;
1139 if (arg == "-fno-inline")
1140 continue;
1141
1142 if (arg == "-resource-dir") {
1143 ++i;
1144 continue;
1145 }
1146 if (arg.find("-resource-dir=") == 0)
1147 continue;
1148
1149 if (arg == "-isysroot") {
1150 ++i;
1151 continue;
1152 }
1153 if (arg.find("-isysroot=") == 0 || (arg.find("-isysroot") == 0 && arg.length() > 9))
1154 continue;
1155
1156
1157
1158 if (arg ==
"-isystem" && i + 1 <
args.size()) {
1159 collectedIsystemPaths.push_back(
args[++i]);
1160 continue;
1161 }
1162 if (arg.find("-isystem") == 0 && arg.length() > 8) {
1163 collectedIsystemPaths.push_back(arg.substr(8));
1164 continue;
1165 }
1166
1167
1168
1169
1170
1171
1172
1173 if (arg ==
"-I" && i + 1 <
args.size()) {
1174
1175 const std::string &includePath =
args[++i];
1176 if (isProjectPath(includePath) && includePath.find("/include") == std::string::npos) {
1177
1178 result.push_back("-iquote");
1179 result.push_back(includePath);
1180 } else if (!isProjectPath(includePath)) {
1181
1182 collectedIsystemPaths.push_back(includePath);
1183 } else {
1184
1185 result.push_back("-I");
1186 result.push_back(includePath);
1187 }
1188 continue;
1189 }
1190 if (arg.find("-I") == 0 && arg.length() > 2) {
1191
1192 std::string includePath = arg.substr(2);
1193 if (isProjectPath(includePath) && includePath.find("/include") == std::string::npos) {
1194
1195 result.push_back("-iquote");
1196 result.push_back(includePath);
1197 } else if (!isProjectPath(includePath)) {
1198
1199 collectedIsystemPaths.push_back(includePath);
1200 } else {
1201
1202 result.push_back("-I");
1203 result.push_back(includePath);
1204 }
1205 continue;
1206 }
1207
1208 result.push_back(arg);
1209 }
1210
1211
1212
1213
1214 for (const auto &arg : appendArgs) {
1215 result.push_back(arg);
1216 }
1217
1218 for (const auto &path : collectedIsystemPaths) {
1219 result.push_back("-isystem");
1220 result.push_back(path);
1221 }
1222
1223
1224 result.push_back("-DASCIICHAT_DEFER_TOOL_PARSING");
1225 if (foundSeparator) {
1226 result.push_back("--");
1227
1228 for (
size_t i = separatorIndex + 1; i <
args.size(); ++i) {
1229 result.push_back(
args[i]);
1230 }
1231 }
1232
1233 return result;
1234 };
1235 tool.appendArgumentsAdjuster(consolidatedAdjuster);
1236
1237 DeferActionFactory actionFactory(outputDir, inputRoot);
1238 const int executionResult = tool.run(&actionFactory);
1239 if (executionResult != 0) {
1240 llvm::errs() << "Defer transformation failed with code " << executionResult << "\n";
1241 }
1242 return executionResult;
1243}