Include What You Use

Hier können Artikel, Tutorials, Bücherrezensionen, Dokumente aller Art, Texturen, Sprites, Sounds, Musik und Modelle zur Verfügung gestellt bzw. verlinkt werden.
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Antworten
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Include What You Use

Beitrag von Jonathan »

Ich habe heute endlich mal Include what you use ausprobiert.
Wie der Name schon sagt, dient es dazu die Includes zu optimieren und überflüssige zu entfernen, um Compilierzeit zu sparen und den Code übersichtlicher zu machen.
Das Ding ist eher ein Prototyp ("alpha quality software -- at best", wie die Webseite es sagt), es produziert eine Reihe falscher Ergebnisse, aber vieles ist doch ganz brauchbar.

Da ich keine Lust hatte selber zu kompilieren, habe ich Binaries gesucht und auch gefunden: http://www.ishani.org/web/articles/code/clang-win32/
Das sind inoffizielle Clang-Build für Windows, interessant daran ist aber, dass eine clang-IWYU.exe beiliegt, die ohne Abhängigkeiten funktioniert.
Das Tool bedient man über die Commandozeile, was für Windows-User natürlich etwas ungewohnt ist. Außerdem hat clang massive Probleme die Standardbibliothek oder boost zu parsen, weswegen man mit Fehlermeldungen bombardiert wird.

Ich habe daher ein Python-Skritp geschrieben, mit dem man IWYU auf alle Dateien eines Projektes anwenden kann und die Ergebnisse in einer Textdatei geliefert bekommt. IWYU hat zwar auch ein Skript, das Includes automatisch anpasst, aber da das Tool nicht soo zuverlässig ist, will man das vermutlich lieber von Hand machen.

In den ersten zwei Zeilen vom Skript trägt man das Verzeichnis der Quellcodedateien und die Include Verzeichnisse ein. In VC sieht man unter Projektoptionen die Command Line (Configuration Properties -> C/C++ -> Command Line), von dort habe ich die rauskopiert (alles mit /I), anschließend wird es noch umformatiert, damit Clang es versteht.
Dann kann man es starten und sollte eine IWYU-Result.txt bekommen, in der die Ergebnisse aller Dateien stehen.

Wie gesagt, man muss nochmal drübersehen, aber es ist definitiv schneller und einfacher, als alle Includes per Hand zu überprüfen.

Hier das Script:

Code: Alles auswählen

BaseDir="D:/Dev/Projects/Apocalyptical Age/Source/Render"
IncludeDirs=r'/I"C:\Libs\boost\boost_1_55_0" /I"C:\Libs\Assimp\install\include" /I"C:\Libs\glm" /I"D:\Dev\Projects\Apocalyptical Age\Source\Utilities" /I"C:\Libs\FreeImage\Dist"'
IncludeDirs=IncludeDirs.replace("/I", "-I")


def AnalyzeFile(FileName):
    import subprocess, os
    Command='clang-IWYU.exe ' + IncludeDirs + ' -w ' + ' --cwd="'+ BaseDir + '"' + ' "' + FileName + '"'
    #print(Command)
    #os.system(Command)

    def run_command(command):
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        return iter(p.stdout.readline, b'')
    
    Start=False
    Result=""
    for l in run_command(Command):
        line=l.decode("utf8")
        if " should add these lines:" in line:
            Start=True
        if Start:
            Result+=line
    
    return Result
    

def AnalyzeDirectory(Directory):
    import os
    from os.path import join, relpath
    file=open(join(Directory, "IWYU-Result.txt"), "w")
    ext=(".h", ".hpp", ".c", ".cpp", ".inl")
    for root, dirs, files in os.walk(Directory):
        for name in files:
            if name.endswith(ext):
                print("analyzing ", join(root, name))
                FileRes=AnalyzeFile(join(root, name))
                #print(FileRes)
                file.write("------- "+name+" ---------\n")
                file.write(FileRes)
                file.write("\n\n\n\n")
                file.flush()
                os.fsync(file)
    file.close()            
                
AnalyzeDirectory(BaseDir)
Die angehängte Datei enthält das Skript und clang-IWYU.exe, so dass man direkt loslegen kann.
Dateianhänge
IWYU.7z
(1.47 MiB) 338-mal heruntergeladen
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Include What You Use

Beitrag von Jonathan »

Ich habe IWYU gerade mal an einem kompletten Library getestet. In 42 Dateien habe ich immerhin 19 Includes entfernt.

Das Hauptproblem was das Tool hat, ist dass es immer versucht, die Datei zu inkludieren, in der das Symbol auch definiert ist. Man wird also meistens sehr viele interne Header angeboten bekommen (z.B. <string> durch <xstring> ersetzen. Oder <glm/glm.hpp> durch 5-8 <glm/detail/*> Header). Dafür gibt es eine Lösung, man kann ein Mapping angeben, welches Symbol man von welchem Header haben will, was auch recht durchdacht aussieht. Aber leider gibt es nicht wirklich vorgefertigte Mappings für die VC-Standardbibliothek (oder so Dinger wie glm, womit ich die meisten Probleme hatte), so dass es doch auf Handarbeit hinausläuft. Die sich kaum lohnt, weil man es so oft dann ja auch wieder nicht benutzt.

Ansonsten tendiert es dazu, wirklich jedes Symbol explizit zu inkludieren. Man bekommt also in der Regel einige zusätzliche Inkludes empfohlen, die man gar nicht braucht. Dann ist es halt Geschmackssache, ob man sie wegen der Übersicht haben möchte oder nicht - ich denke dank Include-Guards wird es beim Compilieren keinen Unterschied machen.

Was äußerst nett ist, ist das auch Forward-Deklarations beachtet werden. Und gleichnamige Header für cpp-Dateien. Man bekommt also tatsächlich Vorschläge, ein Include in die cpp-Datei zu verschieben und im Header nur eine Forward-Declaration zu schreiben. Und das System ist gut genug um ein Inklude in einem Header zu entfernen und in einer ganz anderen Source-Datei 2 Inkludes vorzuschlagen, die dadurch implizit definiert waren.

Und ich habe sogar noch etwas gelernt. Wenn ein Typ nur Parameter in einer Funktionsdeklaration ist, reicht eine Forward-Deklaration dey Typs. War bei mir bloß einmal der Fall, aber immerhin.

Insgesamt lässt sich IWYU ziemlich leicht verwenden (Das Script zu schreiben hat mit Abstand am längsten gedauert - hauptsächlich weil ich die Ausgabe des Programms nicht in der Konsole, sondern formatiert und gefiltert in einer Datei haben wollte), und liefert wertvolle Hinweise, was man verbessern kann.
Auch wenn es an der Compilierzeit vielleicht nicht schrecklich viel geändert hat, ist es doch ein gutes Gefühl, wenn der eigene Code wieder etwas sauberer ist. Und ich habe von Projekten gelesen, die Inklude-Optimierung 12% schneller kompiliert wurden.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Antworten