Skip to end of metadata
Go to start of metadata

Author: Andrea Olivieri
Submitted: 21 December 2009

Andrea Olivieri Nov 10, 2014 2:18 PM

Dear All,

ABAPSloc is now available as Code Inspector check (SCI) and it's part of CloneFinder in SAP Custom Code Applications (Just start tcode /SDF/CD_CCA) and for this reasons is quite useless to continue the development of this tool.

However, I set up a repository in GitHub; you can download the ABAPSloc here.

Best,
Andrea

Source: http://scn.sap.com/community/abap/blog/2011/10/25/how-many-lines-of-custom-abap-code-are-inside-your-system-part3#comment-541363

See the UPDATES section below for change log

This is the continuation of the previous blog How many lines of custom ABAP code are inside your system? published at the beginning of 2009 by Sergio Ferrari, my colleague and SAP Mentor. The SLOC, the number of line of ABAP code of custom objects, is one of the most interesting indicators provided during a software assessment as described here. In this wiki I am glad to share with the great SDN community the new version of the ABAP ZSDN_SIMPLE_ABAP_SLOC_COUNT, developed at night, during the few hours of free time. Let me summarize briefly the improvements and extensions that this new version introduces.

Main Changes

• Fix some bugs of the first version reported in the previous blog
• New management of function groups and related objects.

News

• Packages and Objects descriptions
• Type Groups
• Dynpro Flow Logic of module pools and function groups (even if they are not properly ABAP ...)
• BSP source (ABAP)

Currently out of scope

• Enhancement implementations (hook, class, etc. ...)
• Adobe Form / SmartForms / SAPscript
• XSLT
• Web Dynpro

Supported releases

• SAP ECC 6.0

Download

Latest version in blog post: How many lines of custom ABAP code are inside your system? – Part 3

ZSDN_SIMPLE_ABAP_SLOC_COUNT (Version 2.2)
*&---------------------------------------------------------------------*
*& Report  ZSDN_SIMPLE_ABAP_SLOC_COUNT
*& Author: Andrea Olivieri, Sergio Ferrari
*&         Techedge SpA
*& Version: 2.2  - 2011/10/12
*& Title   How many lines of custom ABAP code are inside your system?
*&---------------------------------------------------------------------*

* http://scn.sap.com/community/abap/blog/2011/10/25/how-many-lines-of-custom-abap-code-are-inside-your-system-part3

REPORT  zsdn_simple_abap_sloc_count_n LINE-SIZE 255.

TYPE-POOLS: sedi, seop.
* Global Data Type [ GDT ;-) ]
TYPES: BEGIN OF ty_repository,
         devclass       TYPE tadir-devclass,
         ctext          TYPE tdevct-ctext,
         name           TYPE e071-obj_name,
         object         TYPE tadir-object,
         text           TYPE trdirt-text,
         srcsystem      TYPE tadir-srcsystem,
         author         TYPE tadir-author,
         subc           TYPE trdir-subc,
         sloc      TYPE i,
       END OF ty_repository.
TYPES: BEGIN OF ty_repository_key,
         devclass       TYPE tadir-devclass,
         obj_name       TYPE tadir-obj_name,
         object         TYPE tadir-object,
       END OF ty_repository_key.
TYPES: BEGIN OF devclass_ty,
        devclass TYPE tdevc-devclass,
        ctext    TYPE tdevc-ctext,
      END OF devclass_ty.

TYPES: BEGIN OF ty_irdir,
          name      LIKE trdir-name,
          devc      LIKE tadir-devclass,
          author    LIKE tadir-author,
          srcsystem LIKE tadir-srcsystem,
        END OF ty_irdir.
DATA: l_irdir         TYPE ty_irdir,
      t_irdir TYPE STANDARD TABLE OF ty_irdir.

DATA: gtypes         TYPE tfpcoding,
      gcoding        TYPE tfpcoding,
      fcoding        TYPE tfpcoding,
      ftab           TYPE tfpcoding,
      node_code      TYPE tsfcode,
      l_node_code LIKE LINE OF node_code,
      ltab LIKE LINE OF ftab.

TYPES:  t_tab_code        TYPE tsffbcntco,
        t_tab_page        TYPE tsffbcntpa,
        t_tab_window      TYPE tsffbcntwi.
DATA:   l_codes    TYPE  ssffbcntco,
        l_pages    TYPE  ssffbcntpa,
        l_windows  TYPE   ssffbcntwi.

DATA: g_codes             TYPE t_tab_code,
      g_pages             TYPE t_tab_page ,
      g_windows           TYPE t_tab_window .
DATA: g_tabix   TYPE i,
      g_counter TYPE i.

DATA: gdata          TYPE tsfgdata.


DATA: l_repository TYPE ty_repository.
DATA: t_repository TYPE STANDARD TABLE OF ty_repository.
DATA: t_repository_dynp TYPE STANDARD TABLE OF ty_repository.
DATA: repository_dynp TYPE ty_repository.
DATA: t_devclass TYPE SORTED TABLE OF devclass_ty WITH UNIQUE KEY devclass,
      l_devclass TYPE devclass_ty.

INCLUDE rstxsfcdef.
* Makro
*
DEFINE incr_counter.
  g_tabix   = 0.
  g_counter = 0.
  loop at &1 into &3.
    if &3-obj = &2.
      g_tabix = sy-tabix.
      add 1 to &3-cnt.
      modify &1 from &3 index g_tabix.
      g_counter = &3-cnt.
      exit.
    endif.
  endloop.
  if g_tabix = 0.
    &3-obj ?= &2.
    &3-cnt = 1.
    append &3 to &1.
    describe table &1 lines g_tabix.
    g_counter = 1.
  endif.
END-OF-DEFINITION.


*--------------------------------------------------------------------*
* defining the selection-screen
*--------------------------------------------------------------------*
TABLES: tadir.
SELECT-OPTIONS: xpack    FOR tadir-devclass,
                xobject  FOR tadir-object,
                xobjname FOR tadir-obj_name,
                xauthor  FOR tadir-author.
SELECTION-SCREEN SKIP.
PARAMETERS:     xsvim AS CHECKBOX.
*--------------------------------------------------------------------*
* Initialization
*--------------------------------------------------------------------*
INITIALIZATION.
  PERFORM init_selection_texts.
  PERFORM init_select_options.
*--------------------------------------------------------------------*
* Start of Selection
*--------------------------------------------------------------------*
START-OF-SELECTION.
  PERFORM get_obj_set.
  PERFORM upd_obj_set_count_class_sloc.
  PERFORM count_prog_sloc.
  PERFORM provide_missing_text.
*--------------------------------------------------------------------*
* End of Selection
*--------------------------------------------------------------------*
END-OF-SELECTION.
  PERFORM alv.

*&---------------------------------------------------------------------*
*&      Form  init_select_options
*&---------------------------------------------------------------------*
*       INCLUDE OBJECTS:  PROG, FUGR, CLAS, TYPE, WAPA
*       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.
    xobject-sign   = 'I'. xobject-option = 'EQ'. xobject-low = 'TYPE'. APPEND xobject.
    xobject-sign   = 'I'. xobject-option = 'EQ'. xobject-low = 'WAPA'. APPEND xobject.
    xobject-sign   = 'I'. xobject-option = 'EQ'. xobject-low = 'SSFO'. APPEND xobject.
    xobject-sign   = 'I'. xobject-option = 'EQ'. xobject-low = 'SFPF'. 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 xobjname
           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.
      APPEND l_repository TO t_repository.
    ENDIF.
  ENDSELECT.

  "Removes Generated Function Groups of View Maintenance (we don't care about mixed FUGR)
  IF xsvim IS NOT INITIAL.
    LOOP AT t_repository INTO l_repository.
      IF l_repository-object <> 'FUGR'. CONTINUE. ENDIF.
      DATA l_area TYPE tvdir-area.
      SELECT SINGLE area INTO l_area FROM tvdir WHERE area = l_repository-name.
      IF sy-subrc = 0.
        DELETE t_repository.
      ENDIF.
    ENDLOOP.
  ENDIF.

  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.
  TYPES:
        BEGIN OF ty_itab,
          repname   LIKE sy-repid,
          devc      LIKE tadir-devclass,
          author    LIKE tadir-author,
          srcsystem LIKE tadir-srcsystem,
        END OF ty_itab.

  DATA: l_itab          TYPE ty_itab,
        l_itab1         TYPE ty_itab.
  DATA: t_itab  TYPE STANDARD TABLE OF ty_itab,
        t_itab1 TYPE STANDARD TABLE OF ty_itab.
  DATA: save_tabix      TYPE sy-tabix.
*--------------------------------------------------------------------*

  LOOP AT t_repository INTO l_repository.
    save_tabix = sy-tabix.
    CASE l_repository-object.
      WHEN 'PROG'.
*-----  Report, Module Pool, Includes
        PERFORM get_prog_info TABLES t_repository
                              USING  l_repository save_tabix.

        IF l_repository-subc CA '1M'.
          PERFORM add_dynpro_info  TABLES t_repository_dynp
                                   USING  l_repository.
        ENDIF.
      WHEN 'FUGR'.
*-----  Function Group
        PERFORM get_fgroup_info TABLES t_repository
                                USING  l_repository save_tabix.
        PERFORM add_dynpro_info  TABLES t_repository_dynp
                                 USING  l_repository.

      WHEN 'CLAS'.
*-----  Class
        PERFORM get_class_info TABLES t_repository
                               USING  l_repository save_tabix.

      WHEN 'TYPE'.
*-----  Type Groups
        PERFORM get_tygr_info TABLES t_repository
                              USING  l_repository save_tabix.

      WHEN 'WAPA'.
*-----  BSP Application
        PERFORM get_bsp_info  TABLES t_repository
                              USING  l_repository save_tabix.

      WHEN 'SSFO'.
*-----  Smartforms
        PERFORM get_ssfo_info  TABLES t_repository
                               USING  l_repository save_tabix.

      WHEN 'SFPF'.
*-----  Adobe Form
        PERFORM get_sfpf_info  TABLES t_repository
                               USING  l_repository save_tabix.

      WHEN OTHERS. CONTINUE.
    ENDCASE.

  ENDLOOP.

  IF t_irdir[] IS NOT INITIAL.
    SORT t_irdir BY name.
    DELETE ADJACENT DUPLICATES FROM t_irdir COMPARING name.
    LOOP AT t_irdir INTO l_irdir.
      REFRESH:  t_itab1.
      CALL FUNCTION 'GET_INCLUDETAB'
        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.
        l_itab-author    = l_irdir-author.
        l_itab-srcsystem = l_irdir-srcsystem.
        APPEND l_itab TO t_itab.
        CLEAR l_itab.
      ENDLOOP.
    ENDLOOP.
  ENDIF.

  DATA: l_trdir  TYPE trdir,
        l_custom TYPE trpari-w_nameclas.

* Rebuilds repository for missing includes
  CLEAR: l_repository.
  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-author    = l_itab-author.
    l_repository-srcsystem = l_itab-srcsystem.
    l_repository-object    = 'PROG'.
    APPEND l_repository TO t_repository.

  ENDLOOP.

  IF NOT t_repository_dynp IS INITIAL.
    APPEND LINES OF t_repository_dynp TO t_repository.
  ENDIF.

* Delete Pollution (entries with sloc = 0)
* DELETE t_repository WHERE sloc = 0.                        "FIX #3 20110107-

  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' OR object = 'TYPE'.
*   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
                                       ctext     = l_repository-ctext
                                       object    = l_repository-object
                                       text      = l_repository-text
                                       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  PROVIDE_MISSING_TEXT
*&---------------------------------------------------------------------*
* Get DEVCs and Programs descriptions
*----------------------------------------------------------------------*
FORM provide_missing_text .
  DATA: save_tabix TYPE sy-tabix,
        ltdevct    TYPE tdevct,
        ltlibt     TYPE tlibt,
*        lfugr      TYPE rs38l-area,
        lnamespace TYPE rs38l-namespace.

  DATA: ltrdirt TYPE trdirt,
        ltftit  TYPE tftit,
        lfuncname TYPE tftit-funcname.

  DATA:  lfgroup TYPE  rs38l-str_area,
        lprog    TYPE   rs38l-include.

  TYPES: BEGIN OF n_ty,
           name TYPE progname,
         END OF n_ty.
  DATA:  xxkey TYPE SORTED TABLE OF n_ty WITH UNIQUE KEY name,
         lxkey TYPE n_ty.
*--------------------------------------------------------------------*
  CHECK NOT t_repository[] IS INITIAL.                                    "Fix #2 20111027+
* Get Development Classes
  LOOP AT t_repository INTO l_repository.
    l_devclass-devclass = l_repository-devclass.
    INSERT l_devclass INTO TABLE t_devclass.
  ENDLOOP.
* Get DEVC Text
  LOOP AT t_devclass INTO l_devclass.
    CLEAR ltdevct.
    SELECT SINGLE * FROM tdevct INTO ltdevct
      WHERE devclass = l_devclass-devclass AND spras = sy-langu.
    IF sy-subrc = 0.
      l_repository-ctext = ltdevct-ctext.
      MODIFY t_repository FROM l_repository TRANSPORTING ctext WHERE devclass = l_devclass-devclass.
    ENDIF.
  ENDLOOP.

* Get Abap Program Short Text
  LOOP AT t_repository INTO l_repository.
    lxkey-name = l_repository-name.
    INSERT lxkey INTO TABLE xxkey.
  ENDLOOP.

  SELECT name text FROM trdirt INTO (ltrdirt-name,ltrdirt-text)
                FOR ALL ENTRIES IN xxkey
                WHERE name  EQ xxkey-name
                AND   sprsl EQ sy-langu.
    READ TABLE t_repository INTO l_repository WITH KEY name = ltrdirt-name.
    CHECK sy-subrc EQ 0.
    l_repository-text = ltrdirt-text.
    MODIFY t_repository FROM l_repository INDEX sy-tabix TRANSPORTING text.
  ENDSELECT.


* Get Function Group and Function Module Short Text
  LOOP AT t_repository INTO l_repository WHERE  text EQ space AND
       ( name CS 'SAPL'  ) OR  ( name(1) EQ 'L' AND name CS 'U' ).

    save_tabix = sy-tabix.
    CLEAR: lprog, lnamespace, lfgroup, ltlibt, ltftit .

    lprog = l_repository-name.

    IF l_repository-name CS 'SAPL'.
      CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
        EXPORTING
          program   = lprog
        IMPORTING
          namespace = lnamespace
          group     = lfgroup
        EXCEPTIONS
          OTHERS    = 1.


      IF lfgroup  NE space AND sy-subrc EQ 0.
        CONCATENATE lnamespace lfgroup INTO lfgroup.
*       Function Group Text
        SELECT SINGLE * FROM tlibt INTO ltlibt WHERE spras = sy-langu
                                   AND   area  = lfgroup.
        IF sy-subrc = 0.
          l_repository-text =  ltlibt-areat.
          MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING text.
        ENDIF.
      ENDIF.
    ELSE.
      CLEAR lfuncname.
      CALL FUNCTION 'FUNCTION_INCLUDE_INFO'
        CHANGING
          funcname = lfuncname
          include  = lprog
        EXCEPTIONS
          OTHERS   = 1.

      IF sy-subrc EQ 0 AND lfuncname NE space.
        SELECT  SINGLE * FROM  tftit INTO ltftit
               WHERE  spras       = sy-langu
               AND    funcname    = lfuncname.
        IF sy-subrc = 0.
          l_repository-text =  ltftit-stext.
          MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING text.
        ENDIF.
      ENDIF.
    ENDIF.
  ENDLOOP.

ENDFORM.                    " PROVIDE_MISSING_TEXT
*&---------------------------------------------------------------------*
*&      Form  ADD_DYNPRO_INFO
*&---------------------------------------------------------------------*
*       Add DYNPRO Text and flow locic SLOC
*----------------------------------------------------------------------*
FORM add_dynpro_info    TABLES   t_repository_dynp LIKE t_repository_dynp
                        USING    l_repository TYPE ty_repository.

* Local Data
  DATA: sourceline      TYPE string.
  DATA: l_1st_char      TYPE c,
        l_sloc TYPE i.

  DATA: td020s   TYPE TABLE OF d020s,
        ld020s   TYPE d020s,
        flowtab  TYPE TABLE OF d022s ,
        flowline TYPE d022s,
        dynp_text TYPE d020t-dtxt.
*        repository_dynp TYPE ty_repository.
  DATA:  BEGIN OF dynpro_id,
             prog LIKE d020s-prog,
             dnum LIKE d020s-dnum,
           END   OF dynpro_id.
*--------------------------------------------------------------------*

  SELECT * FROM d020s INTO TABLE td020s
    WHERE  type NE 'S'                        "Selection Screen
    AND    type NE 'J'
    AND    prog = l_repository-name.

  LOOP AT td020s INTO ld020s.
    CLEAR repository_dynp.
    REFRESH: flowtab.
    CLEAR:   flowline, dynp_text.
    dynpro_id-prog = ld020s-prog.
    dynpro_id-dnum = ld020s-dnum.

    CALL FUNCTION 'RPY_DYNPRO_READ_NATIVE'
      EXPORTING
        progname         = ld020s-prog
        dynnr            = ld020s-dnum
      IMPORTING
        dynprotext       = dynp_text
      TABLES
        flowlogic        = flowtab
      EXCEPTIONS
        cancelled        = 1
        not_found        = 2
        permission_error = 3
        OTHERS           = 4.
    IF sy-subrc = 0.
      repository_dynp = l_repository.
      repository_dynp-name   = dynpro_id.
      repository_dynp-object = 'DYNP'.
      repository_dynp-text   = dynp_text.
*           Compute SLOC !
      LOOP AT flowtab INTO flowline.
        l_1st_char = sourceline.
        IF    flowline IS INITIAL
           OR l_1st_char = '*'.
          DELETE flowtab INDEX sy-tabix.
        ENDIF.
      ENDLOOP.
      l_sloc = lines( flowtab ).
      repository_dynp-sloc = l_sloc.
      APPEND repository_dynp TO t_repository_dynp.
      CLEAR repository_dynp.
    ENDIF.
  ENDLOOP.

ENDFORM.                    " ADD_DYNPRO_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_CLASS_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_class_info  TABLES   t_repository LIKE t_repository
                     USING    l_repository TYPE ty_repository
                              save_tabix   TYPE sy-tabix.
* Local Data
  DATA source_new TYPE sedi_source.
  DATA l_inctype(5).

  TYPES: BEGIN OF ty_crdir,
           name LIKE trdir-name,
           subc LIKE trdir-subc,
           devc LIKE tadir-devclass,
         END OF ty_crdir.
  DATA: 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: source_wa TYPE tdline.                                    "Fix 20111001+
  DATA: source_tab TYPE TABLE OF tdline.           "Coding        "Fix 20111001+
  DATA: all_methods TYPE seop_methods_w_include.   "All Methods   "Fix 20111001+
  DATA: all_methods_wa TYPE seop_method_w_include.                "Fix 20111001+
  DATA: source_key TYPE seocpdkey.
  DATA: sourceline      TYPE string.
  DATA: l_1st_char      TYPE c.
  DATA: l_crdir         TYPE ty_crdir.
  DATA: t_crdir TYPE STANDARD TABLE OF ty_crdir.

  DATA: l_sloc          TYPE i.
*--------------------------------------------------------------------*

  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.
*   IF sy-subrc = 0.                           "Fix 20111001 -
    IF sy-subrc <> 0.                          "Fix 20111001+
      DELETE t_repository INDEX save_tabix.
    ENDIF.
  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.
*--> Begin of Fix 20111001 ---------------------------------------------------------------------*
      IF l_inctype CP 'CM+++'.
        IF all_methods IS INITIAL.
          CALL FUNCTION 'SEO_CLASS_GET_METHOD_INCLUDES'
            EXPORTING
              clskey                       = lclskey
            IMPORTING
              includes                     = all_methods
            EXCEPTIONS
              _internal_class_not_existing = 1
              OTHERS                       = 2.
        ENDIF.

        READ TABLE all_methods INTO all_methods_wa WITH KEY incname = l_crdir-name.
        IF sy-subrc = 0.
          CLEAR source_new.

          source_key-clsname = lclskey-clsname.
          source_key-cpdname = all_methods_wa-cpdkey-cpdname.

          CALL FUNCTION 'SEO_METHOD_GET_SOURCE'
            EXPORTING
              mtdkey                        = source_key
            IMPORTING
              source_expanded               = source_new
            EXCEPTIONS
              _internal_method_not_existing = 1
              _internal_class_not_existing  = 2
              OTHERS                        = 3.
        ELSE.
*<-- End   of Fix 20111001 ---------------------------------------------------------------------*
          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.
        ENDIF.

      ENDIF.
      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.

ENDFORM.                    " GET_CLASS_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_FGROUP_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_fgroup_info TABLES   t_repository LIKE t_repository
                     USING    l_repository TYPE ty_repository
                              save_tabix   TYPE sy-tabix.

  DATA: lgroup TYPE rs38l-area,
        lpname TYPE tfdir-pname.

  lgroup = l_repository-name.

  CALL FUNCTION 'FUNCTION_INCLUDE_INFO'
    IMPORTING
      pname  = lpname
    CHANGING
      group  = lgroup
    EXCEPTIONS
      OTHERS = 0.

* Function group doesn't exist
  IF lpname IS INITIAL.
    CONCATENATE 'SAPL' lgroup INTO lpname.
  ENDIF.

  " Prepare entries for INCLUDEs reading
  l_irdir-name      = lpname.
  l_irdir-devc      = l_repository-devclass.
  l_irdir-srcsystem = l_repository-srcsystem.
  l_repository-name   = l_irdir-name.
  l_repository-object = 'PROG'.

  SELECT SINGLE subc unam FROM trdir INTO (l_repository-subc, l_repository-author) WHERE  name = l_repository-name.
  IF sy-subrc = 0.
    MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING name object subc srcsystem author.
  ELSE.
    DELETE t_repository INDEX save_tabix.
*    RETURN.
  ENDIF.

  l_irdir-author    = l_repository-author.

  APPEND l_irdir TO t_irdir.
  CLEAR l_irdir.

ENDFORM.                    " GET_FGROUP_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_PROG_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_prog_info  TABLES   t_repository LIKE t_repository
                    USING    l_repository TYPE ty_repository
                             save_tabix   TYPE sy-tabix.

  SELECT SINGLE subc unam FROM trdir INTO (l_repository-subc, l_repository-author) WHERE  name = l_repository-name.
  IF sy-subrc = 0. " and l_repository-subc <> 'I'.
    MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING name object subc srcsystem author.
  ELSE.
    DELETE t_repository INDEX save_tabix.
  ENDIF.

  " Prepare entries for INCLUDEs reading
  l_irdir-name      = l_repository-name.
  l_irdir-devc      = l_repository-devclass.
  l_irdir-srcsystem = l_repository-srcsystem.
  l_irdir-author    = l_repository-author.

  APPEND l_irdir TO t_irdir.
  CLEAR l_irdir.

ENDFORM.                    " GET_PROG_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_TYGR_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_tygr_info  TABLES   t_repository LIKE t_repository
                    USING    l_repository TYPE ty_repository
                             save_tabix   TYPE sy-tabix.

  CONSTANTS: inclprefix(3) VALUE '%_C'.     "Type Group
  DATA:      tygr_name       TYPE programm.

  CONCATENATE inclprefix l_repository-name INTO tygr_name.
  SELECT SINGLE subc FROM trdir INTO l_repository-subc WHERE  name = tygr_name.
  IF sy-subrc = 0.
    SELECT SINGLE ddtext FROM ddtypet INTO l_repository-text
     WHERE typegroup = l_repository-name
     AND ddlanguage  = sy-langu.

    l_repository-name   = tygr_name.
    MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING name subc .
  ELSE.
    DELETE t_repository INDEX save_tabix.
  ENDIF.

ENDFORM.                    " GET_TYGR_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_BSP_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_bsp_info  TABLES   t_repository LIKE t_repository
                    USING    l_repository TYPE ty_repository
                             save_tabix   TYPE sy-tabix.

  DATA: to2pagdir TYPE TABLE OF o2pagdir,
        lo2pagdir TYPE o2pagdir,
        l_pagekey TYPE o2pagkey,
        source_bsp TYPE rswsourcet.
  DATA: sourceline      TYPE string.
  DATA: l_1st_char      TYPE c,
        l_sloc TYPE i.

* Get Pages with logic
  SELECT * FROM  o2pagdir INTO TABLE to2pagdir
         WHERE  applname  = l_repository-name
         AND    pagetype  = space.

  CHECK sy-subrc = 0.

* Get Application Text
  SELECT SINGLE applext FROM o2appl INTO l_repository-text
       WHERE  applname  = l_repository-name
       AND    version   = 'A'.   "Active

  LOOP AT to2pagdir INTO lo2pagdir.

    l_pagekey-applname =  lo2pagdir-applname.
    l_pagekey-pagekey  =  lo2pagdir-pagekey.

    CALL FUNCTION 'BSP_GET_SOURCE'
      EXPORTING
        p_pagekey         = l_pagekey
      IMPORTING
        p_source          = source_bsp[]
      EXCEPTIONS
        page_not_existing = 0
        OTHERS            = 0.

    LOOP AT source_bsp INTO sourceline.
      l_1st_char = sourceline.
      IF    sourceline IS INITIAL
         OR l_1st_char = '*'.
        DELETE source_bsp INDEX sy-tabix.
      ENDIF.
    ENDLOOP.
    l_sloc = lines( source_bsp ).
    ADD l_sloc TO l_repository-sloc.

    MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING sloc text.
  ENDLOOP.

ENDFORM.                    " GET_BSP_INFO
*&---------------------------------------------------------------------*
*&      Form  INIT_SELECTION_TEXTS
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM init_selection_texts .

  %_xpack_%_app_%-text    = 'Package'.
  %_xobject_%_app_%-text  = 'Object Type'.
  %_xobjname_%_app_%-text = 'Object Name'.
  %_xauthor_%_app_%-text  = 'Person Responsible'.
  %_xsvim_%_app_%-text    = 'Removes Gen.View Maint. FUGR'.

ENDFORM.                    " INIT_SELECTION_TEXTS
*&---------------------------------------------------------------------*
*&      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,
        gr_columns   TYPE REF TO cl_salv_columns_table.     "20111107+
  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 ).
*     Optimize columns                                                              "20111107+
      gr_columns = gr_table->get_columns( ).                "20111107+
      gr_columns->set_optimize( abap_true ).                "20111107+
*     Sort
      gr_sorts = gr_table->get_sorts( ).
      gr_sorts->add_sort( columnname = 'DEVCLASS' subtotal = abap_true ).
*     gr_sorts->add_sort( columnname = 'CTEXT'    subtotal = abap_false ).
      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
*&---------------------------------------------------------------------*
*&      Form  GET_SSFO_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_ssfo_info  TABLES   t_repository LIKE t_repository
                    USING    l_repository TYPE ty_repository
                             save_tabix   TYPE sy-tabix.

  DATA: sform             TYPE REF TO cl_ssf_fb_smart_form,
        mode              TYPE tdsfflag.

  DATA: formname    TYPE tdsfname,
        variant     TYPE tdvariant,
        language    TYPE sy-langu.

  DATA: ls_stxfadm  TYPE stxfadm,
        ld_errtext  TYPE text100.

  DATA: tabix      LIKE sy-tabix.


* Check SmartForm general data
  SELECT  SINGLE * FROM stxfadm INTO ls_stxfadm
    WHERE    formname = l_repository-name
      AND    formtype = ' '.
  "AND    devclass <> '$TMP'.

  CHECK sy-subrc = 0.

* Create form object
  CREATE OBJECT sform.

  formname = ls_stxfadm-formname.
  language = ls_stxfadm-masterlang.

  CLEAR: fcoding[], gcoding[], gtypes[].

  TRY.
      "Read form definition
      sform->load( im_formname    = formname
                   im_language    = language
                   im_active      = abap_true ).

    CATCH cx_ssf_fb.
      MESSAGE i200(smartforms) WITH formname.
      RETURN.
  ENDTRY.

  "SmartForm Text
  l_repository-text = sform->header-caption.

  "ABAP coding
  fcoding[] = sform->fcoding[].   "Form Routines
  gcoding[] = sform->gcoding[].   "Initialization
  gtypes[]  = sform->gtypes[].    "Types

  " Form Routines
  PERFORM code_process USING fcoding save_tabix.
  " Initialization
  PERFORM code_process USING gcoding save_tabix.
  " Types
  PERFORM code_process USING gtypes save_tabix.


  "Get All nodes (codes)
  PERFORM get_all_nodes USING g_pages
                              g_windows
                              g_codes
                              sform.
  LOOP AT g_codes INTO l_codes.
    node_code = l_codes-obj->code.
    PERFORM nodes_code_process USING node_code save_tabix.
  ENDLOOP.

ENDFORM.                    " GET_SSFO_INFO
*&---------------------------------------------------------------------*
*&      Form  CODE_PROCESS
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM code_process  USING    code         TYPE tfpcoding
                            save_tabix   TYPE sy-tabix.

  DATA: l_fcoding  LIKE LINE OF fcoding.
  DATA: l_1st_char      TYPE c,
        l_sloc TYPE i.

  LOOP AT code INTO l_fcoding .
    l_1st_char = l_fcoding.
    IF    l_fcoding IS INITIAL
       OR l_1st_char = '*'.
      DELETE code INDEX sy-tabix.
    ENDIF.
  ENDLOOP.

  CHECK NOT code[] IS INITIAL.

  l_sloc = lines( code ).
  ADD l_sloc TO l_repository-sloc.

  MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING sloc text.

ENDFORM.                    " CODE_PROCESS
*&---------------------------------------------------------------------*
*&      Form  GET_SFPF_INFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_sfpf_info  TABLES   t_repository LIKE t_repository
                    USING    l_repository TYPE ty_repository
                             save_tabix   TYPE sy-tabix.

  DATA: l_intf_name             TYPE fpname,
        l_form_name             TYPE fpname,
        l_wb_form               TYPE REF TO if_fp_wb_form,
        l_form                  TYPE REF TO if_fp_form,
        l_interface             TYPE REF TO if_fp_interface,
        l_interface_data        TYPE REF TO if_fp_interface_data,
        l_coding                TYPE REF TO if_fp_coding,
        l_init_coding_lines     TYPE tfpcoding,
        l_forms_coding_lines    TYPE tfpcoding.
  DATA: l_rcx_rep               TYPE REF TO cx_fp_api_repository.         "Fix #2 20111027+


  l_form_name = l_repository-name.

  TRY.                                                                    "Fix #2 20111027+
      l_wb_form = cl_fp_wb_helper=>form_load_for_generate( l_form_name ).
      l_form ?= l_wb_form->if_fp_wb_object~get_object( ).

      CALL METHOD cl_fp_wb_helper=>interface_load_for_generate
        EXPORTING
          i_fp_form   = l_form
        IMPORTING
          e_interface = l_interface.
    CATCH cx_fp_api.                                                      "Fix #2 20111027+
  ENDTRY.                                                                 "Fix #2 20111027+

  IF l_interface_data IS BOUND.                                           "Fix #2 20111027+
    l_interface_data      = l_interface->get_interface_data( ).
    l_coding              = l_interface_data->get_coding( ).
    l_init_coding_lines   = l_coding->get_init_coding( ).
    l_forms_coding_lines  = l_coding->get_forms_coding( ).

*     Form Routines
    PERFORM code_process USING l_forms_coding_lines save_tabix.
*     Initialization
    PERFORM code_process USING l_init_coding_lines save_tabix.
  ENDIF.                                                                  "Fix #2 20111027+

ENDFORM.                    " GET_SFPF_INFO
*&---------------------------------------------------------------------*
*&      Form  GET_ALL_NODES
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM get_all_nodes USING  p_pages      LIKE g_pages
                          p_windows    LIKE g_windows
                          p_codes      LIKE g_codes
                          p_form       TYPE      REF TO cl_ssf_fb_smart_form.

  DATA: l_varheader LIKE LINE OF p_form->varheader,
        l_node      TYPE REF TO cl_ssf_fb_node.

  CLEAR: p_pages[],
         p_windows[],
         p_codes[].


  LOOP AT p_form->varheader INTO l_varheader.
    LOOP AT l_varheader-pagetree->succ INTO l_node.
      PERFORM get_node USING  p_pages
                              p_windows
                              p_codes
                              l_node.
    ENDLOOP.
  ENDLOOP.

ENDFORM.                               "GET_ALL_NODES

*&---------------------------------------------------------------------*
*&      Form  get_node
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_PAGES    text
*      -->P_WINDOWS  text
*      -->P_CODES    text
*      -->P_NODE     text
*----------------------------------------------------------------------*
FORM get_node USING  p_pages      TYPE t_tab_page
                     p_windows    TYPE t_tab_window
                     p_codes      TYPE t_tab_code
                     p_node       TYPE      REF TO cl_ssf_fb_node.

  DATA: l_window  TYPE REF TO cl_ssf_fb_window,
        l_succ    TYPE REF TO cl_ssf_fb_node.

  PERFORM incr_g_tables USING  p_pages
                               p_windows
                               p_codes
                               p_node.

* Check the successor
  IF p_node->nodetype = c_node_window.
    l_window ?= p_node->obj.
    PERFORM get_node USING  p_pages
                            p_windows
                            p_codes
                            l_window->proc_ctrl.
  ENDIF.
  LOOP AT p_node->succ INTO l_succ.
    PERFORM get_node USING  p_pages
                            p_windows
                            p_codes
                            l_succ.
  ENDLOOP.

ENDFORM.                               "GET_NODE

*----------------------------------------------------------------------*
*       FORM INCR_G_TABLES
*----------------------------------------------------------------------*
FORM incr_g_tables   USING  p_pages      TYPE t_tab_page
                            p_windows    TYPE t_tab_window
                            p_codes      TYPE t_tab_code
                            p_node       TYPE      REF TO cl_ssf_fb_node.




  DATA: l_page    TYPE REF TO cl_ssf_fb_page.

  CASE p_node->nodetype.
    WHEN c_node_page.
      incr_counter p_pages      p_node->obj l_pages.
      l_page ?= p_node->obj.
    WHEN c_node_window.       incr_counter p_windows    p_node->obj l_windows.
    WHEN c_node_code.         incr_counter p_codes      p_node->obj l_codes.
    WHEN OTHERS.
  ENDCASE.

ENDFORM.                               "INCR_G_TABLES
*&---------------------------------------------------------------------*
*&      Form  CODE_PROCESS
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM nodes_code_process  USING    code         TYPE tsfcode
                                  save_tabix   TYPE sy-tabix.

  DATA: l_1st_char      TYPE c,
        l_sloc TYPE i.

  LOOP AT code INTO l_node_code .
    l_1st_char = l_node_code.
    IF    l_node_code IS INITIAL
       OR l_1st_char = '*'.
      DELETE code INDEX sy-tabix.
    ENDIF.
  ENDLOOP.

  CHECK NOT code[] IS INITIAL.

  l_sloc = lines( code ).
  ADD l_sloc TO l_repository-sloc.

  MODIFY t_repository FROM l_repository INDEX save_tabix TRANSPORTING sloc text.

ENDFORM.                    " nodes_CODE_process

 

Warnings

• If you specify in the selection screen the name of a main program (Report/Module Pool), the SLOC indicator of the related INCLUDE sub-objects sometimes may not be counted, because it should be used "where used" (table D010INC) that, if no refreshed, it doesn't work.
The INCLUDEs in Function Groups instead, with the help of standard functionalities, are well managed.
So, in order to retrieve the total SLOC of a certain main program (with INCLUDEs) specifying the development class is enough (main objects and related sub-object are read from the object directory entry table TADIR), otherwise where used list should be regenerated.
• The selection by author works directly on the object directory entry table (TADIR); for the object type FUGR (function group), the author in TADIR often differs from the author of the last change in the corresponding program (TRDIR)

Updates

2011.01.10 ABAPSloc  CodeExchange project born

2011.01.10 New Version (2.1) of Simple ABAP SLOC Count for SAP ECC 6.0 (SAPLink Slinkee) now available

2011.10.25 Blog post: How many lines of custom ABAP code are inside your system? – Part 3 (GitHub Version 2.2)

2014.11.10 ABAPSloc is now available as Code Inspector check (SCI) and it's part of CloneFinder in SAP Custom Code Applications (tcode /SDF/CD_CCA)