Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
1. Write at the front
LLVM download and LLVM compilation were described in my previous blog post. Clang and clangTooling were also compiled for LLVM project.
LLVM for iOS (part 2) — Custom Clang plugin (Part 1)
IOS Low-level exploration LLVM(I) — the first experience of LLVM
This blog will teach you how to code and customize a Clang plug-in. The final function is to report an error for incorrect use of attribute modifiers and suggest the correct word. The final effect is as follows.
2. Prepare
2.1 Creating a Plug-in
Create JPPlugins in/LLVM /tools/clang/tools.
2.2 modify CMakeLists. TXT
Add add_clang_subdirectory(JPPlugins) to cmakelists. TXT in/LLVM /tools/clang/tools
- in
JPPlugins
Create a new directory namedJPPlugins.cpp
The documents andCMakeLists.txt
The document, inCMakeLists.txt
Write the following code in
add_llvm_library( JPPlugins MODULE BUILDTREE_ONLY
JPPlugins.cpp
)
Copy the code
2.3 Compiling Plug-ins
- And then use it again
cmake
The command is regeneratedxcode
Project, or atllvm_build
Use in directorycmake -G Xcode .. /llvm
Command. - Finally, you can
LLVM
theXcode
You can see that in the projectLoadable modules
We have our own under the directoryPlugin
Catalog, I cheerfully open the project a look…
What? It failed! There was nothing! Is this some kind of joke? CPP file with the same name as JPPlugins. I changed the CPP name and removed the “S” from the plugin name, and it worked! It’s got me scratching my head. It’s weird!
- My stubborn temper, I do not believe in this evil, I changed back to the original name,
JPPlugins.cpp
Files andJPPlugins
The plugin still has the same name. I compiled it again and it failed again. - I again to
JP.cpp
File and plug-in namesJPPlugins
This time it’s different. It’s a success! It failed. - I tried a third time,
JPPlugin.cpp
File and plug-in namesJPPlugin
This time I wills
It’s gone. That’s how weird it is. It worked.
I do not go to any conclusion, anyway, the same name can be compiled successfully, specific is not caused by the SUFFIX S, I do not know, anyway, my two success is related to this, more time old iron can go to verify, here will not bother to verify.
So we can now go to CPP to write code for the Clang plug-in.
3. Write plug-in code
No more nonsense to write, directly on the code, the steps are not a list out, are written in the code.
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
// Declare a namespace
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
// Plug-in namespace
namespace JPPlugin {
// Step 3: callback after scan
// Create a custom callback class from MatchCallback
class JPMatchCallback : public MatchFinder::MatchCallback {
private:
// CI pass path: the CreateASTConsumer method parameter in the JPASTAction class -> JPASTConsumer's constructor -> JPMatchCallback's private property, obtained from the JPASTConsumer constructor via the constructor
CompilerInstance &CI;
// Check if it is your own file
bool isUserSourceCode(const string fileName) {
// The file name is not empty
if (fileName.empty()) return false;
// Code that is not in Xcode is assumed to belong to the user
if (0 == fileName.find("/Applications/Xcode.app/")) return false;
return true;
}
// Check whether copy should be used
bool isShouldUseCopy(const string typeStr) {
// Check whether the type is NSString/NSArray/NSDictionary
if (typeStr.find("NSString") != string::npos ||
typeStr.find("NSArray") != string::npos ||
typeStr.find("NSDictionary") != string::npos) {
return true;
}
return false;
}
public:
// constructor
JPMatchCallback(CompilerInstance &CI):CI(CI) {}
// Override the run method
void run(const MatchFinder::MatchResult &Result) {
// Get the node object from Result, based on the node ID ("objcPropertyDecl") (this id needs to be the same as the ID of bind in the JPASTConsumer constructor)
const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
// Get the file name (including the path)
string fileName = CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str(a);// If the node has a value && is a user file
if (propertyDecl && isUserSourceCode(fileName)) {
// Get the node type and convert it to a string
string typeStr = propertyDecl->getType().getAsString(a);// Node description
ObjCPropertyAttribute::Kind attrKind = propertyDecl->getPropertyAttributes(a);// Copy should have been used, but it was not
if (isShouldUseCopy(typeStr) && ! (attrKind & ObjCPropertyAttribute::kind_copy)) {// Get the diagnostic engine from CI
DiagnosticsEngine &diag = CI.getDiagnostics(a);/ / the Report Report
Error: getCustomDiagID (level, hint) */
diag.Report(propertyDecl->getLocation(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0 - Copy is recommended for this property!!"))<< typeStr; }}}};// Step 2: The scan configuration is complete
// 3, custom JPASTConsumer, derived from the abstract class ASTConsumer, used to listen for information on AST nodes -- filters
class JPASTConsumer : public ASTConsumer {
private:
// AST node finder (filter)
MatchFinder matcher;
// Callback object
JPMatchCallback callback;
public:
// The MatchFinder object is created in the constructor
JPASTConsumer(CompilerInstance &CI):callback(CI) { // Construct passes CI to callback
// Add a MatchFinder with each objcPropertyDecl node bound to an objcPropertyDecl identifier (to match the objcPropertyDecl node)
// Callback is a callback that overwrites the run method in CJLMatchCallback.
matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &callback);
}
Override the two methods HandleTopLevelDecl and HandleTranslationUnit
// Call back once after parsing a top-level declaration (top-level nodes, i.e. global variables, attributes, functions, etc.)
bool HandleTopLevelDecl(DeclGroupRef D) {
// cout<<" parsing..." <
return true;
}
// Call back when the entire file has been parsed
void HandleTranslationUnit(ASTContext &Ctx) {
// cout<<" File parsed!!" <
// Give matcher the context (AST syntax tree) of the parsed file
matcher.matchAST(Ctx); }};//2, inherit PluginASTAction to implement our custom JPASTAction, i.e. custom AST syntax tree behavior
class JPASTAction : public PluginASTAction {
public:
// Override the ParseArgs and CreateASTConsumer methods
/* Parse the given plug-in command line argument - param CI compiler instance for reporting diagnostics. - return True if the parsing succeeds. Otherwise, the plug-in is destroyed and no action is taken. This plug-in is responsible for reporting errors using the Diagnostic object CompilerInstance. * /
bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string> &arg) {
return true;
}
// Returns a custom JPASTConsumer object, a subclass of the abstract class ASTConsumer
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
/** Pass CI CI for: - Determine if the file is the user's - throw a warning */
return unique_ptr<JPASTConsumer>(new JPASTConsumer(CI)); }}; }// Step 1: Register the plug-in and customize the JPASTAction class
// 1
static FrontendPluginRegistry::Add<JPPlugin::JPASTAction> X("JPPlugin"."this is JPPlugin");
Copy the code
3.1 Terminal Test Plug-in
Create a new project and write the following code in viewController.m
@interface ViewController(a)
@property (nonatomic , strong) NSString *name;
@property (nonatomic , strong) NSArray *array;
@end
Copy the code
- The test command is as follows
“Self-compiled clang file path -isysroot simulator file path -xclang-load-Xclang plug-in path (.dylib) -xclang-add-plugin -xclang plug-in name -c source file path”
- self-compiled
clang
The file path is:llvm-project/llvm_build/Debug/bin/clang
-
The emulator file path is: / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator. The SDK, Use your computer to judge.
-
Plugin name: this is the name of the plugin you created, in this case JPPlugin
-
Source file path: the path to the file that you need the plugin to recognize
-
For example, 👇
/Users/RENO/Desktop/TEST/JPDemo/llvm-project/llvm_build/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -Xclang -load -Xclang /Users/RENO/Desktop/TEST/JPDemo/llvm-project/llvm_build/Debug/lib/JPPlugin.dylib -Xclang -add-plugin -Xclang JPPlugin -c /Users/RENO/Desktop/TEST/JPDemo/PluginTestDemo/PluginTestDemo/ViewController.m
- The test results are as follows
3.2 Xcode integration plug-in
- Load the plug-in
Open your test project and add the following to Build Settings -> Other C Flags in the code project:
- xclang-load-xclang plug-in path (.dylib) - xclang-add-plugin - The name of the Xclang plug-in
- Set the coder
Since the Clang plug-in needs to be loaded with the corresponding version, if the version is inconsistent, a compilation error will occur, as shown in the following figure:
- in
Build Settings
Add two user-defined Settings in the column
CC and CXX, respectively
CC
The corresponding one is compiled by itselfclang
Absolute path ofCXX
The corresponding one is compiled by itselfclang++
Absolute path of
- Set up the
CC
和CXX
- The next in
Build Settings
Search in columnindex
That will beEnable Index-Wihle-Building Functionality
willDefault
Instead ofNO
, myXcode
Toxic, search, I was hard to find, directly down, good search, finally found.
3.3 Compiling test Plug-ins
- The last
command + B
Compile the
As you can see from the results, clang’s plug-in works perfectly.
4. To summarize
- The process is tortuous, the result is beautiful! 😁
- my
Xcode
Version isVersion 12.5
MacOS 11.4 Big Sur
- The above content is for reference only, everyone’s computer environment is not the same, there may be differences.
- Be careful not to write wrong paths during configuration.
- This is just to record my own exploration process, the most important is some ideas and methods, stepped on the pit, I hope to help you avoid lightning!
5. Write in the back
Pay attention to me, more content continues to output!
Stay tuned!
- CSDN
- The Denver nuggets
- Jane’s book
🌹 if you like, give it a thumbs up 👍🌹
🌹 feel harvest, can come to a wave of collection + attention, so as not to find you next time I 😁🌹
🌹 welcome everyone to leave a message exchange, criticism and correction, forwarding please indicate the source, thank you for your support! 🌹