ABAP Cleaner ist ein Open-Source-Tool, das ABAP-Code automatisch aufräumt, formatiert und modernisiert. Es wendet über 60 Cleanup-Regeln an und hilft dabei, Code-Standards teamweit einheitlich durchzusetzen.
Was ist ABAP Cleaner?
ABAP Cleaner analysiert ABAP-Code und transformiert ihn automatisch nach konfigurierbaren Regeln. Im Gegensatz zum Pretty Printer geht ABAP Cleaner weit über reine Formatierung hinaus und modernisiert auch Sprachkonstrukte.
| Aspekt | Pretty Printer | ABAP Cleaner |
|---|---|---|
| Fokus | Einrückung, Groß-/Kleinschreibung | Vollständige Code-Modernisierung |
| Regeln | ~5 Grundregeln | 60+ konfigurierbare Regeln |
| Sprachkonstrukte | Keine Änderung | Modernisierung (z.B. NEW statt CREATE OBJECT) |
| Leerzeilen | Einfache Regeln | Intelligente Gruppierung |
| Kommentare | Keine Änderung | Ausrichtung und Formatierung |
| Performance | - | Entfernung unnötiger Statements |
| Profile | Fest vorgegeben | Individuell konfigurierbar |
Vorteile
- Konsistenz: Einheitlicher Code-Stil im gesamten Team
- Produktivität: Automatisches Cleanup spart manuelle Arbeit
- Modernisierung: Alte Syntax wird automatisch aktualisiert
- Lesbarkeit: Bessere Strukturierung und Formatierung
- Code Review: Weniger Diskussionen über Stil-Fragen
- Onboarding: Neue Teammitglieder schreiben sofort konformen Code
Installation in ADT
Voraussetzungen
- Eclipse mit ADT (ABAP Development Tools)
- Eclipse Version 2022-03 oder neuer
- Java 11 oder neuer
Installation via Eclipse Marketplace
- Eclipse öffnen und zu Help → Eclipse Marketplace navigieren
- Suche nach “ABAP Cleaner”
- Install klicken und den Anweisungen folgen
- Eclipse neu starten
Manuelle Installation via Update Site
Falls der Marketplace nicht verfügbar ist:
Help → Install New Software...
Update Site URL:https://sap.github.io/abap-cleaner/updatesite
Verfügbare Features:☑ ABAP Cleaner
→ Next → Accept License → Finish → Restart EclipseInstallation verifizieren
Nach dem Neustart sollte ABAP Cleaner verfügbar sein:
┌──────────────────────────────────────────────────────────────┐│ ADT Toolbar │├──────────────────────────────────────────────────────────────┤│ ││ [📁] [💾] [↩️] [↪️] ... [🧹 ABAP Cleaner] ││ ││ Oder: Source → Clean Up With Interactive ABAP Cleaner... ││ Shortcut: Ctrl+4 (konfigurierbar) ││ │└──────────────────────────────────────────────────────────────┘Wichtigste Cleanup-Regeln
ABAP Cleaner gruppiert seine Regeln in Kategorien. Hier sind die wichtigsten:
1. Empty Lines - Leerzeilen optimieren
Intelligente Gruppierung von Code-Blöcken:
Vorher:
METHOD process_order.
DATA lv_status TYPE string. DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'. lv_amount = calculate_total( ).
update_order( lv_amount ). ENDIF.
ENDMETHOD.Nachher:
METHOD process_order. DATA lv_status TYPE string. DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'. lv_amount = calculate_total( ). update_order( lv_amount ). ENDIF.ENDMETHOD.2. Declarations - Deklarationen modernisieren
Vorher:
DATA: lv_count TYPE i, lt_items TYPE TABLE OF zitem, ls_item TYPE zitem, lo_processor TYPE REF TO zcl_processor.
CREATE OBJECT lo_processor.CLEAR lt_items.Nachher:
DATA lv_count TYPE i.DATA lt_items TYPE TABLE OF zitem.DATA ls_item TYPE zitem.DATA lo_processor TYPE REF TO zcl_processor.
lo_processor = NEW #( ).CLEAR lt_items.3. Syntax - Moderne ABAP-Syntax verwenden
Vorher:
CALL METHOD lo_service->get_data EXPORTING iv_id = lv_id RECEIVING rt_data = lt_data.
IF lt_data IS INITIAL. " ...ENDIF.
LOOP AT lt_data INTO ls_data. MOVE-CORRESPONDING ls_data TO ls_result. APPEND ls_result TO lt_result.ENDLOOP.Nachher:
lt_data = lo_service->get_data( iv_id = lv_id ).
IF lt_data IS INITIAL. " ...ENDIF.
LOOP AT lt_data INTO DATA(ls_data). lt_result = VALUE #( BASE lt_result ( CORRESPONDING #( ls_data ) ) ).ENDLOOP.4. Commands - Überflüssige Befehle entfernen
Vorher:
CLEAR lv_result.lv_result = calculate_value( ).
IF sy-subrc = 0. " ErfolgENDIF.
MOVE lv_source TO lv_target.Nachher:
lv_result = calculate_value( ).
IF sy-subrc = 0. " ErfolgENDIF.
lv_target = lv_source.5. Alignment - Ausrichtung verbessern
Vorher:
ls_order-order_id = lv_id.ls_order-customer = lv_customer.ls_order-status = 'NEW'.ls_order-created_at = sy-datum.ls_order-created_by = sy-uname.ls_order-amount = lv_amount.Nachher:
ls_order-order_id = lv_id.ls_order-customer = lv_customer.ls_order-status = 'NEW'.ls_order-created_at = sy-datum.ls_order-created_by = sy-uname.ls_order-amount = lv_amount.6. Pretty Print - Formatierung
Vorher:
IF LV_STATUS='OPEN'AND LV_AMOUNT>100. CALL METHOD LO_SERVICE->PROCESS(EXPORTING IV_ID=LV_ID CHANGING CT_DATA=LT_DATA).ENDIF.Nachher:
IF lv_status = 'OPEN' AND lv_amount > 100. lo_service->process( EXPORTING iv_id = lv_id CHANGING ct_data = lt_data ).ENDIF.7. Inline Declarations - VALUE und NEW nutzen
Vorher:
DATA lt_orders TYPE TABLE OF zorder.DATA ls_order TYPE zorder.DATA lo_service TYPE REF TO zcl_order_service.
CREATE OBJECT lo_service EXPORTING iv_client = sy-mandt.
ls_order-order_id = '12345'.ls_order-status = 'NEW'.APPEND ls_order TO lt_orders.Nachher:
DATA(lo_service) = NEW zcl_order_service( iv_client = sy-mandt ).
DATA(lt_orders) = VALUE zorders_t( ( order_id = '12345' status = 'NEW' )).8. Comments - Kommentare formatieren
Vorher:
"Diese Methode berechnet den Gesamtpreis"inkl. MwSt und Rabatt."Parameter: iv_net_price - Nettopreis" iv_discount - Rabatt in Prozent"Rückgabe: BruttopreisMETHOD calculate_gross_price.Nachher:
" Diese Methode berechnet den Gesamtpreis" inkl. MwSt und Rabatt." Parameter: iv_net_price - Nettopreis" iv_discount - Rabatt in Prozent" Rückgabe: BruttopreisMETHOD calculate_gross_price.Konfiguration und Profile
Profile-Konzept
ABAP Cleaner verwendet Profile zur Konfiguration. Ein Profil definiert, welche Regeln aktiv sind und wie sie angewendet werden.
┌──────────────────────────────────────────────────────────────┐│ Profile Manager │├──────────────────────────────────────────────────────────────┤│ ││ Profile: ││ ┌────────────────────────────────────────────────────────┐ ││ │ [★] Default │ ││ │ [ ] Team Standard │ ││ │ [ ] Strict Modernization │ ││ │ [ ] Minimal Changes │ ││ │ [ ] + Create new profile... │ ││ └────────────────────────────────────────────────────────┘ ││ ││ [ Edit ] [ Copy ] [ Delete ] [ Export ] [ Import ] ││ │└──────────────────────────────────────────────────────────────┘Profil erstellen und konfigurieren
- Window → Preferences → ABAP Cleaner öffnen
- Profiles auswählen
- Create new profile klicken
Regelkonfiguration
Jede Regel kann individuell eingestellt werden:
┌──────────────────────────────────────────────────────────────┐│ Rule Configuration: "Use inline declarations" │├──────────────────────────────────────────────────────────────┤│ ││ ☑ Rule enabled ││ ││ Options: ││ ┌────────────────────────────────────────────────────────┐ ││ │ Convert DATA declarations: │ ││ │ ○ Only if used once │ ││ │ ● If used in same block │ ││ │ ○ Always (aggressive) │ ││ │ │ ││ │ ☑ Convert FIELD-SYMBOLS │ ││ │ ☑ Keep declarations for complex types │ ││ │ ☐ Convert even if type is lost │ ││ └────────────────────────────────────────────────────────┘ ││ ││ Preview: ││ ┌─────────────────────┐ ┌─────────────────────┐ ││ │ DATA lv_name TYPE │ │ DATA(lv_name) = │ ││ │ string. │→│ get_name( ). │ ││ │ lv_name = get_name( │ │ │ ││ └─────────────────────┘ └─────────────────────┘ ││ │└──────────────────────────────────────────────────────────────┘Profil exportieren und teilen
Profile können als JSON-Datei exportiert und im Team geteilt werden:
{ "profileName": "Team Standard", "rules": { "EmptyLinesInClassDefinition": { "enabled": true, "maxEmptyLines": 1 }, "UseInlineDeclaration": { "enabled": true, "convertFieldSymbols": true, "onlyIfUsedOnce": false }, "ReplaceCreateObject": { "enabled": true }, "RemoveUnusedVariables": { "enabled": false } }}Im Team verteilen:
1. Profil exportieren: Preferences → ABAP Cleaner → Export Profile2. JSON-Datei ins Git-Repository legen: /config/abap-cleaner-profile.json3. Teammitglieder importieren: Preferences → ABAP Cleaner → Import ProfileVerwendung im Alltag
Interaktiver Modus
Der interaktive Modus zeigt alle Änderungen vor der Anwendung:
Shortcut: Ctrl+4 (oder Source → Clean Up With Interactive ABAP Cleaner...)┌──────────────────────────────────────────────────────────────┐│ Interactive ABAP Cleaner │├──────────────────────────────────────────────────────────────┤│ ││ Rule: "Replace obsolete ADD with modern syntax" ││ ││ ┌─────────────────────────────┬─────────────────────────┐ ││ │ Before │ After │ ││ ├─────────────────────────────┼─────────────────────────┤ ││ │ ADD 1 TO lv_count. │ lv_count += 1. │ ││ │ ADD lv_amount TO lv_total. │ lv_total += lv_amount. │ ││ └─────────────────────────────┴─────────────────────────┘ ││ ││ [✓ Apply] [✗ Skip] [Apply All] [Skip All] [Show Rule Info] ││ ││ Changes: 2 of 15 applied | Profile: Team Standard ││ │└──────────────────────────────────────────────────────────────┘Automatischer Modus
Alle Regeln ohne Rückfrage anwenden:
Shortcut: Ctrl+Shift+4 (oder Source → Clean Up With Automatic ABAP Cleaner)Cleanup bei Save
ABAP Cleaner kann bei jedem Speichern automatisch ausgeführt werden:
Preferences → ABAP Cleaner → Clean-up on save
☑ Enable clean-up on save Profile: [Team Standard ▼]
Scope: ○ Current method only ● Changed code blocks ○ Entire source code
☑ Show notification after clean-upTastenkürzel konfigurieren
Window → Preferences → General → Keys
Suche: "ABAP Cleaner"
Ergebnisse:- Clean Up With Interactive ABAP Cleaner: Ctrl+4- Clean Up With Automatic ABAP Cleaner: Ctrl+Shift+4- Open ABAP Cleaner Preferences: Ctrl+Shift+Alt+4Vollständiges Vorher/Nachher-Beispiel
Vorher: Legacy-Code
CLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS: process_order IMPORTING iv_order_id TYPE zorder_id RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_order_error.
PRIVATE SECTION.
DATA: mv_client TYPE mandt. DATA: mo_logger TYPE REF TO zcl_logger.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD process_order.
DATA: lv_status TYPE zorder_status, lt_items TYPE TABLE OF zorder_item, ls_item TYPE zorder_item, lv_total TYPE p DECIMALS 2, lo_validator TYPE REF TO zcl_order_validator.
CREATE OBJECT lo_validator.
CLEAR lv_total.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id EQ iv_order_id.
IF sy-subrc NE 0. RAISE EXCEPTION TYPE zcx_order_error. ENDIF.
IF lv_status EQ 'CLOSED'. rv_success = abap_false. RETURN. ENDIF.
SELECT * FROM zorder_items INTO TABLE lt_items WHERE order_id EQ iv_order_id.
LOOP AT lt_items INTO ls_item. ADD ls_item-amount TO lv_total. ENDLOOP.
CALL METHOD lo_validator->validate EXPORTING iv_order_id = iv_order_id iv_total = lv_total RECEIVING rv_valid = DATA(lv_valid).
IF lv_valid EQ abap_true. UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id. rv_success = abap_true. ELSE. rv_success = abap_false. ENDIF.
ENDMETHOD.
ENDCLASS.Nachher: Modernisierter Code
CLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS process_order IMPORTING iv_order_id TYPE zorder_id RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_order_error.
PRIVATE SECTION. DATA mv_client TYPE mandt. DATA mo_logger TYPE REF TO zcl_logger.ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION. METHOD process_order. DATA(lo_validator) = NEW zcl_order_validator( ).
SELECT SINGLE status FROM zorders INTO @DATA(lv_status) WHERE order_id = @iv_order_id.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_order_error. ENDIF.
IF lv_status = 'CLOSED'. rv_success = abap_false. RETURN. ENDIF.
SELECT * FROM zorder_items INTO TABLE @DATA(lt_items) WHERE order_id = @iv_order_id.
DATA(lv_total) = REDUCE #( INIT sum = 0 FOR ls_item IN lt_items NEXT sum += ls_item-amount ).
DATA(lv_valid) = lo_validator->validate( iv_order_id = iv_order_id iv_total = lv_total ).
IF lv_valid = abap_true. UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id. rv_success = abap_true. ELSE. rv_success = abap_false. ENDIF. ENDMETHOD.ENDCLASS.Zusammenfassung der Änderungen
| Kategorie | Änderung |
|---|---|
| Leerzeilen | Überflüssige Leerzeilen entfernt, logische Gruppierung |
| Deklarationen | Kettete DATA:-Deklarationen aufgelöst |
| CREATE OBJECT | Ersetzt durch NEW #( ) |
| CLEAR | Unnötiges CLEAR entfernt (Variable wird direkt zugewiesen) |
| CALL METHOD | Funktionale Schreibweise verwendet |
| EQ/NE | Ersetzt durch =/<> |
| Inline DATA | Inline-Deklarationen wo sinnvoll |
| REDUCE | LOOP AT mit ADD ersetzt durch REDUCE |
| Alignment | Konsistente Ausrichtung der Parameter |
Integration in CI/CD
ABAP Cleaner kann als Teil der CI/CD-Pipeline genutzt werden, um Code-Standards zu prüfen:
name: ABAP Style Check
on: pull_request: paths: - 'src/**.abap'
jobs: style-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Java uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin'
- name: Run ABAP Cleaner Check run: | # ABAP Cleaner CLI herunterladen wget https://github.com/SAP/abap-cleaner/releases/latest/download/abap-cleaner-cli.jar
# Prüfen, ob Cleanup-Änderungen nötig wären java -jar abap-cleaner-cli.jar \ --profile config/abap-cleaner-profile.json \ --check-only \ --source src/
- name: Upload Report if: failure() uses: actions/upload-artifact@v4 with: name: cleanup-report path: cleanup-report.txtBest Practices
| Thema | Empfehlung |
|---|---|
| Profil-Auswahl | Mit Default-Profil starten, schrittweise anpassen |
| Team-Standard | Ein gemeinsames Profil für das gesamte Team definieren |
| Cleanup on Save | Aktivieren für konsistenten Code |
| Interaktiver Modus | Bei Legacy-Code erst interaktiv prüfen |
| Code Review | Cleanup vor dem Review durchführen |
| Neue Regeln | Bei ABAP Cleaner-Updates neue Regeln prüfen |
| Ausnahmen | Regeln für spezifische Dateien deaktivierbar |
| CI/CD | Als Style-Check in Pipeline integrieren |
| Migration | Legacy-Code schrittweise bereinigen, nicht alles auf einmal |
| Dokumentation | Team-Profil-Entscheidungen dokumentieren |
Weiterführende Themen
- gCTS - Git-basiertes Transport-Management
- CI/CD mit ABAP Cloud - Automatisierte Pipelines
- ADT Tipps & Tricks - Effiziente Entwicklung in Eclipse