How Many Lines of ABAP Code In Your System?
I ran into a curious post by Sergio Ferrari which allows us to calculate how many lines of ABAP code we have in our systems. More than a curiosity, this simple tool can help us determine if our system is too overloaded with custom code or not.
The author excluded ABAP generated by special frameworks, like Report writer, SAP Query, Web Dynpro, LSMW, and so on. He opted to do the filtering by Source System, Owner and Package. The release covered by this tool is ECC 6.0 an the following are also out of scope of this tool:
- BSP’s
- Adobe Forms, Smartforms and SAPScript
- XSLT/Web Services
This utility was named ZSDN_simple_ABAP_SLOC_COUNT, and aims to be an an indicator. The author didn’t want to give it too much relevance, the idea is solely to be an useful tool and help us estimate the upgrade effort or to evaluate the amount of work allocated for each of our packages.
As a curiosity, the post also includes the following information, related to lines of code in known software systems:
- Windows XP: 45 million lines of code
- Windows Vista: 50 million lines of code
- Mac OS: 86 million lines of code
- SAP (ABAP): 238 million lines of ABAP
Copy the code from the textearea below and paste it in a new report in transaction code SE38.
*&---------------------------------------------------------------------* *& Report ZSDN_SIMPLE_ABAP_SLOC_COUNT *& Author: Andrea Olivieri, Sergio Ferrari *& Techedge SpA *& Version: 1.0 - 2009/01/20 *& Title How many lines of custom ABAP code are inside your system? *&---------------------------------------------------------------------* REPORT zsdn_simple_abap_sloc_count_n LINE-SIZE 255. * Global Data Type [ GDT] TYPES: BEGIN OF ty_repository, devclass TYPE tadir-devclass, name TYPE trdir-name, object TYPE tadir-object, srcsystem TYPE tadir-srcsystem, author TYPE tadir-author, subc TYPE trdir-subc, sloc TYPE i, END OF ty_repository. DATA: l_repository TYPE ty_repository. DATA: t_repository TYPE STANDARD TABLE OF ty_repository. * defining the selection-screen TABLES: tadir. SELECT-OPTIONS: xpack FOR tadir-devclass, xobject FOR tadir-object, xauthor FOR tadir-obj_name. INITIALIZATION. PERFORM init_select_options. START-OF-SELECTION. PERFORM get_obj_set. PERFORM upd_obj_set_count_class_sloc. PERFORM count_prog_sloc. END-OF-SELECTION. PERFORM alv. *&---------------------------------------------------------------------* *& Form init_select_options *&---------------------------------------------------------------------* * INCLUDE OBJECTS: PROG, FUGR and CLAS * EXCLUDE PACKAGES: $TMP and from A to W * EXCLUDE USERS: SAP, SAP*, DDIC *----------------------------------------------------------------------* FORM init_select_options. * Include objects: PROG, FUGR and CLAS IF xobject[] IS INITIAL. xobject-sign = 'I'. xobject-option = 'EQ'. xobject-low = 'PROG'. APPEND xobject. xobject-sign = 'I'. xobject-option = 'EQ'. xobject-low = 'FUGR'. APPEND xobject. xobject-sign = 'I'. xobject-option = 'EQ'. xobject-low = 'CLAS'. APPEND xobject. ENDIF. * Exclude packages: $TMP and from A to W IF xpack[] IS INITIAL. xpack-sign = 'E'. xpack-option = 'BT'. xpack-low = 'A'. xpack-high = 'WZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'. APPEND xpack. xpack-sign = 'E'. xpack-option = 'EQ'. xpack-low = '$TMP'. APPEND xpack. ENDIF. * Exclude objects belonging to users: SAP* and DDIC IF xauthor[] IS INITIAL. xauthor-sign = 'E'. xauthor-option = 'EQ'. xauthor-low = 'SAP'. APPEND xauthor. xauthor-sign = 'E'. xauthor-option = 'EQ'. xauthor-low = 'SAP*'. APPEND xauthor. xauthor-sign = 'E'. xauthor-option = 'EQ'. xauthor-low = 'DDIC'. APPEND xauthor. ENDIF. ENDFORM. "init_select_options *&---------------------------------------------------------------------* *& Form get_obj_set *&---------------------------------------------------------------------* * Get from TADIR *----------------------------------------------------------------------* FORM get_obj_set. DATA: l_tadir TYPE tadir. SELECT * FROM tadir INTO l_tadir WHERE pgmid EQ 'R3TR' AND obj_name IN xauthor AND object IN xobject AND devclass IN xpack AND srcsystem NE 'SAP' AND author IN xauthor. "#EC CI_SGLSELECT IF NOT l_tadir-obj_name CP '_______________________________*'. MOVE-CORRESPONDING l_tadir TO l_repository. l_repository-name = l_tadir-obj_name. l_repository-devclass = l_tadir-devclass. APPEND l_repository TO t_repository. ENDIF. ENDSELECT. * SORT t_repository BY devclass name. ENDFORM. "get_obj_set *&---------------------------------------------------------------------* *& Form upd_obj_set_count_class_sloc *&---------------------------------------------------------------------* * ...and Compute SLOC for CLAS *----------------------------------------------------------------------* FORM upd_obj_set_count_class_sloc. TYPE-POOLS: sedi. TYPES: BEGIN OF ty_irdir, name LIKE trdir-name, devc LIKE tadir-devclass, END OF ty_irdir, BEGIN OF ty_itab, repname LIKE sy-repid, devc LIKE tadir-devclass, END OF ty_itab, BEGIN OF ty_crdir, name LIKE trdir-name, subc LIKE trdir-subc, devc LIKE tadir-devclass, END OF ty_crdir. DATA: save_tabix TYPE sy-tabix, l_class TYPE sobj_name, l_pgmid TYPE pgmid, l_object TYPE trobjtype, class_name TYPE programm, class_pool_name TYPE programm, lclskey TYPE seoclskey. DATA: sourceline TYPE string. DATA: l_1st_char TYPE c. DATA: l_irdir TYPE ty_irdir, l_itab TYPE ty_itab, l_itab1 TYPE ty_itab, l_crdir TYPE ty_crdir. DATA: t_irdir TYPE STANDARD TABLE OF ty_irdir, t_itab TYPE STANDARD TABLE OF ty_itab, t_itab1 TYPE STANDARD TABLE OF ty_itab, t_crdir TYPE STANDARD TABLE OF ty_crdir. DATA: l_sloc TYPE i. LOOP AT t_repository INTO l_repository. save_tabix = sy-tabix. CASE l_repository-object. WHEN 'PROG'. SELECT SINGLE subc FROM trdir INTO l_repository-subc WHERE name = l_repository-name. IF sy-subrc = 0. MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING subc . ENDIF. WHEN 'FUGR'. CONCATENATE 'SAPL' l_repository-name INTO l_irdir-name. l_irdir-devc = l_repository-devclass. APPEND l_irdir TO t_irdir. l_repository-name = l_irdir-name. l_repository-object = 'PROG'. SELECT SINGLE subc FROM trdir INTO l_repository-subc WHERE name = l_repository-name. IF sy-subrc = 0. MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING name object subc. ENDIF. CLEAR l_irdir. WHEN 'CLAS'. DATA source_new TYPE sedi_source. DATA l_inctype(5). class_name = l_repository-name. l_pgmid = 'R3TR'. l_object = l_repository-object. l_class = class_name. CALL FUNCTION 'SEO_CLASS_GET_CP_NAME' EXPORTING pgmid = l_pgmid object = l_object obj_name = l_class IMPORTING progname = class_pool_name EXCEPTIONS no_class_or_interface = 1. IF sy-subrc = 0. SELECT SINGLE subc FROM trdir INTO l_repository-subc WHERE name = class_pool_name. ENDIF. lclskey-clsname = cl_oo_classname_service=>get_clsname_by_include( class_name ). CLEAR l_class. CONCATENATE l_repository-name '%' INTO l_class. SELECT name FROM trdir INTO TABLE t_crdir WHERE name LIKE l_class. IF NOT t_crdir[] IS INITIAL. LOOP AT t_crdir INTO l_crdir. l_inctype = l_crdir-name+30(5) . REFRESH source_new. CALL FUNCTION 'SEO_CLASS_GET_INCLUDE_SOURCE' EXPORTING clskey = lclskey inctype = l_inctype IMPORTING source_expanded = source_new EXCEPTIONS _internal_class_not_existing = 1 not_existing = 2 OTHERS = 3. CHECK sy-subrc = 0. * Compute SLOC ! LOOP AT source_new INTO sourceline. l_1st_char = sourceline. IF sourceline IS INITIAL OR l_1st_char = '*'. DELETE source_new INDEX sy-tabix. ENDIF. ENDLOOP. l_sloc = LINES( source_new ). ADD l_sloc TO l_repository-sloc. ENDLOOP. MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING sloc subc. ENDIF. WHEN OTHERS. CONTINUE. ENDCASE. ENDLOOP. IF t_irdir[] IS NOT INITIAL. LOOP AT t_irdir INTO l_irdir. REFRESH t_itab1. CALL FUNCTION 'GET_INCLUDES' EXPORTING progname = l_irdir-name TABLES incltab = t_itab1. * append lines of t_itab1 to itab. LOOP AT t_itab1 INTO l_itab1. l_itab-repname = l_itab1-repname. l_itab-devc = l_irdir-devc. "Sometimes it's false APPEND l_itab TO t_itab. CLEAR l_itab. ENDLOOP. ENDLOOP. ENDIF. DATA: l_trdir TYPE trdir. LOOP AT t_itab INTO l_itab. SELECT SINGLE * FROM trdir INTO l_trdir WHERE name = l_itab-repname AND cnam NOT LIKE 'SAP%' AND cnam <> 'DDIC'. CHECK sy-subrc = 0. MOVE-CORRESPONDING l_trdir TO l_repository. l_repository-devclass = l_itab-devc. l_repository-object = 'PROG'. APPEND l_repository TO t_repository. ENDLOOP. SORT t_repository BY name object devclass. DELETE ADJACENT DUPLICATES FROM t_repository COMPARING name object devclass. ENDFORM. "upd_obj_set_count_class_sloc *&---------------------------------------------------------------------* *& Form count_prog_sloc *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* FORM count_prog_sloc. DATA: l_prog TYPE c LENGTH 30. DATA: isource TYPE TABLE OF string. DATA: l_1st_char TYPE c. LOOP AT t_repository INTO l_repository WHERE object = 'PROG'. * Compute SLOC ! l_prog = l_repository-name. READ REPORT l_prog INTO isource. DATA: l_source_line TYPE string. LOOP AT isource INTO l_source_line. l_1st_char = l_source_line. IF l_source_line IS INITIAL "Empty OR l_1st_char = '*'. "Comment DELETE isource INDEX sy-tabix. ENDIF. ENDLOOP. l_repository-sloc = LINES( isource ). * Update SLOC into t_repository READ TABLE t_repository WITH TABLE KEY name = l_repository-name devclass = l_repository-devclass object = l_repository-object srcsystem = l_repository-srcsystem author = l_repository-author subc = l_repository-subc TRANSPORTING NO FIELDS. IF sy-subrc = 0. MODIFY t_repository FROM l_repository INDEX sy-tabix TRANSPORTING sloc. ENDIF. ENDLOOP. ENDFORM. "count_prog_sloc *&---------------------------------------------------------------------* *& Form alv *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* FORM alv. DATA: gr_table TYPE REF TO cl_salv_table, gr_display TYPE REF TO cl_salv_display_settings, gr_functions TYPE REF TO cl_salv_functions, gr_sorts TYPE REF TO cl_salv_sorts, gr_agg TYPE REF TO cl_salv_aggregations. TRY. * Create ALV table cl_salv_table=>factory( IMPORTING r_salv_table = gr_table CHANGING t_table = t_repository ). * Set zebra layout gr_display = gr_table->get_display_settings( ). gr_display->set_striped_pattern( cl_salv_display_settings=>true ). * Display all standard function gr_functions = gr_table->get_functions( ). gr_functions->set_all( abap_true ). * Sort gr_sorts = gr_table->get_sorts( ). gr_sorts->add_sort( columnname = 'DEVCLASS' subtotal = abap_true ). gr_sorts->add_sort( columnname = 'NAME' subtotal = abap_false ). * Totals gr_agg = gr_table->get_aggregations( ). gr_agg->add_aggregation( 'SLOC' ). * Display table gr_table->display( ). CATCH cx_salv_msg. WRITE: / 'Exception CX_SALV_MSG'. CATCH cx_salv_not_found. WRITE: / 'Exception CX_SALV_NOT_FOUND'. CATCH cx_salv_data_error. WRITE: / 'Exception CX_SALV_DATA_ERROR'. CATCH cx_salv_existing. WRITE: / 'Exception CX_SALV_EXISTING'. ENDTRY. ENDFORM. "alv
Additional links and resources:
















