File indexing completed on 2024-04-28 04:38:28
0001 /* 0002 SPDX-FileCopyrightText: 2014 Sergey Kalinichev <kalinichev.so.0@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "includepathsconverter.h" 0008 0009 #include <KSharedConfig> 0010 0011 #include <QCommandLineParser> 0012 #include <QCoreApplication> 0013 #include <QDirIterator> 0014 #include <QFileInfo> 0015 #include <QTextStream> 0016 0017 #include <algorithm> 0018 0019 #include "settingsmanager.h" 0020 0021 using namespace KDevelop; 0022 0023 namespace { 0024 KSharedConfigPtr openConfigFile(const QString& configFile) 0025 { 0026 return KSharedConfig::openConfig(configFile, KConfig::SimpleConfig); 0027 } 0028 0029 QString findconfigFile(const QString& projectDir) 0030 { 0031 QDirIterator dirIterator(projectDir + QLatin1String("/.kdev4")); 0032 while (dirIterator.hasNext()) { 0033 dirIterator.next(); 0034 if (dirIterator.fileName().endsWith(QLatin1String(".kdev4"))) { 0035 return dirIterator.fileInfo().canonicalFilePath(); 0036 } 0037 } 0038 return {}; 0039 } 0040 0041 QString findProject(const QString& subdirectory) 0042 { 0043 QDir project(subdirectory); 0044 do { 0045 if (project.exists(QStringLiteral(".kdev4"))) { 0046 return project.path(); 0047 } 0048 } while(project.cdUp()); 0049 0050 return {}; 0051 } 0052 } 0053 0054 IncludePathsConverter::IncludePathsConverter() 0055 { 0056 } 0057 0058 bool IncludePathsConverter::addIncludePaths(const QStringList& includeDirectories, const QString& projectConfigFile, const QString& subdirectory) 0059 { 0060 auto configFile = openConfigFile(projectConfigFile); 0061 if (!configFile) { 0062 return false; 0063 } 0064 0065 auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); 0066 QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; 0067 0068 ConfigEntry config; 0069 for (auto& entry: configEntries) { 0070 if (path == entry.path) { 0071 config = entry; 0072 config.includes += includeDirectories; 0073 config.includes.removeDuplicates(); 0074 entry = config; 0075 break; 0076 } 0077 } 0078 0079 if (config.path.isEmpty()) { 0080 config.path = path; 0081 config.includes = includeDirectories; 0082 configEntries.append(config); 0083 } 0084 0085 SettingsManager::globalInstance()->writePaths(configFile.data(), configEntries); 0086 0087 return true; 0088 } 0089 0090 bool IncludePathsConverter::removeIncludePaths(const QStringList& includeDirectories, const QString& projectConfigFile, const QString& subdirectory) 0091 { 0092 auto configFile = openConfigFile(projectConfigFile); 0093 if (!configFile) { 0094 return false; 0095 } 0096 0097 auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); 0098 QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; 0099 0100 for (auto& entry: configEntries) { 0101 if (path == entry.path) { 0102 for(const auto& include: includeDirectories) { 0103 entry.includes.removeAll(include); 0104 } 0105 SettingsManager::globalInstance()->writePaths(configFile.data(), configEntries); 0106 return true; 0107 } 0108 } 0109 0110 return true; 0111 } 0112 0113 QStringList IncludePathsConverter::readIncludePaths(const QString& projectConfigFile, const QString& subdirectory) const 0114 { 0115 auto configFile = openConfigFile(projectConfigFile); 0116 if (!configFile) { 0117 return {}; 0118 } 0119 0120 QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; 0121 const auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); 0122 for (const auto& entry: configEntries) { 0123 if (path == entry.path) { 0124 return entry.includes; 0125 } 0126 } 0127 0128 return {}; 0129 } 0130 0131 int main(int argc, char** argv) 0132 { 0133 QCoreApplication app(argc, argv); 0134 QCoreApplication::setApplicationName(QStringLiteral("kdev_includepathsconverter")); 0135 0136 QCommandLineParser parser; 0137 parser.setApplicationDescription(QStringLiteral("\nAdds, removes or shows include directories of a project. Also it can be used as a tool to convert include directories from .kdev_include_paths file to the new format.\n\n" 0138 "Examples:\ncat /project/path/.kdev_include_paths | xargs -d '\\n' kdev_includepathsconverter -a /project/path/\n\n" 0139 "kdev_includepathsconverter -r /project/path/subdirectory/ \"/some/include/dir\" \"/another/include/dir\" \n\n" 0140 "kdev_includepathsconverter -l /project/path/another/subdirectory/")); 0141 parser.addHelpOption(); 0142 0143 QCommandLineOption listOption(QStringLiteral("l"), QCoreApplication::translate("main", "Shows include directories used by the project"), 0144 QCoreApplication::translate("main", "project")); 0145 parser.addOption(listOption); 0146 QCommandLineOption addOption(QStringLiteral("a"), QCoreApplication::translate("main", "Adds include directories to the project"), 0147 QCoreApplication::translate("main", "project")); 0148 parser.addOption(addOption); 0149 QCommandLineOption removeOption(QStringLiteral("r"), QCoreApplication::translate("main", "Removes include directories from the project"), 0150 QCoreApplication::translate("main", "project")); 0151 parser.addOption(removeOption); 0152 0153 parser.process(app); 0154 0155 QString projectDir; 0156 QStringList includeDirectories(parser.positionalArguments()); 0157 0158 std::transform(includeDirectories.begin(), includeDirectories.end(), includeDirectories.begin(), [](const QString& path) { 0159 return path.trimmed(); 0160 } ); 0161 includeDirectories.erase(std::remove_if(includeDirectories.begin(), includeDirectories.end(), [](const QString& path) { 0162 return path.isEmpty(); 0163 } ), includeDirectories.end()); 0164 0165 bool show = parser.isSet(listOption); 0166 bool add = parser.isSet(addOption); 0167 bool remove = parser.isSet(removeOption); 0168 if (show) { 0169 projectDir = parser.value(listOption); 0170 } else if(add) { 0171 projectDir = parser.value(addOption); 0172 } else if(remove) { 0173 projectDir = parser.value(removeOption); 0174 } 0175 0176 if (projectDir.isEmpty()) { 0177 parser.showHelp(-1); 0178 } 0179 0180 QString subdirectory = projectDir; 0181 projectDir = findProject(projectDir); 0182 0183 QString configFile = findconfigFile(projectDir); 0184 0185 QTextStream out(stdout); 0186 0187 if (configFile.isEmpty()) { 0188 out << QCoreApplication::translate("main", "No project found for: ") << subdirectory; 0189 return -1; 0190 } 0191 0192 if (add || remove) { 0193 if (includeDirectories.isEmpty()) { 0194 parser.showHelp(-1); 0195 } 0196 } 0197 0198 { 0199 auto subdirCanonical = QFileInfo(subdirectory).canonicalFilePath(); 0200 auto projectCanonical = QFileInfo(projectDir).canonicalFilePath(); 0201 if (subdirCanonical != projectCanonical) { 0202 subdirectory = subdirCanonical.mid(projectCanonical.size()); 0203 if (subdirectory.startsWith(QLatin1Char('/'))) { 0204 subdirectory.remove(0,1); 0205 } 0206 } else { 0207 subdirectory.clear(); 0208 } 0209 } 0210 0211 IncludePathsConverter converter; 0212 0213 if (remove) { 0214 if (!converter.removeIncludePaths(includeDirectories, configFile, subdirectory)) { 0215 out << QCoreApplication::translate("main", "Can't remove include paths"); 0216 } 0217 } 0218 0219 if (add) { 0220 if (!converter.addIncludePaths(includeDirectories, configFile, subdirectory)) { 0221 out << QCoreApplication::translate("main", "Can't add include paths"); 0222 } 0223 } 0224 0225 if (show) { 0226 const auto& includes = converter.readIncludePaths(configFile, subdirectory); 0227 for (const auto& include : includes) { 0228 out << include << "\n"; 0229 } 0230 } 0231 }