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