编程知识 cdmana.com

Intégration du processus de compilation llvm et du développement du plug - in clang pour iOS

LLVM Introduction

Un.、Qu'est - ce que LLVM?
  • LLVM Est un compilateur d'architecture(compiler)Système de châssis pour,Par C++ Écrit à partir de,Utilisé pour optimiser le temps de compilation des programmes écrits dans n'importe quelle langue de programme(compile-time)、Temps de liaison(link-time)、Temps de fonctionnement(runtime)Et le temps libre(idle-time),Rester ouvert aux développeurs,Et compatible avec les scripts existants.
  • LLVM La première fois, c'était Illinois Un projet de recherche de,Le principal responsable est Chris Lattner,Il travaille maintenant àApple.Apple Pour l'instant aussi. LLVM Un des principaux commanditaires du projet.
  • Comprendre LLVM Heure,On peut penser qu'il inclut un sens étroit LLVM Et un sens large LLVM.Au sens large LLVM En fait, c'est tout LLVM Architecture du compilateur,Y compris l'avant、Arrière - plan、Optimizer、Beaucoup de fonctions de bibliothèque et beaucoup de modules;Au sens étroit LLVM En fait, il s'agit de se concentrer sur les fonctions de back - end du compilateur(Génération de code、Optimisation du Code、JITAttendez.)Une série de modules et de bibliothèques.
2.、Conception traditionnelle du compilateur
  • Le compilateur traditionnel se compose de trois phases: Front End(Frontend)-> Optimizer(Optimizer)-> Arrière - plan(Backend);
    • Front EndFrontend:Responsable de l'analyse du code source,Vous pouvez vérifier les erreurs de niveau de syntaxe(Y compris l'analyse lexicale、Analyse grammaticale、Analyse sémantique), Et construire un arbre de syntaxe abstrait pour la langue (AST:Abstract Syntax Tree); L'Arbre syntaxique abstrait peut être converti en optimisation , Enfin, une nouvelle représentation , Ensuite, il est remis à l'optimiseur et à l'arrière - plan ,LLVM Un code intermédiaire est également généré à l'avant de(intermediate representation,AbréviationsIR); Le code machine exécutable est finalement généré par l'arrière - plan (Peut être compris comme LLVM C'est le compilateur + Optimizer, Reçu IR Code intermédiaire, La sortie est toujours IR, À l'arrière. , Traduction vers l'ensemble d'instructions cible par l'arrière - plan );
    • Optimizer Optimizer:L'optimiseur est responsable de diverses optimisations,Améliorer le temps d'exécution du Code,Par exemple, éliminer les calculs redondants, etc.;
    • Arrière - plan Backend(Générateur de code Code Generator):Mapper le Code vers l'ensemble d'instructions cible,Générer le Code de la machine, Et effectuer l'optimisation du Code par rapport au Code de la machine ;
  • Code source Source Code + Front End Frontend + Optimizer Optimizer + Arrière - plan Backend(Générateur de code CodeGenerator)+ Code machine Machine Code,Comme suit:

Insérer la description de l'image ici

  • LLVM L'avantage de,Le milieu indiqueIR Le Code est bien écrit , Et différentes langues frontales finissent par être converties en la même IR.
Trois、iOS Architecture du compilateur pour
  • OC、C、C++ Le compilateur frontal utilisé estClang,Swift- Oui.swift,L'arrière - plan estLLVM,Comme le montre la figure ci - dessous:

Insérer la description de l'image ici

Quatre、LLVM Conception
  • LLVM L'aspect le plus important de la conception,Utiliser une représentation de code commune(IR),Il est utilisé pour représenter le code dans le compilateur,Tous les LLVM Il est possible d'écrire le Front End indépendamment pour n'importe quel langage de programmation,Et peut écrire des backends indépendants pour n'importe quelle architecture matérielle,Comme suit:

Insérer la description de l'image ici

  • LLVMLes avantages de:
    • Le milieu indiqueIR Le Code est bien écrit , Et les différentes langues frontales sont finalement converties en un code intermédiaire unifié LLVM IR(LLVM Intermediate Representation);
    • Si vous avez besoin de support pour une nouvelle langue , Il suffit de mettre en place un nouveau front end ;
    • Si vous avez besoin de prendre en charge un nouveau matériel , Il suffit de mettre en place un nouveau Backend ;
    • La phase d'optimisation est une phase générique , Il ne vise que l'unité LLVM IR, Que ce soit pour soutenir de nouveaux langages de programmation , Toujours prendre en charge le nouveau matériel , Pas besoin de modifier la phase d'optimisation ;
    • LLVM Quelle est l'infrastructure universelle du langage de compilation statique et d'exécution maintenant implémentée par le siège (GCCFamille、Java、.NET、Python、Ruby、Scheme、Haskell、DAttendez.);
    • En comparaison,GCC L'avant et l'arrière - plan sont trop ouverts , Les extrémités avant et arrière sont couplées ,Alors...GCC Pour soutenir une nouvelle langue , Ou pour soutenir une nouvelle plateforme cible , Ça devient particulièrement difficile ;
Cinq、Clang
  • clang - Oui. LLVM Un sous - projet du projet,Il est basé sur LLVM Compilateur léger pour les diagrammes d'architecture ,Il est né pour remplacer GCC,Fournit une vitesse de compilation plus rapide, Il est responsable de C、C++、OC Compilateur de langue, Appartient à tout LLVM Compilateur frontal dans le schéma,Pour les développeurs,Recherche Clang Ça pourrait nous faire beaucoup de bien..
  • Par rapport à GCC,Clang Avec les avantages suivants:
    • Compiler le bloc de vitesse:Sur une plate - forme,Clang Compilé beaucoup plus rapidement que GCC(Debug Compiler en mode OC Rapport de vitesse GCC Allez 3 X);
    • Faible consommation de mémoire:Clang ProduitASTLa mémoire utilisée est GCC Environ un cinquième de;
    • Conception modulaire:Clang Adopter une conception modulaire basée sur la Bibliothèque,Facile à IDE Intégration et réutilisation à d'autres fins;
    • L'information diagnostique est lisible:Pendant la compilation,Clang Un grand nombre de métadonnées minces ont été créées et conservées ;(metadata), Faciliter le débogage et les messages d'erreur plus conviviaux ;
    • Design clair et simple,Facile à comprendre,Extensibilité.
Six、Clang Avec LLVM

Insérer la description de l'image ici

LLVMProcessus de compilation

Un.、 Imprimer la phase de compilation du code source par commande
  • clang -ccc-print-phases main.m
    • Fichier d'entrée:Trouver le fichier source
      ± 0: input, “main.m”, objective-c
    • Phase de prétraitement:Ce processus traite du remplacement des macros,Importation de fichiers d'en - tête
      ± 1: preprocessor, {0}, objective-c-cpp-output
    • Phase de compilation:Analyse lexicale、Analyse grammaticale、Vérifier si la syntaxe est correcte,Génération finaleIR
      ± 2: compiler, {1}, ir
    • Arrière - plan:Ici.LLVMIls passeront un par un.passPour optimiser,ChaquepassFais quelque chose.,Générer le Code d'assemblage final
      ± 3: backend, {2}, assembler
    • Assembler le Code pour générer le fichier cible
      ± 4: assembler, {3}, object
    • Liens:Lier les bibliothèques dynamiques et statiques nécessaires,Générer un exécutable
      ± 5: linker, {4}, image(Fichier miroir)
    • BIND:Par différentes architectures,Générer l'exécutable correspondant
      6: bind-arch, “x86_64”, {5}, image
  • Nouveau projet,cd À main.m Chemin,Et ensuite exécuter clang -ccc-print-phases main.m,Les résultats sont les suivants::
	yangdw@Kody LLVM %  clang -ccc-print-phases main.m      
	               +- 0: input, "main.m", objective-c
	            +- 1: preprocessor, {
    0}, objective-c-cpp-output
	         +- 2: compiler, {
    1}, ir
	      +- 3: backend, {
    2}, assembler
	   +- 4: assembler, {
    3}, object
	+- 5: linker, {
    4}, image
	6: bind-arch, "x86_64", {
    5}, image
2.、 Pré - traitement de la phase de compilation
  • In main.m Tapez le code suivant :
	int test(int a,int b){
    
	    return a + b + 3;
	}
	
	int main(int argc, const char * argv[]) {
    
	    @autoreleasepool {
    
	        int a = test(1, 2);
	        printf("%d",a);
	    }
	    return 0;
	}
  • Et ensuite exécuter clang -E main.m,Je vois.,Les résultats sont les suivants::
	# 9 "main.m" 2
	
	int test(int a,int b){
    
	    return a + b + 3;
	}
	
	int main(int argc, const char * argv[]) {
    
	    @autoreleasepool {
    
	        int a = test(1, 2);
	        printf("%d",a);
	    }
	    return 0;
	}
  • C'est facile à voir., Cette phase traite principalement du remplacement des macros et de l'importation des fichiers d'en - tête ;
Trois、Phase de compilation
① Analyse lexicale
  • Une Analyse lexicale est effectuée après le prétraitement ,Le Code sera découpé ici.Token,Comme les parenthèses、Signe égal, chaîne, etc..
  • Voir les résultats de l'analyse lexicale en utilisant la commande suivante :
	clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
  • Les résultats sont les suivants:
	yangdw@Kody LLVM % clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
	annot_module_include '#import <Foundation/Foundation.h>
	
	int test(int a,int b){
    
	    return a + b + 3;
	}
	
	int main(int argc, con'		Loc=<main.m:8:1>
	int 'int'	 [StartOfLine]	Loc=<main.m:10:1>
	identifier 'test'	 [LeadingSpace]	Loc=<main.m:10:5>
	l_paren '('		Loc=<main.m:10:9>
	int 'int'		Loc=<main.m:10:10>
	identifier 'a'	 [LeadingSpace]	Loc=<main.m:10:14>
	comma ','		Loc=<main.m:10:15>
	int 'int'		Loc=<main.m:10:16>
	identifier 'b'	 [LeadingSpace]	Loc=<main.m:10:20>
	r_paren ')'		Loc=<main.m:10:21>
	l_brace '{'		Loc=<main.m:10:22>
	return 'return'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:11:5>
	identifier 'a'	 [LeadingSpace]	Loc=<main.m:11:12>
	plus '+'	 [LeadingSpace]	Loc=<main.m:11:14>
	identifier 'b'	 [LeadingSpace]	Loc=<main.m:11:16>
	plus '+'	 [LeadingSpace]	Loc=<main.m:11:18>
	numeric_constant '3'	 [LeadingSpace]	Loc=<main.m:11:20>
  • Si le fichier d'en - tête n'est pas trouvé ,Désignationsdk:
	clang -isysroot (Moi - même.SDKChemin) -fmodules -fsyntax-only -Xclang -dump-tokens main.m
	clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -fmodules -fsyntax-only -Xclang -dump-tokens main.m
② Analyse grammaticale
  • Une fois l'analyse lexicale terminée, l'analyse grammaticale ,Sa tâche est de vérifier que la syntaxe est correcte, Sur la base de l'analyse lexicale, les séquences de mots sont combinées en différentes phrases ,Comme la procédure、Déclarations、Expressions, etc.,Ensuite, tous les noeuds forment un arbre de syntaxe abstrait(Abstract Syntax Tree􏰊AST), Le Programme d'analyse grammaticale détermine si le programme est structuré correctement .
  • Les résultats de l'analyse grammaticale peuvent être visualisés par la commande suivante :
	clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
  • Si le fichier d'en - tête d'importation n'est pas trouvé,Peut spécifierSDK:
	 clang -isysroot (Moi - même.SDKChemin) -fmodules -fsyntax-only -Xclang -ast-dump main.m
	
	 clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -fmodules -fsyntax-only -Xclang -ast-dump main.m
  • Les résultats de l'analyse grammaticale sont présentés ci - dessous :

Insérer la description de l'image ici

③ Générer un code intermédiaireIR
  • Générer un code intermédiaireIR,Générateur de code(Code Generation)Traduit l'Arbre syntaxique de haut en basLLVM IR;
  • Peut être généré par la commande suivante .llFichier texte pour,VoirIRCode.
	clang -S -fobjc-arc -emit-llvm main.m
  • OC Le Code sera exécuté à cette étape runtime Pont:propertySynthèse、ARCTraitement, etc.;
  • IR Syntaxe de base pour:
    • @ Identification globale
    • % Identification locale
    • alloca Ouvrir l'espace
    • align Alignement de la mémoire
    • i32 32bit 4Octets
    • store Écrire en mémoire
    • load Lire les données
    • call Appelez la fonction
    • ret Retour
  • Code intermédiaire généré .llLes documents sont les suivants:

Insérer la description de l'image ici

  • Parmi eux,test Le paramètre de la fonction est :

Insérer la description de l'image ici

  • IR DocumentsOC Peut être optimisé , Les paramètres généraux sont target - Build Setting - Optimization Level( Niveau d'optimisation )Paramètres intermédiaires.LLVMLes niveaux d'optimisation pour-O0 -O1 -O2 -O3 -Os(Le premier est en majusculesO), Voici le Code intermédiaire de génération avec optimisation IROrdre de:
	clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
  • Après optimisation, voici :

Insérer la description de l'image ici

  • Xcode7 Ouvert plus tard bitcode, Apple fera d'autres optimisations ,Générer.bcCode intermédiaire pour, Après optimisation IRGénération de code .bc Code:
	clang -emit-llvm -c main.ll -o main.bc
Quatre、Générer le Code d'assemblage(Arrière - plan)
  • Par le Final.bcOu.llCode d'assemblage de génération de code:
	 clang -S -fobjc-arc main.bc -o main.s 
	 clang -S -fobjc-arc main.ll -o main.s
  • La génération de code d'assemblage peut également être optimisée:
	clang -Os -S -fobjc-arc main.m -o main.s
  • Généré à ce moment main.s Le format du fichier est le Code d'assemblage :
	yangdw@Kody LLVM % clang -emit-llvm -c main.ll -o main.bc
	yangdw@Kody LLVM % clang -S -fobjc-arc main.bc -o main.s 
	yangdw@Kody LLVM % clang -Os -S -fobjc-arc main.m -o main.s
	yangdw@Kody LLVM % file main.s
	main.s: assembler source text, ASCII text
Cinq、Générer un fichier cible(Compilateur)
  • Génération de fichiers cibles, Est l'assembleur inséré avec le Code d'assemblage ,Convertir le Code d'assemblage en code machine,Dernier fichier cible de sortie(object file)
	clang -fmodules -c main.s -o main.o
  • Peut passer nm Les ordres,Voir ci - dessous main.o Symbole dans:
	$xcrun nm -nm main.o
  • Voici main.o Symbole dans, Son format de fichier est le fichier cible :
	yangdw@Kody LLVM % clang -fmodules -c main.s -o main.o
	yangdw@Kody LLVM % $xcrun nm -nm main.o
	                 (undefined) external _objc_autoreleasePoolPop
	                 (undefined) external _objc_autoreleasePoolPush
	                 (undefined) external _printf
	0000000000000000 (__TEXT,__text) external _test
	000000000000000a (__TEXT,__text) external _main
	yangdw@Kody LLVM % file main.o                        
	main.o: Mach-O 64-bit object x86_64
  • Description de l'analyse :
    • _printf La fonction est undefined 、external De
    • undefined Indique que le symbole n'a pas été trouvé temporairement dans le fichier courant_printf
    • external Indique que ce symbole est accessible à l'extérieur
Six、Liens
  • Les liens sont principalement les bibliothèques dynamiques et statiques nécessaires pour les liens ,Générer un exécutable,Parmi eux
    • La bibliothèque statique est fusionnée avec l'exécutable ;
    • Les bibliothèques dynamiques sont indépendantes ;
  • Le connecteur construit .o Documents et .dyld .a Liens vers les fichiers,Générer unmach-oDocumentation:
	clang main.o -o main
  • Afficher les symboles après le lien:
	$xcrun nm -nm main
  • Dont:undefined Indique qu'une liaison dynamique sera effectuée au moment de l'exécution :
	clang main.o -o main
	$xcrun	nm	-nm	main
	(undefined)	external	_printf(from	libSystem)															
	(undefined)	external	dyld_stub_binder(from libSystem)
	0000000100000000 (__TEXT,__text)[referenced	dynamically]	external	__execute_header
	0000000100003f20 (__TEXT,__text) external _test
	0000000100003f40 (__TEXT,__text) external _main
	0000000100008008 (__DATA,__data) non_external _dyld_private
  • Voir main Quel format ,En ce moment mach-oFichier exécutable:
	yangdw@Kody LLVM % file main
	main:Mach-O 64-bit executable x86_64  
Sept、BIND

La liaison passe principalement par différentes architectures ,Générer la correspondancemach-o Format exécutable

Huit、LLVM Le processus de compilation pour

Insérer la description de l'image ici

ClangDéveloppement de plug - ins

Un.、LLVM Télécharger
  • En raison des restrictions du réseau domestique , Besoin d'un téléchargement miroir LLVM Source de:LLVM Lien miroir pour
  • Télécharger LLVM Projets:
	git clone https://github.com/llvm/llvm-project.git
	Ou
	git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git
  • In LLVM De tools Téléchargement dans le répertoire Clang:
	cd	llvm/tools
	git	clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git
  • In LLVM De projects Téléchargement dans le répertoire compiler-rt、libcxx、libcxxabi:
	cd	../projects
	git	clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.g
	it
	git	clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git
	git	clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git
  • In Clang De tools Installation inférieure extra Outils:
	cd ../tools/clang/tools
	git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-extra.git
2.、LLVM Compiler

Grâce aux dernières LLVM Soutien uniquement cmake Pour compiler,Donc vous devez installer cmake.

① Installation cmake
  • VoirbrewInstallé ou noncmake,Si installé, Sauter les étapes suivantes :
	brew list
  • Adoption brew Installation cmake:
	brew install cmake
② Adoption Xcode Compiler LLVM
  • cmake Compilé en Xcode Projets:
	// Inllvm Créer un nouveau build_xcodeDocumentation
	mkdir	build_xcode
	cd	build_xcode
	// Compilerllvm
	cmake	-G	Xcode	../llvm
  • Si vous rencontrez l'erreur suivante lors de la compilation : Supprimer sous le répertoire Build CMakeCache.txt C'est tout..
	CMake Error: Error: generator : Xcode
	Does not match the generator used previously: Ninja
	Either remove the CMakeCache.txt file and CMakeFiles directory or choose a different binary directory.
  • Utiliser Xcode Compiler Clang, Sélectionnez créer automatiquement Schemes:

Insérer la description de l'image ici

  • Compiler(CMD + B),Sélectionner ALL_BUILD Secheme Pour compiler(Longue durée, Estimé à plus d'une heure )

Insérer la description de l'image ici

  • Si une erreur est rencontrée lors de la compilation :error: The i386 architecture is deprecated. You should update your ARCHS build setting to remove the i386 architecture.
    Il suffit de mettre Build Settings Options Architectures Basculer la valeur en Standard Architectures(64-bit Intel) C'est tout..
    Ou:Sélectionner la création manuelleSchemes, Puis compilez la compilation Clang + ClangTooling C'est tout..
③ Adoption ninja Compiler LLVM
  • Utiliser ninja Pour compiler, vous devez installer ninja,Utilisez la commande suivante pour installerninja:
	brew install ninja
  • In LLVM Nouveau dans le Répertoire racine du code source build_ninja Table des matières,Il finira par build_ninja Générer sous le Répertoirebuild.ninja;
  • In LLVM Nouveau dans le Répertoire racine du code source llvm_release Table des matières,Le fichier de compilation final sera llvm_release Sous le chemin du dossier:
	cd llvm_build
	// -DCMAKE_INSTALL_PREFIX Désignation LLVM Chemin d'installation pour,Attention!:DCMAKE_INSTALL_PREFIXIl ne doit pas y avoir d'espace derrière
	cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX= Chemin d'installation(Cette machine est/ Users/xxx/xxx/LLVM/llvm_release)
  • Exécuter la compilation une fois ,Instructions d'installation:
	ninja
	ninja install
Trois、Créer un plug - in
  • In /llvm/tools/clang/tools Nouveau plug - in dans le répertoire YDWPlugin:

Insérer la description de l'image ici

  • In /llvm/tools/clang/tools Sous la table des matières CMakeLists.txt Documentation,Nouveauadd_clang_subdirectory(YDWPlugin),Ici YDWPlugin Est le nom du plug - in créé pour l'étape précédente ;

Insérer la description de l'image ici

  • In YDWPlugin Créer deux nouveaux fichiers dans le Répertoire ,Respectivement. YDWPlugi.cpp Et CMakeLists.txt,Et dansCMakeLists.txt Ajouter le code suivant:
	// Par terminal àYDWPluginCréer sous le Répertoire
	touch YDWPlugin.cpp
	touch CMakeLists.txt
	// CMakeLists.txtAjouter le code suivant
	add_llvm_library(YDWPlugin MODULE BUILDTREE_ONLY 
    YDWPlugin.cpp
)
  • Utilisation cmake Régénérer Xcode Projets,In build_xcode Exécutez la commande suivante dans le Répertoire:
	cmake -G Xcode ../llvm
  • Ça pourrait finir par LLVM De Xcode Comme vous pouvez le voir dans le projet Loadable modules Sous le répertoire personnalisé par YDWPlugin Table des matières, Ensuite, vous pouvez écrire le Code plug - in correspondant à l'intérieur .

Insérer la description de l'image ici

Quatre、Écrivez le Code du plug - in
  • In YDWPlugin Sous la table des matières YDWPlugin.cpp Dans le document,Ajouter le code suivant:
// create by YDW
// 2020/11/25

#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"

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
//Espace de noms, Le même nom que le plug - in 
namespace YDWPlugin {
    

//Troisième étape: Fonction de rappel scannée 
//4、Classe de rappel personnalisée,DeMatchCallback
class YDWMatchCallback: public MatchFinder::MatchCallback {
    
    
private:
    //CIChemin de transfert:YDWASTActionDans la classeCreateASTConsumerParamètres de la méthode - YDWConsumerLe constructeur de - YDWMatchCallbackPropriété privée de,Par constructeur à partir deYDWASTConsumerObtenir dans le constructeur
    CompilerInstance &CI;
    
    //Déterminer s'il s'agit d'un fichier source utilisateur
    bool isUserSourceCode(const string filename) {
    
        //Le nom du fichier n'est pas vide
        if (filename.empty()) return  false;
        //NonxcodeLes sources sont considérées comme appartenant à l'utilisateur
        if (filename.find("/Applications/Xcode.app/") == 0) return false;
        return  true;
    }

    //Pour déterminer sicopyModification
    bool isShouldUseCopy(const string typeStr) {
    
        //Déterminer si le type estNSString | NSArray | NSDictionary
        if (typeStr.find("NSString") != string::npos ||
            typeStr.find("NSArray") != string::npos ||
            typeStr.find("NSDictionary") != string::npos/*...*/)
        {
    
            return true;
        }
        
        return false;
    }
    
public:
    YDWMatchCallback(CompilerInstance &CI) :CI(CI) {
    }
    
    //RéécriturerunMéthodes
    void run(const MatchFinder::MatchResult &Result) {
    
        //Adoptionresult Obtenir le noeud associé  --  Obtenir à partir de l'étiquette du noeud ( Le marquage doit être effectué avec YDWASTConsumer La méthode de construction est cohérente )
        const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
        // Déterminer si le noeud a une valeur , Et c'est un fichier utilisateur 
        if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) {
    
            //15、Obtenir la description du noeud
            ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes();
            //Obtenir le type de noeud,Et les transformer en chaînes
            string typeStr = propertyDecl->getType().getAsString();
// cout<<"---------Je l'ai.:"<<typeStr<<"---------"<<endl;
            
            // Le jugement doit être utilisé copy,Mais non utilisécopy
            if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {
    
                //UtiliserCI Envoyer un message d'avertissement 
                //AdoptionCIObtenir le moteur de diagnostic
                DiagnosticsEngine &diag = CI.getDiagnostics();
                // Par le moteur de diagnostic  reportRapport Erreur,C'est - à - dire lancer une exception
                /* Mauvaise position:getBeginLoc  Position de départ du noeud  Erreur:getCustomDiagID(Grade,Conseils) */
                diag.Report(propertyDecl->getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0 -  Cet endroit est recommandé copy!!"))<< typeStr;
            }
        }
    }
};


//Deuxième étape:Configuration de numérisation terminée
//3、PersonnalisationYDWASTConsumer,DeASTConsumer,Pour écouterASTInformations sur le noeud -- Filtre
class YDWASTConsumer: public ASTConsumer {
    
private:
    //AST Filtre de recherche pour le noeud 
    MatchFinder matcher;
    // Définir un objet de classe de rappel 
    YDWMatchCallback callback;
    
public:
    //Créer dans la méthode de constructionmatcherFinderObjet
    YDWASTConsumer(CompilerInstance &CI) : callback(CI) {
    
        //Ajouter unMatchFinder,ChaqueobjcPropertyDeclNoeud lié à unobjcPropertyDeclIdentification(Pour correspondreobjcPropertyDeclNoeud)
        //Rappelcallback,En fait, c'estYDWMatchCallbackRéécriture à l'intérieurrunMéthodes(Le vrai rappel est le rappelrunMéthodes)
        matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &callback);
    }
    
    // Réaliser deux méthodes de rappel  HandleTopLevelDecl Et HandleTranslationUnit
    // Résolution d'une déclaration de haut niveau , Juste un rappel (Noeud de haut niveau, équivalent à une variable globale 、Déclaration de la fonction)
    bool HandleTopLevelDecl(DeclGroupRef D){
    
// cout<<"Analyse en cours..."<<endl;
        return  true;
    }
    
    // Le fichier entier résout les callbacks complétés 
    void HandleTranslationUnit(ASTContext &context) {
    
// cout<<"Analyse du fichier terminée!"<<endl;
        //Contexte après analyse du fichiercontext(C'est - à - dire:ASTArbre syntaxique) Voilà. matcher
        matcher.matchAST(context);
    }
};

//2、SuccessionPluginASTAction,Implémenter notreAction,PersonnalisationASTComportement de l'Arbre syntaxique
class YDWASTAction: public PluginASTAction {
    
    
public:
    //SurchargeParseArgs Et CreateASTConsumerMéthodes
    bool ParseArgs(const CompilerInstance &ci, const std::vector<std::string> &args) {
    
        return true;
    }
    
    //RetourASTConsumerObjet de type,Parmi euxASTConsumerC'est une classe abstraite, C'est - à - dire la classe de base 
    /* Résoudre les paramètres de ligne de commande du plug - in donné. - param CI Exemple de compilateur,Pour signaler un diagnostic. - return Si la résolution est réussie,Oui.true;Sinon,Le plugin sera détruit,Et ne rien faire.Ce plugin est responsable de l'utilisation deCompilerInstanceDeDiagnosticL'objet signale une erreur. */
    unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef iFile) {
    
        //Renvoie personnaliséYDWASTConsumer,C'est - à - dire:ASTConsumerObjet de sous - classe pour
        /* CIPour: -  Pour déterminer si le fichier utilise  - Lancer un avertissement */
        return unique_ptr<YDWASTConsumer> (new YDWASTConsumer(CI));
    }
    
};

}

// Première étape:Enregistrer le plug - in,Et personnaliserASTArbre syntaxiqueActionCatégorie
// 1、Enregistrer le plug - in
static FrontendPluginRegistry::Add<YDWPlugin::YDWASTAction> YDW("YDWPlugin", "This is YDWPlugin");
  • Analyse des principes:
    • ① Enregistrer le plug - in,Et personnaliserASTArbre syntaxiqueActionCatégorie
      • De PluginASTAction,Personnalisation ASTAction, Deux méthodes doivent être surchargées ParseArgs Et CreateASTConsumer, L'approche clé est CreateASTConsumer,Il y a un paramètre dans la méthode CI C'est - à - dire compiler l'objet instance ,Principalement utilisé pour Déterminer si le fichier appartient à l'utilisateur EtLancer un avertissement;
      • Adoption FrontendPluginRegistry Enregistrer le plug - in,Le nom du plug - in doit être associé à la personnalisation ASTAction Catégorie;
    • ② Configuration de numérisation terminée
      • De ASTConsumer Catégorie, Implémenter des sous - classes personnalisées YDWASTConsumer,Il y a deux paramètres MatchFinder Objet matcher Et YDWMatchCallback Objet de rappel personnalisé callback;
      • Implémenter le constructeur,Principalement pour créer MatchFinder Objet,Et CI Passer à l'objet de rappel;
      • Réaliser deux méthodes de rappel :HandleTopLevelDecl: Résolution d'une déclaration de haut niveau , Juste un rappel ;
        HandleTranslationUnit: Le fichier entier résout les callbacks complétés ,Contexte après analyse du fichiercontext(C'est - à - dire:ASTArbre syntaxique) Voilà. matcher;
    • ③ Fonction de rappel scannée
      • De MatchFinder::MatchCallback,Classe de rappel personnalisée YDWMatchCallback;
      • Définition CompilerInstance Propriété privée,Pour recevoir ASTConsumer La classe passe CI Information;
      • Réécriture run Méthodes:
        • Adoption result, Selon le marquage du noeud ,Obtenir le noeud approprié, Pour le moment, le marquage doit être effectué en conjonction avec YDWASTConsumer La méthode de construction est cohérente ;
        • Déterminer si le noeud a une valeur , Et c'est un fichier utilisateur, c'est - à - dire isUserSourceCode Méthode privée;
        • Obtenir la description du noeud;
        • Obtenir le type de noeud,Et les transformer en chaînes;
        • Le jugement doit être utilisé copy,Mais non utilisé copy;
        • Adoption CI Obtenir le moteur de diagnostic;
        • Signaler les erreurs par le moteur de diagnostic;
Cinq、Module d'essai
  • Tester le plug - in dans le terminal :
	// Format de commande
	Compilé par lui - mêmeclangChemin du fichier  -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -Xclang -load -Xclang Plug - in(.dyld)Chemin -Xclang -add-plugin -Xclang Nom du plugin -c Chemin source
	
	// Exemples
	/Users/XXX/Desktop/build_xcode/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -Xclang -load -Xclang /Users/XXXX/Desktop/build_xcode/Debug/lib/YDWPlugin.dylib -Xclang -add-plugin -Xclang YDWPlugin -c /Users/XXXX/Desktop/XXX/XXXX/Testsdemo/testClang/testClang/ViewController.m
  • Présentation des résultats:
	/Users/XXX/Desktop/build_xcode/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -Xclang -load -Xclang /Users/XXXX/Desktop/build_xcode/Debug/lib/YDWPlugin.dylib -Xclang -add-plugin -Xclang YDWPlugin -c /Users/XXXX/Desktop/XXX/XXXX/Testsdemo/testClang/testClang/ViewController.m
	...
	Controller.m:12: Warning:
		------- NSString* Ça ne marchera pas. copy Modification --------
	@property (nonatomic, strong) NSString *name;
		------- NSArray* Ça ne marchera pas. copy Modification --------
	@property (nonatomic, strong) NSArray *list;
	...
Six、Xcode Plug - in intégré
  • Charger le plug - in,Ouvrir le projet de test,In target -> Build Settings -> Other C Flags Ajouter ce qui suit::
	 -Xclang -load -Xclang (.dylib)Chemin dynamique de la Bibliothèque -Xclang -add-plugin -Xclang YDWPlugin

Insérer la description de l'image ici

  • Configurer le compilateur,Parce que Clang Le plug - in doit être chargé avec la version correspondante,Si les versions sont incohérentes, la compilation échouera,Comme suit:

Insérer la description de l'image ici

  • In Build Settings Ajouter deux nouveaux paramètres définis par l'utilisateur dans la colonne,Respectivement. CC Et CXX;
    • CC Il correspond à ce qu'il a compilé lui - même Clang Le chemin absolu de;
    • CXX Il correspond à ce qu'il a compilé lui - même Clang++ Le chemin absolu de;

Insérer la description de l'image ici

  • La prochaine fois Build Settings Recherche moyenne index,Oui. Enable Index-Wihle-Building Functionality De Default Lire comme suit: NO;

Insérer la description de l'image ici

  • Enfin,Recompiler le projet de test, Les effets suivants se produiront :

Insérer la description de l'image ici

版权声明
本文为[ForeverWJ]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/10/20211013211741741J.html

Scroll to Top