All Downloads are FREE. Search and download functionalities are using the official Maven repository.

progress.pct.compile.p Maven / Gradle / Ivy

There is a newer version: 229
Show newest version
/**
 * Copyright 2005-2019 Riverside Software
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/* Callbacks are only supported on 11.3+ */
 &IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.3 &THEN
 USING Progress.Lang.Class.
 &ENDIF
 USING Progress.Json.ObjectModel.*.

&IF INTEGER(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.'))) GE 11 &THEN
  { pct/v11/xrefd0004.i}
&ELSEIF INTEGER(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.'))) GE 10 &THEN
  { pct/v10/xrefd0003.i}
&ENDIF

DEFINE TEMP-TABLE CRCList NO-UNDO
  FIELD ttTable AS CHARACTER
  FIELD ttCRC   AS CHARACTER
  INDEX ttCRC-PK IS PRIMARY UNIQUE ttTable.
DEFINE TEMP-TABLE TimeStamps NO-UNDO
  FIELD ttFile     AS CHARACTER CASE-SENSITIVE
  FIELD ttFullPath AS CHARACTER CASE-SENSITIVE
  FIELD ttExcept   AS LOGICAL INITIAL FALSE  /* True in case of includes to ignore for recompile */
  FIELD ttMod      AS DATETIME
  INDEX PK-TimeStamps IS PRIMARY UNIQUE ttFile
  INDEX TimeStamps-ttFile ttFile.
DEFINE TEMP-TABLE ttXref NO-UNDO
  FIELD xProcName   AS CHARACTER
  FIELD xFileName   AS CHARACTER
  FIELD xLineNumber AS CHARACTER
  FIELD xRefType    AS CHARACTER
  FIELD xObjID      AS CHARACTER FORMAT "X(50)"
  INDEX typ IS PRIMARY xRefType.
DEFINE TEMP-TABLE ttDirs NO-UNDO
  FIELD baseDir AS CHARACTER
  FIELD dirName AS CHARACTER
  INDEX ttDirs-PK IS PRIMARY baseDir dirName.
DEFINE TEMP-TABLE ttXrefInc NO-UNDO
  FIELD ttIncName AS CHARACTER.
DEFINE TEMP-TABLE ttXrefCRC NO-UNDO
  FIELD ttTblName AS CHARACTER.
DEFINE TEMP-TABLE ttXrefClasses NO-UNDO
  FIELD ttClsName AS CHARACTER.
DEFINE TEMP-TABLE ttWarnings NO-UNDO
  FIELD msgNum   AS INTEGER
  FIELD rowNum   AS INTEGER
  FIELD fileName AS CHARACTER
  FIELD msg      AS CHARACTER.
DEFINE TEMP-TABLE ttErrors NO-UNDO
  FIELD intNum   AS INTEGER
  FIELD fileName AS CHARACTER
  FIELD rowNum   AS INTEGER
  FIELD colNum   AS INTEGER
  FIELD msg      AS CHARACTER
  INDEX ttErrors-PK IS PRIMARY UNIQUE intNum
  INDEX ttErrors-PK2 IS UNIQUE fileName rowNum colNum.

DEFINE TEMP-TABLE ttProjectWarnings NO-UNDO
  FIELD msgNum   AS INTEGER
  FIELD rowNum   AS INTEGER
  FIELD fileName AS CHARACTER
  FIELD mainFileName AS CHARACTER
  FIELD msg      AS CHARACTER.
DEFINE TEMP-TABLE ttProjectErrors NO-UNDO
  FIELD fileName AS CHARACTER
  FIELD mainFileName AS CHARACTER
  FIELD rowNum   AS INTEGER
  FIELD colNum   AS INTEGER
  FIELD msg      AS CHARACTER.

DEFINE SHARED VARIABLE pctVerbose AS LOGICAL NO-UNDO.

FUNCTION getTimeStampDF RETURN DATETIME (INPUT d AS CHARACTER, INPUT f AS CHARACTER) FORWARD.
FUNCTION getTimeStampF RETURN DATETIME (INPUT f AS CHARACTER) FORWARD.
FUNCTION CheckIncludes RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT ts AS DATETIME, INPUT d AS CHARACTER) FORWARD.
FUNCTION CheckCRC RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT d AS CHARACTER) FORWARD.
FUNCTION CheckHierarchy RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT ts AS DATETIME, INPUT d AS CHARACTER) FORWARD.
FUNCTION fileExists RETURNS LOGICAL (INPUT f AS CHARACTER) FORWARD.
FUNCTION createDir RETURNS LOGICAL (INPUT base AS CHARACTER, INPUT d AS CHARACTER) FORWARD.

/** Named streams */
DEFINE STREAM sXref.
DEFINE STREAM sXref2.
DEFINE STREAM sIncludes.
DEFINE STREAM sCRC.
DEFINE STREAM sWarnings.
DEFINE STREAM sHierarchy.

/* PCTCompile attributes */
DEFINE VARIABLE OutputDir AS CHARACTER  NO-UNDO.
DEFINE VARIABLE DestDir AS CHARACTER  NO-UNDO INITIAL ?.
DEFINE VARIABLE PCTDir    AS CHARACTER  NO-UNDO.
DEFINE VARIABLE preprocessDir AS CHARACTER  NO-UNDO.
DEFINE VARIABLE dbgListDir AS CHARACTER NO-UNDO.
DEFINE VARIABLE flattenDbg AS LOGICAL   NO-UNDO.
DEFINE VARIABLE ForceComp AS LOGICAL    NO-UNDO.
DEFINE VARIABLE NoParse   AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE StrXref   AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE AppStrXrf AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE RunList   AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE Lst       AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE LstPrepro AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE PrePro    AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE DebugLst  AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE keepXref  AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE multiComp AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lXmlXref  AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lXCode    AS LOGICAL    NO-UNDO.
DEFINE VARIABLE lRelative AS LOGICAL    NO-UNDO INITIAL FALSE.
DEFINE VARIABLE ProgPerc  AS INTEGER    NO-UNDO INITIAL 0.
DEFINE VARIABLE lOptFullKw AS LOGICAL   NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lOptFldQlf AS LOGICAL   NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lOptFullNames AS LOGICAL NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lOptRetVals AS LOGICAL  NO-UNDO INITIAL FALSE.
DEFINE VARIABLE cOpts     AS CHARACTER NO-UNDO.
DEFINE VARIABLE iLine     AS INTEGER    NO-UNDO.
DEFINE VARIABLE iTotlines AS INTEGER    NO-UNDO.
DEFINE VARIABLE iNrSteps  AS INTEGER    NO-UNDO.
DEFINE VARIABLE iStep     AS INTEGER    NO-UNDO.
DEFINE VARIABLE iStepPerc AS INTEGER    NO-UNDO.
DEFINE VARIABLE cDspSteps AS CHARACTER  NO-UNDO.
DEFINE VARIABLE cIgnoredIncludes AS CHARACTER  NO-UNDO.
DEFINE VARIABLE lIgnoredIncludes AS LOGICAL    NO-UNDO.
DEFINE VARIABLE iFileList AS INTEGER    NO-UNDO.
DEFINE VARIABLE callbackClass AS CHARACTER NO-UNDO.
DEFINE VARIABLE outputType AS CHARACTER NO-UNDO.
DEFINE VARIABLE cLastIncludeName AS CHARACTER NO-UNDO.

DEFINE VARIABLE lOutputJson    AS LOGICAL NO-UNDO INITIAL FALSE.
DEFINE VARIABLE lOutputConsole AS LOGICAL NO-UNDO INITIAL FALSE.

/* Handle to calling procedure in order to log messages */
DEFINE VARIABLE hSrcProc AS HANDLE NO-UNDO.
ASSIGN hSrcProc = SOURCE-PROCEDURE.

DEFINE VARIABLE majorMinor AS DECIMAL NO-UNDO.
DEFINE VARIABLE bAbove101 AS LOGICAL NO-UNDO INITIAL TRUE.
DEFINE VARIABLE bAboveEq113 AS LOGICAL NO-UNDO INITIAL TRUE.
DEFINE VARIABLE bAboveEq117 AS LOGICAL NO-UNDO INITIAL FALSE.
DEFINE VARIABLE bAboveEq1173 AS LOGICAL NO-UNDO INITIAL FALSE.
DEFINE VARIABLE bAboveEq12 AS LOGICAL NO-UNDO INITIAL FALSE.

ASSIGN majorMinor = DECIMAL(REPLACE(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1), '.', SESSION:NUMERIC-DECIMAL-POINT)).
ASSIGN bAbove101 = majorMinor GT 10.1.
ASSIGN bAboveEq113 = (majorMinor GE 11.3).
ASSIGN bAboveEq117 = (majorMinor GE 11.7).
&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11 &THEN
// PROVERSION(1) available since v11
ASSIGN bAboveEq1173 = (majorMinor GT 11.7) OR ((majorMinor EQ 11.7) AND (INTEGER(ENTRY(3, PROVERSION(1), '.')) GE 3)). /* FIXME Check exact version number */
&ENDIF
ASSIGN bAboveEq12 = (majorMinor GE 12).

/* Callbacks are only supported on 11.3+ */
&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.3 &THEN
DEFINE VARIABLE callback AS rssw.pct.ICompileCallback NO-UNDO.
DEFINE VARIABLE compileAction AS INTEGER NO-UNDO.
ASSIGN compileAction = 0.
&ENDIF

PROCEDURE setOption.
  DEFINE INPUT PARAMETER ipName  AS CHARACTER NO-UNDO.
  DEFINE INPUT PARAMETER ipValue AS CHARACTER NO-UNDO.

  CASE ipName:
    when 'OUTPUTDIR':U        THEN ASSIGN DestDir = ipValue.
    WHEN 'PCTDIR':U           THEN ASSIGN PCTDir = ipValue.
    WHEN 'FORCECOMPILE':U     THEN ASSIGN ForceComp = (ipValue EQ '1':U).
    WHEN 'XCODE':U            THEN ASSIGN lXCode = (ipValue EQ '1':U).
    WHEN 'RUNLIST':U          THEN ASSIGN RunList = (ipValue EQ '1':U).
    WHEN 'LISTING':U          THEN ASSIGN Lst = (ipValue EQ '1':U).
    WHEN 'LISTINGSOURCE':U    THEN ASSIGN LstPrepro = (ipValue EQ 'PREPROCESSOR':U).
    WHEN 'PREPROCESS':U       THEN ASSIGN PrePro = (ipValue EQ '1':U).
    WHEN 'PREPROCESSDIR':U    THEN ASSIGN preprocessDir = ipValue.
    WHEN 'DEBUGLISTING':U     THEN ASSIGN DebugLst = (ipValue EQ '1':U).
    WHEN 'DEBUGLISTINGDIR':U  THEN ASSIGN dbgListDir = ipValue.
    WHEN 'FLATTENDBG':U       THEN ASSIGN flattenDbg = (ipValue EQ '1':U).
    WHEN 'STRINGXREF':U       THEN ASSIGN StrXref = (ipValue EQ '1':U).
    WHEN 'APPENDSTRINGXREF':U THEN ASSIGN AppStrXrf = (ipValue EQ '1':U).
    WHEN 'KEEPXREF':U         THEN ASSIGN keepXref = (ipValue EQ '1':U).
    WHEN 'NOPARSE':U          THEN ASSIGN noParse = (ipValue EQ '1':U).
    WHEN 'MULTICOMPILE':U     THEN ASSIGN multiComp = (ipValue EQ '1':U).
    WHEN 'RELATIVE':U         THEN ASSIGN lRelative = (ipValue EQ '1':U).
    WHEN 'PROGPERC':U         THEN ASSIGN ProgPerc = INTEGER(ipValue).
    WHEN 'XMLXREF':U          THEN ASSIGN lXmlXref = (ipValue EQ '1':U).
    WHEN 'IGNOREDINCLUDES':U  THEN ASSIGN cignoredIncludes = REPLACE(TRIM(ipValue), '~\':U, '/':U).
    WHEN 'FULLKW':U           THEN ASSIGN lOptFullKW = (ipValue EQ '1':U).
    WHEN 'FIELDQLF':U         THEN ASSIGN lOptFldQlf = (ipValue EQ '1':U).
    WHEN 'FULLNAMES':U        THEN ASSIGN lOptFullNames = (ipValue EQ '1':U).
    WHEN 'RETURNVALUES':U     THEN ASSIGN lOptRetVals = (ipValue EQ '1':U).
    WHEN 'FILELIST':U         THEN ASSIGN iFileList = INTEGER(ipValue).
    WHEN 'NUMFILES':U         THEN ASSIGN iTotLines = INTEGER(ipValue).
    WHEN 'CALLBACKCLASS':U    THEN ASSIGN callbackClass = ipValue.
    WHEN 'OUTPUTTYPE':U       THEN ASSIGN outputType = ipValue.

    OTHERWISE RUN logError IN hSrcProc (SUBSTITUTE("Unknown parameter '&1' with value '&2'" ,ipName, ipValue)).
  END CASE.

END PROCEDURE.

PROCEDURE initModule:
  ASSIGN lIgnoredIncludes = (LENGTH(cignoredIncludes) > 0).

  IF (callbackClass > "") AND NOT bAboveEq113 THEN
    MESSAGE "Callbacks are only supported on 11.3+".
  /* Callbacks are only supported on 11.3+ */
&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.3 &THEN
  IF (callbackClass > "") THEN DO:
      callback = CAST(Class:GetClass(callbackClass):new(), rssw.pct.ICompileCallback).
      callback:initialize(hSrcProc).
  END.
&ENDIF
  IF (outputType > "") THEN DO:
    IF (LOOKUP("json", outputType) GT 0) THEN DO:
      IF bAboveEq117 THEN DO:
        lOutputJson = TRUE.
      END.
      ELSE DO:
        MESSAGE "JSON outputType is only supported on 11.7+".
      END.
    END.
    IF (LOOKUP("console", outputType) GT 0) THEN DO:
      lOutputConsole = TRUE.
    END.
  END.

  /* Gets CRC list */
  DEFINE VARIABLE h AS HANDLE NO-UNDO.
  h = TEMP-TABLE CRCList:HANDLE.
  RUN pct/pctCRC.p (INPUT-OUTPUT TABLE-HANDLE h) NO-ERROR.
  IF (RETURN-VALUE NE '0') THEN
    RETURN RETURN-VALUE.

  /* Checks if valid config */
  OutputDir = if DestDir ne ? then DestDir else ".".
  IF NOT FileExists(OutputDir) THEN
    RETURN '4'.
  IF NOT FileExists(PCTDir) THEN
    ASSIGN PCTDir = OutputDir + '/.pct':U.
  IF debugLst AND (dbgListDir EQ '') THEN DO:
    ASSIGN dbgListDir = OutputDir + '/.dbg':U.
    createDir(outputDir, '.dbg':U).
  END.
  COMPILER:MULTI-COMPILE = multiComp.
  IF lOptFullKw THEN
    ASSIGN cOpts = 'require-full-keywords' + (IF bAboveEq1173 THEN ':warning' ELSE '').
  IF lOptFldQlf THEN
    ASSIGN cOpts = cOpts + (IF cOpts EQ '' THEN '' ELSE ',') + 'require-field-qualifiers' + (IF bAboveEq1173 THEN ':warning' ELSE '').
  IF lOptFullNames THEN
    ASSIGN cOpts = cOpts + (IF cOpts EQ '' THEN '' ELSE ',') + 'require-full-names' + (IF bAboveEq1173 THEN ':warning' ELSE '').
  IF lOptRetVals THEN
    ASSIGN cOpts = cOpts + (IF cOpts EQ '' THEN '' ELSE ',') + 'require-return-values' + (IF bAboveEq1173 THEN ':warning' ELSE '').
  IF (COMPILER:OPTIONS GT "":U) THEN DO:
    MESSAGE "PCT compiler options are overridden by COMPILER:OPTIONS".
    ASSIGN cOpts = COMPILER:OPTIONS.
  END.

  IF ProgPerc GT 0 THEN DO:
    ASSIGN iNrSteps = 100 / ProgPerc.
    IF iNrSteps GT iTotLines THEN DO:
      ASSIGN iNrSteps = iTotLines
             ProgPerc = 100 / iNrSteps.
      RUN logVerbose IN hSrcProc ("WARNING: Less files then percentage steps. Automatically increasing percentage to " + TRIM(STRING(ProgPerc, ">>9%":U))).
    END.
    DO iStep = 1 TO iNrSteps:
      ASSIGN cDspSteps = cDspSteps + (IF cDspSteps NE "" THEN "," ELSE "") + STRING(MIN(INTEGER((iTotLines / 100) * (ProgPerc * iStep)), iTotLines)).
    END.
  END.

END PROCEDURE.

FUNCTION getRecompileLabel RETURNS CHARACTER (ipVal AS INTEGER):
  CASE ipVal:
    WHEN 0 THEN RETURN 'Up to date'.
    WHEN 1 THEN RETURN 'No r-code'.
    WHEN 2 THEN RETURN 'R-code older than source'.
    WHEN 3 THEN RETURN 'R-code older than include file'.
    WHEN 4 THEN RETURN 'Table CRC'.
    WHEN 5 THEN RETURN 'XCode or force'.
    WHEN 6 THEN RETURN 'Hierarchy change'.
    OTHERWISE   RETURN '???'.
  END.
END FUNCTION.

PROCEDURE compileXref.
  DEFINE INPUT  PARAMETER ipInDir   AS CHARACTER  NO-UNDO. /* Fileset. Never null */
  DEFINE INPUT  PARAMETER ipInFile  AS CHARACTER  NO-UNDO. /* Path relative to fileset. Never null */
  DEFINE INPUT  PARAMETER ipOutFile AS CHARACTER  NO-UNDO. /* Path relative to pcOutDir. Can be null, in this case, the default rcode name */
  DEFINE OUTPUT PARAMETER opError   AS LOGICAL    NO-UNDO INITIAL FALSE.
  DEFINE OUTPUT PARAMETER opComp    AS INTEGER    NO-UNDO. /* 0 -> Not compiled, >0  recompiled */

  DEFINE VARIABLE i        AS INTEGER    NO-UNDO.
  DEFINE VARIABLE cBase    AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cBase2    AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cFile    AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cFile2    AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cFileExt AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cSaveDir AS CHARACTER NO-UNDO.
  DEFINE VARIABLE cXrefFile AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cStrXrefFile AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE preprocessFile AS CHARACTER NO-UNDO.
  DEFINE VARIABLE debugListingFile AS CHARACTER NO-UNDO.
  DEFINE VARIABLE warningsFile AS CHARACTER NO-UNDO.
  DEFINE VARIABLE RCodeTS   AS DATETIME   NO-UNDO.
  DEFINE VARIABLE ProcTS    AS DATETIME   NO-UNDO.
  DEFINE VARIABLE cRenameFrom AS CHARACTER NO-UNDO INITIAL ''.
  DEFINE VARIABLE lWarnings AS LOGICAL NO-UNDO INITIAL FALSE.
  DEFINE VARIABLE lOneWarning AS LOGICAL NO-UNDO INITIAL FALSE.

  EMPTY TEMP-TABLE ttWarnings. /* Emptying the temp-table to store warnings for current file*/
  /* Output progress */
  IF ProgPerc GT 0 THEN DO:
    ASSIGN iLine = iLine + 1.
    IF LOOKUP(STRING(iLine), cDspSteps) GT 0 THEN DO:
      ASSIGN iStepPerc = LOOKUP(STRING(iLine), cDspSteps) * ProgPerc.
      IF iStepPerc LT 100 THEN
        RUN logInfo IN hSrcProc (SUBSTITUTE("&1 &2 Compiling &3...", TRIM(STRING(iStepPerc, ">>9%":U)), STRING(TIME, "HH:MM:SS":U), ipInFile)).
      ELSE
        RUN logInfo IN hSrcProc ("100% " + STRING(TIME,"HH:MM:SS":U)).
    END.
    IF (iLine GE iTotLines) AND (iStepPerc LT 100) THEN DO:
      ASSIGN iStepPerc = 100.
      RUN logInfo IN hSrcProc ("100% " + STRING(TIME,"HH:MM:SS":U)).
    END.
  END.

  IF (NOT fileExists(IF lRelative THEN ipInFile ELSE ipInDir + '/':U + ipInFile)) THEN DO:
    RUN logError IN hSrcProc (SUBSTITUTE("File [&1]/[&2] not found", ipInDir, ipInFile)).
    ASSIGN opError = TRUE.
    ASSIGN opComp = 0.
    RETURN.
  END.

  RUN adecomm/_osprefx.p(INPUT ipInFile, OUTPUT cBase, OUTPUT cFile).
  RUN adecomm/_osfext.p(INPUT cFile, OUTPUT cFileExt).
  ASSIGN opError = NOT createDir(outputDir, cBase).
  IF (opError) THEN RETURN.
  ASSIGN opError = NOT createDir(PCTDir, cBase).
  IF (opError) THEN RETURN.
  ASSIGN cSaveDir = (IF DestDir EQ ?
                       THEN ?
                       ELSE (IF cFileExt = ".cls":U OR lRelative
                               THEN outputDir
                               ELSE outputDir + '/':U + cBase)).

  IF (ipOutFile EQ ?) OR (ipOutFile EQ '') THEN DO:
    ASSIGN ipOutFile = SUBSTRING(ipInFile, 1, R-INDEX(ipInFile, cFileExt) - 1) + '.r':U.
  END.
  ELSE DO:
    RUN adecomm/_osprefx.p(INPUT ipOutFile, OUTPUT cBase2, OUTPUT cFile2).
    ASSIGN opError = NOT createDir(outputDir, cBase2).
    IF (opError) THEN RETURN.
    ASSIGN opError = NOT createDir(PCTDir, cBase2).
    IF (opError) THEN RETURN.
    ASSIGN cRenameFrom = cBase + (IF cbase EQ '' THEN '' ELSE '/') + substring(cfile, 1, R-INDEX(cfile, '.') - 1) + '.r'.
  END.

  IF (noParse OR ForceComp OR lXCode) THEN DO:
    ASSIGN opComp = 5.
  END.
  ELSE DO:
    /* Does .r file exists ?,
       if DestDir = unknown rcode will be located in the same directory as the source : ipInDir */
    ASSIGN RCodeTS = getTimeStampDF(if DestDir = ? then ipInDir else OutputDir, ipOutFile).
    IF (RCodeTS EQ ?) THEN DO:
      opComp = 1.
    END.
    ELSE DO:
      /* Checking if .r timestamp is prior to procedure timestamp */
      ASSIGN ProcTS = getTimeStampDF(ipInDir, ipInFile).
      IF (ProcTS GT RCodeTS) THEN DO:
        opComp = 2.
      END.
      ELSE DO:
        IF CheckIncludes(ipInFile, RCodeTS, PCTDir) THEN DO:
          opComp = 3.
        END.
        ELSE DO:
          IF CheckCRC(ipInFile, PCTDir) THEN DO:
            opComp = 4.
          END.
          ELSE DO:
            IF CheckHierarchy(ipInFile, RCodeTS, PCTDir) THEN DO:
              opComp = 6.
            END.
          END.
        END.
      END.
    END.
  END.
  IF (iFileList GT 0) THEN DO:
    IF ((iFileList EQ 1) AND (opComp GT 0) ) OR (iFileList EQ 2) THEN DO:
      RUN logInfo IN hSrcProc (SUBSTITUTE("&1 [&2&3]", ipInFile, getRecompileLabel(opComp), IF opComp = 3 THEN ": " + cLastIncludeName ELSE "")).
    END.
  END.
  IF opComp EQ 0 THEN RETURN.

  ASSIGN cXrefFile = PCTDir + '/':U + ipInFile + '.xref':U.
  ASSIGN warningsFile = PCTDir + '/':U + ipInFile + '.warnings':U.
  ASSIGN cStrXrefFile = (IF StrXref AND AppStrXrf
                           THEN PCTDir + '/strings.xref':U
                           ELSE (IF StrXref
                                 THEN PCTDir + '/':U + ipInFile + '.strxref'
                                 ELSE ?)).

  IF PrePro THEN DO:
    IF preprocessDir = '' THEN
      ASSIGN preprocessFile = PCTDir + '/':U + ipInFile + '.preprocess':U.
    ELSE DO:
      ASSIGN preprocessFile = preprocessDir + '/':U + ipInFile.
      ASSIGN opError = NOT createDir(preprocessDir, cBase).
      IF (opError) THEN RETURN.
    END.
  END.
  ELSE
    ASSIGN preprocessFile = ?.

  IF debugLst AND NOT (cFile BEGINS '_') THEN DO:
    IF flattenDbg THEN
      ASSIGN debugListingFile = dbgListDir + '/' + REPLACE(REPLACE(ipInFile, '/', '_'), '~\', '_').
    ELSE DO:
      ASSIGN debugListingFile = ipInFile.
      ASSIGN debugListingFile = dbgListDir + '/' + debugListingFile.
      ASSIGN opError = NOT createDir(dbgListDir, cBase).
      IF (opError) THEN RETURN.
    END.
  END.
  ELSE
    ASSIGN debugListingFile = ?.

  RUN logVerbose IN hSrcProc (SUBSTITUTE("Compiling &1 in directory &2 TO &3", ipInFile, ipInDir, cSaveDir)).

&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.3 &THEN
  IF VALID-OBJECT(callback) THEN DO:
    compileAction = callback:beforeCompile(hSrcProc, ipInFile, ipInDir).
    IF (compileAction EQ 1) THEN
      RETURN.
  END.
&ENDIF

/* Before 11.7.3, strict mode compiler was throwing errors. 11.7.3 introduced :warning and :error */
&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.7 &THEN
  IF (cOpts GT "") AND bAboveEq117 AND (NOT bAboveEq1173) THEN DO:
    COMPILE VALUE(IF lRelative THEN ipInFile ELSE ipInDir + '/':U + ipInFile) SAVE=FALSE OPTIONS cOpts NO-ERROR.
    IF COMPILER:ERROR THEN DO i = 1 TO COMPILER:NUM-MESSAGES:
      /* Messages 14786, 14789, 18494 are the only relevant ones */
      IF (COMPILER:GET-NUMBER(i) EQ 14786) OR (COMPILER:GET-NUMBER(i) EQ 14789) OR (COMPILER:GET-NUMBER(i) EQ 18494) THEN DO:
        CREATE ttWarnings.
        ASSIGN ttWarnings.msgNum   = COMPILER:GET-NUMBER(i)
               ttWarnings.rowNum   = COMPILER:GET-ROW(i)
               ttWarnings.fileName = COMPILER:GET-FILE-NAME(i)
               ttWarnings.msg      = COMPILER:GET-MESSAGE(i)
               lWarnings           = TRUE.
      END.
    END.
  END.
&ENDIF

  RUN pctcomp.p (IF lRelative THEN ipInFile ELSE ipInDir + '/':U + ipInFile,
                 cSaveDir, debugListingFile,
                 IF Lst AND NOT LstPrepro THEN PCTDir + '/':U + ipInFile ELSE ?,
                 preprocessFile, cStrXrefFile, cXrefFile, IF bAboveEq1173 THEN cOpts ELSE "").

&IF DECIMAL(SUBSTRING(PROVERSION, 1, INDEX(PROVERSION, '.') + 1)) GE 11.3 &THEN
  IF VALID-OBJECT(callback) THEN callback:afterCompile(hSrcProc, ipInFile, ipInDir).
&ENDIF

  ASSIGN opError = COMPILER:ERROR.
  IF NOT opError THEN DO:
    /* In order to handle  element */
    IF ((cRenameFrom NE '') AND (cRenameFrom NE ipOutFile)) THEN DO:
      RUN logVerbose IN hSrcProc (SUBSTITUTE("Mapper: renaming &1/&2 to &1/&3", outputDir, cRenameFrom, ipOutFile)).
      OS-COPY VALUE(outputDir + '/' + cRenameFrom) VALUE(outputDir + '/' + ipOutFile).
      OS-DELETE VALUE(outputDir + '/' + cRenameFrom).
    END.
    IF (NOT noParse) AND (NOT lXCode) THEN DO:
      IF lXmlXref THEN
        RUN ImportXmlXref (INPUT cXrefFile, INPUT PCTDir, INPUT ipInFile) NO-ERROR.
      ELSE
        RUN ImportXref (INPUT cXrefFile, INPUT PCTDir, INPUT ipInFile) NO-ERROR.
    END.
    IF COMPILER:WARNING OR lWarnings THEN DO:
      DO i = 1 TO COMPILER:NUM-MESSAGES:
        IF bAbove101 THEN DO:
          /* Pointless message coming from strict mode compiler */
          IF COMPILER:GET-NUMBER(i) EQ 2411 THEN NEXT.
          /* Messages 2363, 3619 and 3623 are in fact warnings (from -checkdbe switch) */
          IF (COMPILER:GET-MESSAGE-TYPE(i) EQ 2) OR (COMPILER:GET-NUMBER(i) EQ 2363) OR (COMPILER:GET-NUMBER(i) EQ 3619) OR (COMPILER:GET-NUMBER(i) EQ 3623) THEN DO:
            IF (LOOKUP(STRING(COMPILER:GET-NUMBER(i)), SESSION:SUPPRESS-WARNINGS-LIST) EQ 0) THEN DO:
              CREATE ttWarnings.
              ASSIGN ttWarnings.msgNum   = COMPILER:GET-NUMBER(i)
                     ttWarnings.rowNum   = COMPILER:GET-ROW(i)
                     ttWarnings.fileName = COMPILER:GET-FILE-NAME(i)
                     ttWarnings.msg      = REPLACE(COMPILER:GET-MESSAGE(i), '~n', ' ').
            END.
          END.
        END.
      END.

      IF lOutputJson THEN DO:
        FOR EACH ttWarnings:
          CREATE ttProjectWarnings.
          ASSIGN ttProjectWarnings.msgNum       = ttWarnings.msgNum
                 ttProjectWarnings.rowNum       = ttWarnings.rowNum
                 ttProjectWarnings.fileName     = REPLACE(ttWarnings.fileName, chr(92), '/')
                 ttProjectWarnings.msg          = ttWarnings.msg
                 ttProjectWarnings.mainFileName = REPLACE(ipInDir + (if ipInDir eq '':U then '':U else '/':U) + ipInFile, chr(92), '/').
        END.
      END.
      IF lOutputConsole THEN DO:
        OUTPUT STREAM sWarnings TO VALUE(warningsFile).
        FOR EACH ttWarnings:
          PUT STREAM sWarnings UNFORMATTED SUBSTITUTE("[&1] [&3] &2", ttWarnings.rowNum, ttWarnings.msg, ttWarnings.fileName) SKIP.
          ASSIGN lOneWarning = TRUE.
        END.
        OUTPUT STREAM sWarnings CLOSE.
      END.
    END.
    IF NOT lOneWarning THEN DO:
      OS-DELETE VALUE(warningsFile).
    END.
  END.
  ELSE DO:
    EMPTY TEMP-TABLE ttErrors.
    DO i = 1 TO COMPILER:NUM-MESSAGES:
      IF COMPILER:GET-NUMBER(i) EQ 198 THEN NEXT.
      FIND ttErrors WHERE ttErrors.fileName EQ COMPILER:GET-FILE-NAME(i)
                      AND ttErrors.rowNum   EQ (IF bAbove101 THEN COMPILER:GET-ROW(i) ELSE COMPILER:GET-ERROR-ROW(i))
                      AND ttErrors.colNum   EQ (IF bAbove101 THEN COMPILER:GET-COLUMN(i) ELSE COMPILER:GET-ERROR-COLUMN(i))
                    NO-ERROR.
      IF NOT AVAILABLE ttErrors THEN DO:
        CREATE ttErrors.
        ASSIGN ttErrors.intNum   = i
               ttErrors.fileName = COMPILER:GET-FILE-NAME(i)
               ttErrors.rowNum   = (IF bAbove101 THEN COMPILER:GET-ROW(i) ELSE COMPILER:GET-ERROR-ROW(i))
               ttErrors.colNum   = (IF bAbove101 THEN COMPILER:GET-COLUMN(i) ELSE COMPILER:GET-ERROR-COLUMN(i)).
      END.
      ASSIGN ttErrors.msg = ttErrors.msg + (IF ttErrors.msg EQ '' THEN '' ELSE '~n') + COMPILER:GET-MESSAGE(i).
    END.

    IF lOutputJson THEN DO:
      FOR EACH ttErrors:
        CREATE ttProjectErrors.
        ASSIGN ttProjectErrors.fileName      = REPLACE(ttErrors.fileName, chr(92), '/')
               ttProjectErrors.mainFileName  = REPLACE(ipInDir + (if ipInDir eq '':U then '':U else '/':U) + ipInFile, chr(92), '/')
               ttProjectErrors.rowNum        = ttErrors.rowNum
               ttProjectErrors.colNum        = ttErrors.colNum
               ttProjectErrors.msg           = ttErrors.msg.
      END.
    END.
    IF lOutputConsole THEN DO:
      RUN logError IN hSrcProc (SUBSTITUTE("Error compiling file '&1' ...", REPLACE(ipInDir + (IF ipInDir EQ '':U THEN '':U ELSE '/':U) + ipInFile, CHR(92), '/':U))).
      FOR EACH ttErrors:
        RUN displayCompileErrors(ipInDir + (IF ipInDir EQ '':U THEN '':U ELSE '/':U) + ipInFile, ttErrors.fileName, ttErrors.rowNum, ttErrors.colNum, ttErrors.msg).
      END.
    END.
  END.
  IF NOT keepXref THEN
    OS-DELETE VALUE(cXrefFile).

  IF (NOT opError AND lst AND lstPrepro AND (preprocessFile NE ?)) THEN DO:
    COMPILE VALUE(preprocessFile) SAVE=NO LISTING VALUE(PCTDir + '/':U + ipInFile) NO-ERROR.
    IF ERROR-STATUS:ERROR THEN DO:
      OS-DELETE VALUE(PCTDir + '/':U + ipInFile).
    END.
  END.

END PROCEDURE.

PROCEDURE printErrorsWarningsJson.

  DEFINE INPUT PARAMETER iCompOK AS INTEGER NO-UNDO.
  DEFINE INPUT PARAMETER iCompFail AS INTEGER NO-UNDO.

  DEFINE VARIABLE dsJsonObj AS JsonObject NO-UNDO.
  DEFINE VARIABLE ttErr AS JsonArray NO-UNDO.
  DEFINE VARIABLE ttWarn AS JsonArray NO-UNDO.
  DEFINE VARIABLE outFile AS CHARACTER NO-UNDO.

  IF lOutputJson THEN DO:
    dsJsonObj = NEW JsonObject().
    dsJsonObj:Add("compiledFiles", iCompOK).
    dsJsonObj:Add("errorFiles", iCompFail).

    ttErr = NEW JsonArray().
    ttWarn = NEW JsonArray().
    TEMP-TABLE ttProjectErrors:HANDLE:WRITE-JSON("JsonArray", ttErr).
    TEMP-TABLE ttProjectWarnings:HANDLE:WRITE-JSON("JsonArray", ttWarn).
    IF (ttErr:Length GT 0) THEN
        dsJsonObj:ADD("errors", ttErr).
    IF (ttWarn:Length GT 0) THEN
        dsJsonObj:ADD("warnings", ttWarn).

    ASSIGN outFile = PCTDir + '/':U + 'project-result.json':U.
    dsJsonObj:WriteFile(outFile).
  END.

END PROCEDURE.

PROCEDURE displayCompileErrors PRIVATE:
  DEFINE INPUT  PARAMETER pcInit    AS CHARACTER  NO-UNDO.
  DEFINE INPUT  PARAMETER pcFile    AS CHARACTER  NO-UNDO.
  DEFINE INPUT  PARAMETER piRow     AS INTEGER    NO-UNDO.
  DEFINE INPUT  PARAMETER piColumn  AS INTEGER    NO-UNDO.
  DEFINE INPUT  PARAMETER pcMsg     AS CHARACTER  NO-UNDO.

  DEFINE VARIABLE i       AS INTEGER    NO-UNDO .
  DEFINE VARIABLE c       AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE bit     AS INTEGER    NO-UNDO.
  DEFINE VARIABLE memvar  AS MEMPTR     NO-UNDO.
  DEFINE VARIABLE include AS LOGICAL    NO-UNDO.

  ASSIGN include = REPLACE(pcInit, CHR(92), '/') NE REPLACE(pcFile, CHR(92), '/').

  /* Checking if file is xcoded */
  COPY-LOB FROM FILE pcFile FOR 1 TO memvar.
  bit = GET-BYTE (memvar, 1).
  SET-SIZE(memvar) = 0.

  IF (include) THEN
    RUN logError IN hSrcProc (SUBSTITUTE(" ... in file '&1' at line &2 column &3", REPLACE(pcFile, CHR(92), '/'), piRow, piColumn)).
  ELSE
    RUN logError IN hSrcProc (SUBSTITUTE(" ... in main file at line &2 column &3", pcInit, piRow, piColumn, pcFile)).

  IF (bit NE 17) AND (bit NE 19) THEN DO:
    INPUT STREAM sXref FROM VALUE(pcFile).
    DO i = 1 TO piRow - 1:
      IMPORT STREAM sXref UNFORMATTED ^.
    END.
    IMPORT STREAM sXref UNFORMATTED c.
    RUN logError IN hSrcProc (INPUT ' ' + c).
    RUN logError IN hSrcProc (INPUT FILL('-':U, piColumn - 1) + '-^').
    RUN logError IN hSrcProc (INPUT pcMsg).
    RUN logError IN hSrcProc (INPUT '').

    INPUT STREAM sXref CLOSE.
  END.
  ELSE DO:
    RUN logError IN hSrcProc (INPUT pcMsg).
    RUN logError IN hSrcProc (INPUT ">> Can't display xcoded source").
    RUN logError IN hSrcProc (INPUT '').
  END.

END PROCEDURE.

PROCEDURE importXmlXref.
  DEFINE INPUT  PARAMETER pcXref AS CHARACTER NO-UNDO.
  DEFINE INPUT  PARAMETER pcDir  AS CHARACTER NO-UNDO.
  DEFINE INPUT  PARAMETER pcFile AS CHARACTER NO-UNDO.

  DEFINE VARIABLE zz        AS INTEGER   NO-UNDO.
  DEFINE VARIABLE currCls   AS CHARACTER NO-UNDO.

  EMPTY TEMP-TABLE ttXrefInc.
  EMPTY TEMP-TABLE ttXrefCRC.
  EMPTY TEMP-TABLE ttXrefClasses.

  DATASET Cross-reference:READ-XML("FILE", pcXref, "EMPTY", ?, ?).

  FIND FIRST Reference WHERE Reference.Reference-Type EQ 'CLASS' NO-ERROR.
  IF (AVAILABLE Reference) THEN DO:
    ASSIGN currCls = Reference.Object-identifier.
    FOR EACH Class-Ref WHERE Class-Ref.Ref-seq = Reference.Ref-seq AND Class-Ref.Source-guid = Reference.Source-guid:
      DO zz = 1 TO NUM-ENTRIES(Class-Ref.Inherited-list, ' '):
        CREATE ttXrefClasses.
        ASSIGN ttXrefClasses.ttClsName = ENTRY(zz, Class-Ref.Inherited-list, ' ').
      END.
      DO zz = 1 TO NUM-ENTRIES(Class-Ref.Implements-list, ' '):
        CREATE ttXrefClasses.
        ASSIGN ttXrefClasses.ttClsName = ENTRY(zz, Class-Ref.Implements-list, ' ').
      END.
    END.
  END.
  FOR EACH Reference WHERE LOOKUP(Reference.Reference-Type, 'INCLUDE,CREATE,REFERENCE,ACCESS,UPDATE,SEARCH,INVOKE,NEW':U) NE 0:
    ASSIGN Reference.Object-identifier = TRIM(Reference.Object-identifier).
    IF Reference.Reference-Type EQ 'INCLUDE' THEN DO:
      /* Extract include file name from field (which contains include parameters */
      CREATE ttXrefInc.
      ASSIGN ttXrefInc.ttIncName = TRIM(SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ' ') - 1)).
    END.
    ELSE IF Reference.Reference-Type EQ 'INVOKE':U THEN DO:
      IF (NOT Reference.Object-identifier BEGINS (currCls + ":")) THEN DO:
        FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ':') - 1) NO-ERROR.
        IF (NOT AVAILABLE ttXrefClasses) THEN DO:
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ':') - 1).
        END.
      END.
    END.
    ELSE IF Reference.Reference-Type EQ 'NEW':U THEN DO:
      IF (NOT Reference.Object-identifier EQ currCls) THEN DO:
        FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ Reference.Object-identifier NO-ERROR.
        IF (NOT AVAILABLE ttXrefClasses) THEN DO:
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = Reference.Object-identifier.
        END.
      END.
    END.
    ELSE DO:
      IF (Reference.Object-Context = 'INHERITED-DATA-MEMBER' OR Reference.Object-Context = 'INHERITED-PROPERTY'
          OR Reference.Object-Context = 'PUBLIC-DATA-MEMBER' OR Reference.Object-Context = 'PUBLIC-PROPERTY')
         AND ( NOT Reference.Object-identifier BEGINS (currCls + ":")) THEN DO:
        FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ':') - 1) NO-ERROR.
        IF (NOT AVAILABLE ttXrefClasses) THEN DO:
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ':') - 1).
        END.
      END.
      ELSE DO:
        /* Find CRC of each table */
        CREATE ttXrefCRC.
        IF (INDEX(Reference.Object-identifier, ' ') GT 0) THEN
          ASSIGN ttXrefCRC.ttTblName = SUBSTRING(Reference.Object-identifier, 1, INDEX(Reference.Object-identifier, ' ') - 1).
        ELSE
          ASSIGN ttXrefCRC.ttTblName = Reference.Object-identifier.
      END.
    END.
  END.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.inc':U).
  FOR EACH ttXrefInc BREAK BY ttXrefInc.ttIncName:
    IF FIRST-OF(ttXrefInc.ttIncName) THEN
      EXPORT ttXrefInc.ttIncName SEARCH(ttXrefInc.ttIncName).
  END.
  OUTPUT CLOSE.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.crc':U).
  FOR EACH ttXrefCRC BREAK BY ttXrefCRC.ttTblName:
    IF FIRST-OF(ttXrefCRC.ttTblName) THEN DO:
      FIND CRCList WHERE CRCList.ttTable EQ ttXrefCRC.ttTblName NO-LOCK NO-ERROR.
      IF (AVAILABLE CRCList) THEN DO:
        EXPORT CRCList.
      END.
    END.
  END.
  OUTPUT CLOSE.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.hierarchy':U).
  FOR EACH ttXrefClasses NO-LOCK:
    IF SEARCH(REPLACE(ttXrefClasses.ttClsName, '.', '/') + '.cls') GT '' THEN
      EXPORT ttXrefClasses.ttClsName SEARCH(REPLACE(ttXrefClasses.ttClsName, '.', '/') + '.cls').
  END.
  OUTPUT CLOSE.

END PROCEDURE.

PROCEDURE importXref PRIVATE.
  DEFINE INPUT  PARAMETER pcXref AS CHARACTER NO-UNDO.
  DEFINE INPUT  PARAMETER pcDir  AS CHARACTER NO-UNDO.
  DEFINE INPUT  PARAMETER pcFile AS CHARACTER NO-UNDO.

  DEFINE VARIABLE cSearch AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cTmp    AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cTmp2   AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cTmp3   AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE zz      AS INTEGER    NO-UNDO.
  DEFINE VARIABLE currCls   AS CHARACTER NO-UNDO.

  EMPTY TEMP-TABLE ttXref.
  EMPTY TEMP-TABLE ttXrefInc.
  EMPTY TEMP-TABLE ttXrefCRC.
  EMPTY TEMP-TABLE ttXrefClasses.

  INPUT STREAM sXREF FROM VALUE (pcXref).
  INPUT STREAM sXREF2 FROM VALUE (pcXref).
  REPEAT:
    CREATE ttXref.
    IMPORT STREAM sXREF ttXref.

    /* Import full line in order to reposition the first stream if line is longer than 2000 characters */
    IMPORT STREAM sXREF2 UNFORMATTED cTmp.
    SEEK STREAM sXREF TO SEEK(sXREF2).
    /* Read content of xObjID field from full line */
    ASSIGN cTmp2 = ttXref.xLineNumber + ' ' + ttXref.xRefType + ' '.
    ASSIGN ttXref.xObjID = SUBSTRING(cTmp, INDEX(cTmp, cTmp2) + LENGTH(cTmp2)).

    /* Remove surrounding quotes */
    IF (SUBSTRING(ttXref.xObjId, 1, 1) EQ '"') AND (SUBSTRING(ttXref.xObjId, LENGTH(ttXref.xObjId), 1) EQ '"') THEN
      ASSIGN ttXref.xObjId = SUBSTRING(ttXref.xObjId, 2, LENGTH(ttXref.xObjId) - 2).

    IF (ttXref.xRefType EQ 'INCLUDE':U) OR (RunList AND (ttXref.xRefType EQ 'RUN':U)) THEN
      ttXref.xObjID = ENTRY(1, TRIM(ttXref.xObjID), ' ':U).
    ELSE IF (LOOKUP(ttXref.xRefType, 'CREATE,REFERENCE,ACCESS,UPDATE,SEARCH':U) NE 0) THEN DO:
      IF (ttXref.xObjID BEGINS 'INHERITED-DATA-MEMBER ' OR ttXref.xObjID BEGINS 'INHERITED-PROPERTY '
          OR ttXref.xObjID BEGINS 'PUBLIC-DATA-MEMBER ' OR ttXref.xObjID BEGINS 'PUBLIC-PROPERTY ') THEN DO:
         IF (NOT ENTRY(2, ttXref.xObjID, ' ') BEGINS (currCls + ":")) THEN DO:
           FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ SUBSTRING(ENTRY(2, ttXref.xObjID, ' '), 1, INDEX(ENTRY(2, ttXref.xObjID, ' '), ':') - 1) NO-ERROR.
           IF (NOT AVAILABLE ttXrefClasses) THEN DO:
             CREATE ttXrefClasses.
             ASSIGN ttXrefClasses.ttClsName = SUBSTRING(ENTRY(2, ttXref.xObjID, ' '), 1, INDEX(ENTRY(2, ttXref.xObjID, ' '), ':') - 1).
           END.
         END.
      END.
      ELSE DO:
        /* xObjID may contain DB.Table followed by IndexName or FieldName. We extract table name */
        IF (INDEX(ttXref.xObjID, ' ') GT 0) THEN
          ASSIGN ttXref.xObjID = SUBSTRING(ttXref.xObjID, 1, INDEX(ttXref.xObjID, ' ') - 1).
      END.
    END.
    ELSE IF (ttXref.xRefType EQ 'INVOKE':U) THEN DO:
      ASSIGN cTmp3 = ENTRY(1, ttXref.xObjID, ',').
      IF (NOT cTmp3 BEGINS (currCls + ":")) THEN DO:
        FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ SUBSTRING(cTmp3, 1, INDEX(cTmp3, ':') - 1) NO-ERROR.
        IF (NOT AVAILABLE ttXrefClasses) THEN DO:
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = SUBSTRING(cTmp3, 1, INDEX(cTmp3, ':') - 1).
        END.
      END.
    END.
    ELSE IF (ttXref.xRefType EQ 'NEW':U) THEN DO:
      ASSIGN cTmp3 = ENTRY(1, ttXref.xObjID, ',').
      IF (NOT cTmp3 EQ currCls) THEN DO:
        FIND ttXrefClasses WHERE ttXrefClasses.ttClsName EQ cTmp3 NO-ERROR.
        IF (NOT AVAILABLE ttXrefClasses) THEN DO:
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = cTmp3.
        END.
      END.
    END.
    ELSE IF (ttXref.xRefType EQ 'CLASS':U) THEN DO:
      ASSIGN currCls = ENTRY(1, ttXref.xObjID).
      ASSIGN cTmp = ENTRY(2, ttXref.xObjID).
      IF cTmp BEGINS 'INHERITS ' THEN DO:
        ASSIGN cTmp = SUBSTRING(cTmp, 10). /* To remove INHERITS */
        DO zz = 1 TO NUM-ENTRIES(cTmp, ' '):
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = ENTRY(zz, cTmp, ' ').
        END.
      END.
      ASSIGN cTmp = ENTRY(3, ttXref.xObjID).
      IF cTmp BEGINS 'IMPLEMENTS ' THEN DO:
        ASSIGN cTmp = SUBSTRING(cTmp, 12). /* To remove IMPLEMENTS */
        DO zz = 1 TO NUM-ENTRIES(cTmp, ' '):
          CREATE ttXrefClasses.
          ASSIGN ttXrefClasses.ttClsName = ENTRY(zz, cTmp, ' ').
        END.
      END.
    END.
  END.
  DELETE ttXref. /* ttXref is non-undo'able */
  INPUT STREAM sXREF CLOSE.
  INPUT STREAM sXREF2 CLOSE.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.inc':U).
  FOR EACH ttXref WHERE ttXref.xRefType EQ 'INCLUDE':U NO-LOCK BREAK BY ttXref.xObjID:
    IF FIRST-OF (ttXref.xObjID) THEN
      EXPORT ttXref.xObjID SEARCH(ttXref.xObjID).
  END.
  OUTPUT CLOSE.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.crc':U).
  FOR EACH ttXref WHERE LOOKUP(ttXref.xRefType, 'CREATE,REFERENCE,ACCESS,UPDATE,SEARCH':U) NE 0 NO-LOCK BREAK BY ttXref.xObjID:
    IF FIRST-OF (ttXref.xObjID) THEN DO:
      FIND CRCList WHERE CRCList.ttTable EQ ttXref.xObjID NO-LOCK NO-ERROR.
      IF (AVAILABLE CRCList) THEN DO:
        EXPORT CRCList.
      END.
    END.
  END.
  OUTPUT CLOSE.

  OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.hierarchy':U).
  FOR EACH ttXrefClasses NO-LOCK:
    IF SEARCH(REPLACE(ttXrefClasses.ttClsName, '.', '/') + '.cls') GT '' THEN
      EXPORT ttXrefClasses.ttClsName SEARCH(REPLACE(ttXrefClasses.ttClsName, '.', '/') + '.cls').
  END.
  OUTPUT CLOSE.

  IF RunList THEN DO:
    OUTPUT TO VALUE (pcDir + '/':U + pcFile + '.run':U).
    FOR EACH ttXref WHERE ttXref.xRefType EQ 'RUN':U AND ((ttXref.xObjID MATCHES '*~~.p') OR (ttXref.xObjID MATCHES '*~~.w')) NO-LOCK BREAK BY ttXref.xObjID:
      IF FIRST-OF (ttXref.xObjID) THEN DO:
        FIND TimeStamps WHERE TimeStamps.ttFile EQ ttXref.xObjID NO-LOCK NO-ERROR.
        IF (NOT AVAILABLE TimeStamps) THEN DO:
          ASSIGN cSearch = SEARCH(SUBSTRING(ttXref.xObjID, 1, R-INDEX(ttXref.xObjID, '.')) + 'r').
          IF (cSearch EQ ?) THEN
            ASSIGN cSearch = SEARCH(ttXref.xObjID).
          CREATE TimeStamps.
          ASSIGN TimeStamps.ttFile = ttXref.xObjID
                 TimeStamps.ttFullPath = (IF cSearch EQ ? THEN 'NOT FOUND' ELSE cSearch).
          ASSIGN TimeStamps.ttMod = getTimeStampF(TimeStamps.ttFullPath).
        END.
        EXPORT ttXref.xObjID TimeStamps.ttFullPath.
      END.
    END.
    OUTPUT CLOSE.
  END.

END PROCEDURE.

FUNCTION getTimeStampDF RETURNS DATETIME (INPUT d AS CHARACTER, INPUT f AS CHARACTER):
  RETURN getTimeStampF(d + (IF d EQ '':U THEN '':U ELSE '/':U) + f).
END FUNCTION.

FUNCTION getTimeStampF RETURNS DATETIME (INPUT f AS CHARACTER):
  ASSIGN FILE-INFO:FILE-NAME = f.
  RETURN DATETIME(FILE-INFO:FILE-MOD-DATE, FILE-INFO:FILE-MOD-TIME * 1000).
END FUNCTION.

FUNCTION CheckIncludes RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT ts AS DATETIME, INPUT d AS CHARACTER):
  DEFINE VARIABLE IncFile     AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE IncFullPath AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE lReturn     AS LOGICAL    NO-UNDO INITIAL FALSE.

  /* Small workaround when compiling classes */
  FILE-INFO:FILE-NAME = d + '/':U + f + '.inc':U.
  IF FILE-INFO:FULL-PATHNAME EQ ? THEN DO:
    RETURN lReturn.
  END.

  INPUT STREAM sIncludes FROM VALUE (d + '/':U + f + '.inc':U).
  FileList:
  REPEAT:
    IMPORT STREAM sIncludes IncFile IncFullPath.
    FIND TimeStamps WHERE TimeStamps.ttFile EQ IncFile NO-LOCK NO-ERROR.
    IF (NOT AVAILABLE TimeStamps) THEN DO:
      CREATE TimeStamps.
      ASSIGN TimeStamps.ttFile = IncFile
             TimeStamps.ttFullPath = SEARCH(IncFile).
      ASSIGN TimeStamps.ttMod = getTimeStampF(TimeStamps.ttFullPath).
      IF lIgnoredIncludes AND CAN-DO(cIgnoredIncludes, REPLACE(IncFile, '~\':U, '/':U)) THEN /* include is not relevant for recompile */ DO:
        RUN logVerbose IN hSrcProc (SUBSTITUTE('Ignoring changes in &1', IncFile)).
        ASSIGN TimeStamps.ttExcept = TRUE.
      END.
    END.
    IF ((TimeStamps.ttFullPath NE IncFullPath) OR (TS LT TimeStamps.ttMod)) AND (TimeStamps.ttExcept EQ FALSE) THEN DO:
      ASSIGN lReturn = TRUE
             cLastIncludeName = IncFile.
      LEAVE FileList.
    END.
  END.
  INPUT STREAM sIncludes CLOSE.
  RETURN lReturn.

END FUNCTION.

FUNCTION CheckCRC RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT d AS CHARACTER):
  DEFINE VARIABLE cTab AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE cCRC AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE lRet AS LOGICAL    NO-UNDO INITIAL FALSE.

  IF NOT fileExists(d + '/':U + f + '.crc':U) THEN
    RETURN TRUE.
  INPUT STREAM sCRC FROM VALUE(d + '/':U + f + '.crc':U).
  CRCList:
  REPEAT:
    IMPORT STREAM sCRC cTab cCRC.
    FIND CRCList WHERE CRCList.ttTable EQ cTab NO-LOCK NO-ERROR.
    IF (NOT AVAILABLE CRCList) THEN DO:
      ASSIGN lRet = TRUE.
      LEAVE CRCList.
    END.
    IF (CRCList.ttCRC NE cCRC) THEN DO:
      ASSIGN lRet = TRUE.
      LEAVE CRCList.
    END.
  END.
  INPUT STREAM sCRC CLOSE.
  RETURN lRet.

END FUNCTION.

FUNCTION checkHierarchy RETURNS LOGICAL (INPUT f AS CHARACTER, INPUT ts AS DATETIME, INPUT d AS CHARACTER):
  DEFINE VARIABLE clsName     AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE clsFullPath AS CHARACTER  NO-UNDO.
  DEFINE VARIABLE lReturn     AS LOGICAL    NO-UNDO INITIAL FALSE.

  /* Small workaround when compiling classes */
  FILE-INFO:FILE-NAME = d + '/':U + f + '.hierarchy':U.
  IF FILE-INFO:FULL-PATHNAME EQ ? THEN DO:
    RETURN lReturn.
  END.

  INPUT STREAM sHierarchy FROM VALUE (d + '/':U + f + '.hierarchy':U).
  FileList:
  REPEAT:
    IMPORT STREAM sHierarchy clsName clsFullPath.
    FIND TimeStamps WHERE TimeStamps.ttFile EQ clsName NO-LOCK NO-ERROR.
    IF (NOT AVAILABLE TimeStamps) THEN DO:
      CREATE TimeStamps.
      ASSIGN TimeStamps.ttFile = clsName
             TimeStamps.ttFullPath = SEARCH(REPLACE(clsName, '.', '/') + '.cls').
      ASSIGN TimeStamps.ttMod = getTimeStampF(TimeStamps.ttFullPath).
    END.
    IF ((TimeStamps.ttFullPath NE clsFullPath) OR (ts LT TimeStamps.ttMod)) AND (TimeStamps.ttExcept EQ FALSE) THEN DO:
      ASSIGN lReturn = TRUE.
      LEAVE FileList.
    END.
  END.
  INPUT STREAM sHierarchy CLOSE.
  RETURN lReturn.

END FUNCTION.

FUNCTION fileExists RETURNS LOGICAL (INPUT f AS CHARACTER):
  ASSIGN FILE-INFO:FILE-NAME = f.
  RETURN (FILE-INFO:FULL-PATHNAME NE ?).
END FUNCTION.

FUNCTION createDir RETURNS LOGICAL (INPUT base AS CHARACTER, INPUT d AS CHARACTER):
  DEFINE VARIABLE i AS INTEGER    NO-UNDO.
  DEFINE VARIABLE c AS CHARACTER  NO-UNDO.

  /* Asserts base is a writable directory */
  FIND ttDirs WHERE ttDirs.baseDir EQ base
                AND ttDirs.dirName EQ d
              NO-LOCK NO-ERROR.
  IF (AVAILABLE ttDirs) THEN
    RETURN TRUE.

  ASSIGN d = REPLACE(d, CHR(92), '/':U).
  DO i = 1 TO NUM-ENTRIES(d, '/':U):
    ASSIGN c = c + '/':U + ENTRY(i, d, '/':U).
    FIND ttDirs WHERE ttDirs.baseDir EQ base
                  AND ttDirs.dirName EQ c
                NO-LOCK NO-ERROR.
    IF (NOT AVAILABLE ttDirs) THEN DO:
      OS-CREATE-DIR VALUE(base + c).
      IF (OS-ERROR EQ 0) OR (OS-ERROR EQ 999) THEN DO:
        /* Issue #200: error code 999 is sometimes sent when 2 processes want to create dir at the same time */
        CREATE ttDirs.
        ASSIGN ttDirs.baseDir = base
               ttDirs.dirName = c.
      END.
      ELSE DO:
        RUN logError IN hSrcProc (SUBSTITUTE("Unable to create directory '&1' - Err &2", c, OS-ERROR)).
        RETURN FALSE.
      END.
    END.
  END.
  RETURN TRUE.

END FUNCTION.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy