Saber el numero de lineas de codigo de un proyecto

931 views
Skip to first unread message

Ernesto

unread,
Nov 27, 2012, 11:28:36 AM11/27/12
to publice...@googlegroups.com
Es posible ? es decir el numero total de lineas de las clases los prgs los forms las queries etc que esten en un proyecto

existe alguna herramiento para ello ?

Jose Mario

unread,
Dec 13, 2012, 12:49:35 PM12/13/12
to publice...@googlegroups.com
me impresiona que entre mas leo los mensajes, mas locuras encuentro, que no me imaginaba que existieran estas preguntas.

para que diablos se requerirá esta incognita

Francisco

unread,
Dec 13, 2012, 1:05:42 PM12/13/12
to publice...@googlegroups.com
Jose para decir como el de Parque Jurasico, que no es facil depurar dos millones de lineas de código... 

Carlos Miguel FARIAS

unread,
Dec 13, 2012, 5:30:49 PM12/13/12
to publice...@googlegroups.com
Estimado: En ingeniería de software, un factor muy importante para determinar tiempo de desarrollo, probabilidad de errores y otras cosas relativas o relacionadas, un factor importante que se tiene en cuenta es la cantidad de líneas de código.
Por ejemplo, hay estimaciones que 120 instrucciones de COBOL, pueden ser reescritas con 40 líneas de un lenguaje orientado a objeto.
Irónicamente, los programadores que escriben assembler, son capaces de generar 10 veces mas instrucciones de código que un programador de otros lenguajes, eso si, para hacer la misma funcionalidad, necesitan 20 a 100 veces mas instrucciones.
Justamente, porque estamos tan apegados a VFP, porque con muy pocas líneas de código, logramos funcionalidad mas que interesante.
Y porque para acceso a datos se usa, indiscutiblemente, y siempre que sea posible SQL, porque una instrucción SQL (lenguaje de 4ta generación) puede reemplazar hasta ciento de líneas de código de lenguajes OO.
No se si en el grupo hay algún ingeniero de sistemas, que podría dar una mejor explicación (yo empece una maestría del tema, pero por razones de trabajo y económicas no puede seguir)
Saludos: MIguel, La Pampa (RA)


--
 
 

FidelJ

unread,
Dec 13, 2012, 7:24:37 PM12/13/12
to publice...@googlegroups.com
Esta rutina permite saber el número de líneas de código escritas en PRG y SCX. 
No he analizado el tema de las bibliotecas de clase, cosa que no es menor. Pero al menos para dar una idea.
El procedimiento toma todos los archivos, entre los que pueden existir residuos que han sido descartados.

*************************************************
PROCEDURE Raconto(xcDapli,xcDtemp)
**************************************************
* xcDapli: Directorio de la aplicación
* xcDtemp: Directorio de trabajo (si no se define, toma xcDapli)
* Supone que los procedimientos están en "\PROGS"
* Supone que los Formularios están en "\forms"

LOCAL nFiles,lnLineas
IF VARTYPE(xcDapli)#"C".or.empty(xcDapli)
MESSAGEBOX("Indique la carpeta base",0,"Raconto")
RETURN
ENDIF
IF !DIRECTORY(xcDapli)
MESSAGEBOX("La carpeta &xcDapli no existe!",0,"Raconto")
RETURN
ENDIF
if vartype(xcDtemp)#"C".or.empty(xcDtemp)
        xcdtemp=xcdapli
endif
lcDirProg=ADDBS(xcDapli)+"PROGS"
lcDirForm=ADDBS(xcDapli)+"FORMS"
IF !DIRECTORY(lcDirProg)
MESSAGEBOX("No existe la carpeta &lcDirProg",0,"Raconto")
RETURN
ENDIF
IF !DIRECTORY(lcDirForm)
MESSAGEBOX("No existe la carpeta &lcDirForm",0,"Raconto")
RETURN
ENDIF
nMax=0

RELEASE GACLAVE
PUBLIC ARRAY gaclave(4,3)
gaclave[1,1]="PROCEDURE"
gaclave[1,2]=.t.
gaclave[2,1]="FUNCTION"
gaclave[2,2]=.t.
gaClave[3,1]="ENDPROC"
gaclave[4,1]="ENDFUNC"
FOR i=1 TO ALEN(gaclave,1)
GACLAVE[i,3]=0
NEXT
PUBLIC omens
oMens=CREATEOBJECT("Form")
IF VARTYPE(omens)="O"
WITH omens
.Width=680
.Height=360
.top=40
.Left=50
.BackColor=RGB(255,255,255)
.ForeColor=RGB(0,0,0)
.FontName="Arial"
.FontSize=8
.Caption="Raconto"

.Visible=.t.
.Show()
ENDWITH
endif
nSto=0
LOCAL ARRAY gastore(1,4)
nFiles=ADIR(gaFiles,ADDBS(LcdirProg)+"*.prg")
IF nFiles>0
omens.Caption="Recorriendo Procedimientos Prg"
FOR i=1 TO ALEN(gafiles,1)
IF LEN(gafiles[i,1])>nMax
nMax=LEN(gafiles[i,1])
endif
gafiles[i,2]=0
gafiles[i,3]=0
gafiles[i,4]=0
next
FOR si=1 TO ALEN(gafiles,1)
nfop=-1
IF !GEtfopen(gafiles[si,1],@nfop)
LOOP
ENDIF
* WAIT WINDOW "Archivo de Procedimientos "+gafiles[si] AT 12,15 nowait
? " Archivo de Procedimientos "+gafiles[si,1]
Racontext(NFOP)
NEXT
bi=ALEN(gafiles,2)
FOR i=1 TO ALEN(gafiles,1)
nSto=nSto+1
DIMENSION gastore(nSto,bi)
FOR j=1 TO ALEN(gafiles,2)
gastore[nsto,j]=gafiles[i,j]
NEXT
NEXT

ENDIF

* Formularios
RELEASE gafiles
cNametod=addbs(xcDtemp)+"gMetod.txt"

nFiles=ADIR(gaFiles,ADDBS(LcdirForm)+"*.scx")
IF nFiles>0
* WAIT WINDOW "Recorriendo Formularios..." AT 12,15 nowait
omens.Caption="Recorriendo Formularios..."
FOR i=1 TO ALEN(gafiles,1)
IF LEN(gafiles[i,1])>nMax
nMax=LEN(gafiles[i,1])
endif
gafiles[i,2]=0
gafiles[i,3]=0
gafiles[i,4]=0
NEXT
FOR si=1 TO ALEN(gafiles,1)
lcFormu=ADDBS(lcDirForm)+gafiles[si,1]
* WAIT WINDOW "Analizando &lcFormu..." AT 12,15 nowait
? " Analizando &lcFormu..."
SELECT 0
USE (lcFormu) IN 0 alias oformu 
select oformu
SCAN FOR ALLTRIM(PLATFORM)=="WINDOWS"
if !empty(Methods)
lHavMetod=.t.
Strtofile(Methods,cNametod)
IF Getfopen(cNametod,@nfop)
Racontext(NFOP)
endif
endif
ENDSCAN
SELECT oformu
use
NEXT
bi=ALEN(gafiles,2)
FOR i=1 TO ALEN(gafiles,1)
nSto=nSto+1
DIMENSION gastore(nSto,bi)
FOR j=1 TO ALEN(gafiles,2)
gastore[nsto,j]=gafiles[i,j]
NEXT
NEXT

ENDIF
oMens.Release

cStringTot="Total de Líneas"
IF LEN(cStringTot)>nMax
nMax=LEN(cStringTot)
ENDIF
nMaxLin=0
FOR i=1 TO ALEN(gastore,1)
IF gastore[i,2]>nMaxLin
nMaxLin=gastore[i,2]
ENDIF
NEXT
DO case
CASE nMaxLin>900000
cTRansf="99,999,999"
OTHERWISE
cTransf="9,999,999"
ENDCASE
FCR=ADDBS(xcDTEMP)+"Reportprg.txt"
nfop=FCREATE(fcr)
FOR i=1 TO 2
=FPUTS(nfop,PADR(gaClave[i,1]+"=",nMax+1)+transform(gaclave[i,3],ctransf))
NEXT
=fputs(nfop,"Total de Líneas por Prg")
lnLineas=0
STORE 0 TO lnLineas, lnBlank,lnComment
=FPUTS(nfop,PADR("Archivo",nMax)+" ";
+PADC("Código",LEN(cTransf))+" ";
+PADC("Blank",LEN(cTRansf))+" ";
+PADC("Comment",LEN(ctransf)))
FOR i=1 TO ALEN(gaStore,1)
lnLineas=lnLineas+gaStore[i,2]
lnBlank=lnBlank + gaStore[i,3]
lnComment=lnComment+gaStore[i,4]
=FPUTS(nfop,PADR(gaStore[i,1]+"=",nMax+1)+transform(gaStore[i,2],cTransf);
+" "+TRANSFORM(gaStore[i,3],cTRansf);
+" "+TRANSFORM(gaStore[i,4],cTRansf))
NEXT
=FPUTS(nfop,PADR(cSTringTot,nMax+1)+TRANSFORM(lnLIneas,cTransf);
+" "+TRANSFORM(lnBlank,cTransf);
+" "+TRANSFORM(lnComment,cTransf))
=FCLOSE(nfop)
RELEASE omens,gaclave
IF FILE(fcr)
o = CREATEOBJECT("Shell.Application")
o.ShellExecute("write.exe", '&fcr', "", "open", 1)
*MODIFY FILE &fcr noedit
ENDIF

*************************
PROCEDURE Racontext(NFOP)
*************************
DO WHILE !FEOF(nfop)
cString=UPPER(ALLTRIM(FGETS(nfop,2048)))
lProfun=.f.
DO case
CASE EMPTY(cString)
gafiles[si,3]=gafiles[si,3]+1
CASE LEFT(cString,1)="*"
gafiles[si,4]=gafiles[si,4]+1
CASE LEFT(cSTring,2)=REPLICATE("&",2)
gafiles[si,4]=gafiles[si,4]+1
OTHERWISE
FOR ri=1 TO ALEN(gaclave,1)
IF LEFT(cString,LEN(gaclave[ri,1]))==gaclave[ri,1]
IF gaclave[ri,2]
gaclave[ri,3]=gaclave[ri,3]+1
* gaclave[ri,4]=SUBSTR(cstring,LEN(gaclave[ri,1]))
lProfun=.t.
exit
endif
ENDIF
NEXT
IF !lProfun
gafiles[si,2]=gafiles[si,2]+1
endif
ENDCASE
ENDDO
=FCLOSE(NFOP)
ENDPROC

****************************************************
Procedure GetFopen(xcFile,npfop,xlNotVerb,nPrivileg)
********************************
*lGet=GetFopen(addbs(dtemp)+"archivo.txt",@nfop,lNotVerb)
*************************************************************
local cArchivo,lRet
if vartype(nPrivileg)#"N"
nPrivileg=0
endif
cArchivo=xcFile
lREt=.t.
IF !file(cArchivo)
if !xlNotVerb
GEtINforma("<NFOC/>El archivo &cArchivo no existe")
endif
lREt=.f.
endif
if lRet
npfop=fopen(cARchivo,nPrivileg)
if npfop=-1
if !xlNotVerb
GEtINforma("<NFOC/>El archivo &cArchivo no pudo abrirse")
endif
lREt=.f.
endif
endif
if lRet
gnEnd=Fseek(npfop,0,2)
gntop=Fseek(npfop,0)
if gnEnd<=0
if !xlnotVerb
GEtINforma("<NFOC/>El archivo &cArchivo no tiene contenido")
endif
fclose(npfop)
lRet=.f.
endif
endif
return lret
ENDPROC

Carlos Miguel FARIAS

unread,
Dec 14, 2012, 6:22:27 AM12/14/12
to publice...@googlegroups.com
Que laburo!!!
Sugerencia: con la misma lógica que analisas los formularios, podes analizar las bibliotecas, eso si, cambiando los campos accedidos, pero la lógica es igual.
Saludos: Miguel, Santa Rosa (LP)


2012/12/13 FidelJ <fjch...@gmail.com>

--
 
 

FidelJ

unread,
Dec 14, 2012, 8:02:50 AM12/14/12
to publice...@googlegroups.com
Gracias Miguel, ya estaré investigando.

Ernesto

unread,
Dec 14, 2012, 9:01:08 AM12/14/12
to publice...@googlegroups.com
Lo necesito para establecer metricas de productividad entre distintos desarrolladores, asi como tambien metricas de  calidad de los proyectos concluidos.

Gracias por la rutina la probare y si es posible la adaptare a clases y frx

tomcrux

unread,
Dec 14, 2012, 1:29:59 PM12/14/12
to publice...@googlegroups.com
 
Ernesto!
que tal!
Quizas este código te sirva. No es mío, supe encontrarlo en la web alguna vez.
 
Saludos desde
Córdoba, Argentina
 
Tomás Cruz.
 
 
 **********************************************
* Cuenta lineas de código de un proyecto.
* leer antes las consideraciones
*
**********************************************
* Tipos de archivos incluidos en el conteo:
* Form ("K"), bibliotecas de clases ("V") y programas ("P").
*
* ¿Que determina una "verdadera" línea de código?:
* Las líneas vacías no se cuentan.
* Puede ir más lejos y excluir lineas comentadas .
*
* ¿Qué pasa si se posee un framework se podria excluir?
* Si se tiene una buena Convención de nomenclatura si.
* Podría modificar el select para excluir a estos por su nombre,
* o la ubicación de la carpeta si es posible. 
*
* Para usarlo, simplemente pasar el nombre de un archivo de proyecto.
* Se puede incluir la ruta completa al proyecto o no, pero debe
* existir el archivo en la ruta actual del path
(es decir, SET("PATH")).
*
* Ej:
* ? ProjectLineCount("MiProjecto")
*
**********************************************
**** Begin Code
#Define CR Chr(13)
#Define CRLF CR+Chr(10)
#Define CRLFTAB CRLF+Chr(9)
LParameters tcProject
Local ji, laFiles(1), lcCurDir, lcFile, lcProject, lcTalk, ;
     lcType, lnFCnt, lnLineCnt    
Store "" To lcCurDir, lcFile, lcProject, lcTalk, lcType
Store 0 To ji, lnFCnt, lnLineCnt
If Vartype(tcProject)#"C" Or Empty(tcProject) ;
          Or !File(ForceExt(tcProject, "pjx"))
     MessageBox("Must pass in valid project name.", 16)
     Return 0
EndIf
lcProject = FullPath(ForceExt(tcProject, "pjx"))
lcCurDir = FullPath(CurDir())
CD (JustPath(lcProject))
Close All
Select PadR(Alltrim(Strtran(name, Chr(0))),254), type ;
     From (ForceExt(lcProject, "pjx")) ;
     Where type $ "KPV" ;
     Into Array laFiles
lnFCnt=_Tally
Use && close the pjx
lnLineCnt = 0
lcTalk = Set("Talk")
Set Talk Off
For ji = 1 To lnFCnt
     lcFile = Alltrim(laFiles[ji,1])
     Wait Window lcFile + Chr(13) + Transform(ji) + " of " + Transform(lnFCnt) nowait
     lcType = laFiles[ji,2]
     If lcType = "P"
          *-- program
          lnLineCnt = lnLineCnt + RealLines(FileToStr(lcFile))
     Else
          *-- form or classlib
          Use (lcFile)
          Scan For !Empty(methods)
               lnLineCnt = lnLineCnt + RealLines(Alltrim(methods))
          EndScan
          Use
     EndIf
EndFor
Set Talk &lcTalk
CD (lcCurDir)
Return lnLineCnt
*--------------
* RealLines
*--------------
*     return the "real" line count from the given string
*     may need to be modified depending on your definition of
*     a "real" line - e.g., this version does not elminate
*     comments.
Function RealLines(tcString)
     Local lcString, laLines(1)
     lcString=Alltrim(tcString)
     lcString=Strtran(lcString, CRLFTAB, CRLF)
     Do While CRLF+CRLF $ lcString
          lcString = Strtran(lcString, CRLF+CRLF, CRLF)
     EndDo
     Do While !IsAlpha(Right(lcString, 1)) And !IsDigit(Right(lcString,1))
          lcString = Left(lcString, Len(lcString)-1)
     EndDo
     Return  ALines(laLines, lcString)
EndFunc
*** EndCode
--------------------------------------------------------------------------------

FidelJ

unread,
Dec 14, 2012, 2:45:51 PM12/14/12
to publice...@googlegroups.com

Ahora coloqué las rutinas necesarias en una clase Custom, librería Infocode.vcx.
Aunque todavía están faltando reportes, se han agregado bibliotecas (vcx) y menús. También hay algunos elementos más de control del flujo y un poquito mejorado el informe.

Agregando la biblioteca al proyecto, se podría convocar con:
*****************************************************
PROCEDURE GetRaconto(xcDapli,xcdTemp)
*****************************************************
* xcDapli: Directorio de la aplicación
* xcdTEmp: Directorio de temporales.
*          Se genera un archivo Reportprg.Txt
*          en la carpeta xcdTemp
************************************************
* Instancia la clase INFOCODE
* Crea un objeto sobre la Custom LinCode
* Llama al procedimiento de Lincode Raconto()
* Quita el objeto
* Libera la clase INFOCODE
************************************************
IF VARTYPE(xcDapli)#"C".or.empty(xcDapli)
xcDapli=SYS(5)+CURDIR()
ENDIF
IF VARTYPE(xcDtemp)#"C".or.empty(xcdTEmp)
xcDtemp=xcDapli
ENDIF
xcClase="INFOCODE"
gClase=xcClase+".VCX"
IF AT(gClase,SET("CLASSLIB"))=0
SET CLASSLIB TO &xcClase ADDITIVE
cCurClassLib=SET("CLASSLIB")
nAt=AT(gClase,cCurClassLib)
IF nAt=0
IF EMPTY(cCurClassLib)
    cCurClassLib="Ninguna"
ENDIF
MESSAGEBOX("No se pudo instalar "+PROPER(gClase)+CHR(13);
+"SetClassLib="+Proper(cCurClasslib),0,Th_mensaje)
RETURN
ENDIF
ENDIF
objINfo=CREATEOBJECT("infocode.lincode")
objInfo.raconto(xcdapli,xcdtemp)
RELEASE ObjInfo
RELEASE CLASSLIB INFOCODE
ENDPROC
InfoCode.ZIP

Ernesto

unread,
Dec 15, 2012, 3:34:08 PM12/15/12
to publice...@googlegroups.com

GRACIAS


El martes, 27 de noviembre de 2012 10:28:36 UTC-6, Ernesto escribió:

hquinones

unread,
Dec 15, 2012, 9:24:17 PM12/15/12
to publice...@googlegroups.com
Para Jose Mario, No opines si no conoces el tema. Me imagino que ya leíste la necesidad que tiene.
Message has been deleted

FidelJ

unread,
Dec 16, 2012, 10:34:44 AM12/16/12
to publice...@googlegroups.com
He cometido un error por lo que quito la publicación anterior y vuelvo con esta.
Es que al analizar el contenido del proyecto, los archivos MPR de menú no están disponibles, en su lugar, están los .mnx. Al analizar los mnx como si fueran mpr el resultado era cualquier cosa. Ahora InfoCode toma los mnx como tablas y analiza los distintos contenidos.

Ernesto:
Después de estudiar un poquito hice unos cambios en las rutinas de la clase InfoCode.
Ahora:
1) detecta el proyecto activo
2) Abre el archivo de proyecto (pjx) como tabla.
3) Recoge todas las direcciones y archivos comprendidos. La versión anterior resultaba limitada en esto, puesto que solamente analizaba las rutas de la carpeta de origen. Ahora depende de lo enunciado en el archivo de proyecto pjx.
4) La rutina de inspección se ha cambiado: Case para mnx, Case para prg , Case para .scx y vcx, Case para .frx
Por la mecánica, el procedimiento considera todos los archivos que están en la ruta de búsqueda según su extensión, pero no incluye aquellos que no están referenciados en el proyecto. Por eso aparecen en el informe como "Archivos omitidos por no pertenecer al proyecto". 
5) Se puede detener el proceso en cualquier momento con [F8]. Esto aborta el proceso y no genera informe.
6) Alguna mejora en el informe

Precauciones
a) La rutina utiliza para el caso de los formularios y bibliotecas un test de archivo abierto. Lo que conviene para evitar exclusiones por este motivo, es cerrar el proyecto, abrirlo y ejecutar el GetRaconto() desde la ventana "Command".
b) La lista de exclusión tiene su importancia. A fin de evitar incluir en el conteo los códigos generados y provistos por Visual Fox, no se analizan los elementos que están en la ruta de instalación Home(1).
También se excluyen los archivos típicos que se generan automáticamente con el proyecto.
c) En esta versión no se analizan los arhivos .h ni config.fpw.
d) Como resulta de la mecánica, esta cosa solo analiza el proyecto activo.
Saludos

El martes, 27 de noviembre de 2012 13:28:36 UTC-3, Ernesto escribió:
InfoCode.zip

Mario Oviedo

unread,
Dec 17, 2012, 12:12:21 PM12/17/12
to Comunidad de Visual Foxpro en Español
hquino

perdon flor
> --
>
>

Jose Luis Barros

unread,
Dec 27, 2012, 9:26:01 AM12/27/12
to publice...@googlegroups.com
Sin querer polemizar pero la verdad estoy de acuerdo en lo poco útil que resulta saber cuántas líneas de código tiene una aplicación. Y mucho menos si lo que buscas es medir calidad, tamaño o productividad. Esa fue una idea que era de pronto razonable en las épocas de COBOL donde los programas eran casi que monolíticos. Hoy en día, por ejemplo, uno puede incluir una librería de clases inmensa con mucha funcionalidad como parte de un framework de desarrollo que facilita mucho las cosas para el desarrollador, le permite desarrollo más rápido, sin errores y con una lógica más clara, pero igual puede suceder que el programa real sólo utilice el 10% del total de clases del framework. ¿Indicaría esto que el programa es malo por la cantidad de líneas de código? ¿Sería un buen indicador del tiempo de desarrollo el número de líneas de código? ¿Podría calcularse adecuadamente la productividad sabiendo el total de líneas de código? La respuesta obvia a estas preguntas es NO. Ni siquiera como estimativo sirve. 

En mi concepto, prefiero un programa que utilice muchas líneas de código que sean fáciles de entender que un programa que en una sola línea de código casi que encriptada encierre un montón de funcionalidad que pocos entenderían. 

Si buscas medir productividad, tamaño o calidad existen formas más modernas y adecuadas. En mi caso estoy utilizando metodologías de desarrollo como Programación Extrema y SCRUM que me han permitido poco a poco entender mejor cómo estimar tanto calidad como productividad.

Fox Learner

unread,
Dec 27, 2012, 10:08:32 AM12/27/12
to publice...@googlegroups.com
Ahora veo porque son programadores ANALISTAS jeje   Saludos!

Carlos Miguel FARIAS

unread,
Dec 27, 2012, 11:33:10 AM12/27/12
to publice...@googlegroups.com
Que no se sepa utilizar "la cantidad de líneas de código" no quiere decir que no sea útil cuando se tiene que estimar el presupuesto de un sistema, tanto en monto, como en tiempo de desarrollo.
Por supuesto que esto es generalmente manejado a nivel de ingenieria de software.
Si nunca se han ilustrado acerca de las pautas de ingenieria para diseño de software, no se puede indicar que esa unidad de medida no es aplicable o inutil.
Y en general, de acuerdo a las buenas prácticas de programación, cuando se dicen lineas de código, se habla de instrucciones propiamente dichas (ocupen 1 renglon o N renglones) y no renglones de código amontonado, donde en todos los lenguajes (al menos) que admiten puntos y comas como terminadores de instrucción, permiten mas de una instrucción por renglon.
Muchos que aqui preguntan cuanto cobrar por un sistema, sabrían que cobrar si usaran alguna metodologia de ingenieria como por lo menos saber que costo tendran.
Sabidas las lineas de código, se puede estimar tiempo de desarrollo.
Que se usen técnica de las llamadas Agiles, no quita importancia a las metricas de ingenieria, son un elemento mas de trabajo.
Saludos: Miguel, La Pampa (RA)

El 27 de diciembre de 2012 12:08, Fox Learner <thenewin...@gmail.com> escribió:
Ahora veo porque son programadores ANALISTAS jeje   Saludos!

--
 
 

Reply all
Reply to author
Forward
0 new messages