Posted: Fri May 15, 2009 9:07 am
I think that PB doesn't include unused procedures in the exe. Is this just to shorten the source code?
http://www.purebasic.com
https://www.purebasic.fr/english/
No, PB compiles and includes everything you put in your source. If you keep a lot of utility code in include files (as I do), it can bulk up your executable quite a bit unless you use something like Trimmer.DoubleDutch wrote:I think that PB doesn't include unused procedures in the exe. Is this just to shorten the source code?
Here 'tis.Seymour Clufley wrote:Maker, I'm finding that URL isn't working again!
The best thing would be to post the source code here.
Code: Select all
; Trimmer 1.2 -
; trimmer.pb
; - fixed bug that caused some false hits on variables with same name as procs
; - added ability to filter out recursive references, so no more false hits there
; either
DataSection
usageText:
Data.s "============== "
Data.s "= Trimmer 1.2 "
Data.s "============== "
Data.s "Takes your source code set and creates two files from it: "
Data.s " 1) a merged file containing all code in your main file + "
Data.s " all included file text "
Data.s " 2) a merged-and-trimmed file, same as #1 but with unused "
Data.s " procedures replaced by comments. "
Data.s "By compiling the 2nd file, you should get much smaller executables "
Data.s "(if you had a lot of library code in the include files). "
Data.s " "
Data.s " Usage: "
Data.s " trimmer1.2.exe mainFileName.pb "
Data.s " "
Data.s " Credits: "
Data.s " Trimmer uses stack and file merge code by Stephen Rodriguez (srod)."
Data.s " The rest can be blamed on Craig Gilbert (maker). "
EndDataSection
Macro USAGETEXTLINES: 17 : EndMacro
Macro extensionExtension : "pp" : EndMacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; support routines
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Procedure showUsageAndExit()
Define outtext.s
Restore usageText
For i = 1 To USAGETEXTLINES
Read.s outtext
PrintN(outtext);
Next i
End 1
EndProcedure
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; main program
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
If Not OpenConsole()
End 1
EndIf
;
; get target file from command line
;
If Not CountProgramParameters()
showUsageAndExit()
EndIf
Define mainFile.s
;
; This is pretty crude but it gets around a problem with 3rd party editors and
; batch files causing nested double quoted file names. Note that it also
; means this prog can only have one command line arg.
For i = 1 To CountProgramParameters()
mainFile = mainFile + ProgramParameter()
Next i
Define temp.s
For i = 1 To Len(mainFile)
If Mid(mainFile, i, 1) <> Chr(34)
temp = temp + Mid(mainFile,i,1)
EndIf
Next i
mainFile = temp
;
; get cur dir
;
Define homeDir.s = GetCurrentDirectory()
;
; build one large file from main file + included files
;
XIncludeFile "srod_merge.pbi"
Define mergedFilename.s = mainFile+"MERGED"
Define retval = MergePBSources(mainFile, mergedFilename)
If retval <> #MPBS_OKAY
Select retval
Case #MPBS_INVALIDSOURCEFILE
PrintN("file merger says: MPBS_INVALIDSOURCEFILE:")
PrintN(" " + mainFile)
Case #MPBS_INSUFFICIENTMEMORY
PrintN("file merger says: MPBS_INSUFFICIENTMEMORY")
Case #MPBS_FILEOPENERROR
PrintN("file merger says: MPBS_FILEOPENERROR, one of these:")
PrintN(" " + mainFile)
PrintN(" " + mergedFilename)
Case #MPBS_INVALIDPATH
PrintN("file merger says: MPBS_INVALIDPATH")
Case #MPBS_INVALIDINCLUDEFILE
PrintN("file merger says: MPBS_INVALIDINCLUDEFILE")
Case #MPBS_INCLUDEFILENOTFOUND ;In this case, gMPBS_Include$ will contain the path\filename
;of the file which cannot be located.
PrintN("file merger says: MPBS_INCLUDEFILENOTFOUND:")
PrintN(" " + gMPBS_Include$)
Case #MPBS_NESTEDINCLUDESOVERFLOW ;Probably a recursive use of a certain include file.
PrintN("file merger says: MPBS_NESTEDINCLUDESOVERFLOW")
EndSelect
End 1
EndIf
;
; get merged file
;
Define f = ReadFile(#PB_Any, mergedFilename)
If Not f
PrintN("Unable to open merged file: " + mergedFilename)
End 1
EndIf
Define textLine.s
NewList lineList.s()
While Not Eof(f)
textLine = ReadString(f)
AddElement(lineList())
lineList() = textLine
Wend
CloseFile(f)
If ListSize(lineList()) < 1
PrintN("No merged file available!")
End 1
EndIf
Procedure isIdentifierChar(ch.c)
If (ch >= '0' And ch <= '9') Or (ch >= 'A' And ch <= 'Z') Or (ch >= 'a' And ch <= 'z') Or ch = '_' Or ch = '$'
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure is_procedure_line(l.s)
found = FindString(LCase(l), "procedure", 1)
If Not found : ProcedureReturn 0 : EndIf
Define ch.s = ""
For x = 1 To found-1 ; make sure nothing before 'Procedure' except spaces or tabs
ch = Mid(l,x,1)
If ch <> " " And ch <> Chr(9)
ProcedureReturn 0
EndIf
Next x
ProcedureReturn found
EndProcedure
Procedure is_end_procedure_line(l.s)
found = FindString(LCase(l), "endprocedure", 1)
If Not found : ProcedureReturn #FALSE : EndIf
Define ch.s = ""
For x = 1 To found-1 ; make sure nothing before it except spaces or tabs
ch = Mid(l,x,1)
If ch <> " " And ch <> Chr(9)
ProcedureReturn #FALSE
EndIf
Next x
For x = found + 12 To Len(l)
ch = Mid(l,x,1)
If ch = " " Or ch = Chr(9)
ProcedureReturn #TRUE ; If at least one space or tab after it, assume the
; rest is comments or macros that eval to comments
Else
ProcedureReturn #FALSE
EndIf
Next x
ProcedureReturn #TRUE
EndProcedure
;
; purge unused procedures
;
Structure procstruct
name.s
used.l
*start ; buildProcList() must set this
*endproc
EndStructure
Procedure buildProcList(List lines.s(), List procList.procstruct())
; expects procList to be empty.
Define l.s
ResetList(lines())
For i = 1 To ListSize(lines())
If Not NextElement(lines())
PrintN("Error in buildProcList()! Linked list lines() error!")
End 1
EndIf
l = lines()
found = is_procedure_line(l)
If found
;
; get name
;
found + 9
If found > Len(l): PrintN("Line ended too early! Merged code Line " + Str(i)) : End 1 : EndIf
Define ch.s = Mid(l,found,1)
If ch <> " " And ch <> Chr(9) And ch <> "." ; Not a procedure
Continue
EndIf
If ch = "."
While ch <> " " And ch <> Chr(9) ; roll past procedure return type
found + 1
If found > Len(l): PrintN("Line ended too early! Merged code Line " + Str(i)) : End 1 : EndIf
ch = Mid(l,found,1)
Wend
EndIf
ch = Mid(l,found,1)
While ch = " " Or ch = Chr(9) ; roll past spaces and tabs
found + 1
ch = Mid(l,found,1)
Wend
AddElement(proclist())
While ch <> "(" And ch <> " " And ch <> Chr(9)
procList()\name + ch
found + 1
ch = Mid(l,found,1)
Wend
;
; get line location for quick return later
;
procList()\start = @lines()
Else
If is_end_procedure_line(l)
procList()\endproc = @lines()
EndIf
EndIf
Next i
EndProcedure
Procedure declarationLine(l.s)
Define found = FindString(LCase(l), "declare",1)
If Not found : ProcedureReturn 0 : EndIf
If found > 1
Define ch.l = Asc(Mid(l,found-1,1))
If ch <> ' ' And ch <> 9
ProcedureReturn 0
EndIf
EndIf
found + 7
If found > Len(l): ProcedureReturn 0 : EndIf
ch = Asc(Mid(l,found,1))
If ch <> ' ' And ch <> 9
ProcedureReturn 0
EndIf
ProcedureReturn 1
EndProcedure
Macro NORMAL: 1: EndMacro
Macro INQUOTES: 0: EndMacro
Procedure markProcs(List lines.s(), List procList.procstruct())
; look for procedure references on the line l
Define ch.s, identifierBuffer.s, mode.l = NORMAL, l.s = lines()
For i = 1 To Len(l)
ch = Mid(l, i, 1)
If Asc(ch) = '"'
If mode = NORMAL
mode = INQUOTES
Else
mode = NORMAL
EndIf
Else
If mode = NORMAL
If Asc(ch) = ';' ; no need to look further on this line
Break
ElseIf isIdentifierChar(Asc(ch))
identifierBuffer + ch
ElseIf ch = "(" ;; ding ding ding! we may have a procedure reference
If Len(identifierBuffer) > 0
ResetList(procList()) ;; loop through all proc names
For x = 1 To ListSize(procList())
If Not NextElement(procList())
PrintN("Error in markProcs()! Linked list procList() error!")
End 1
EndIf
If LCase(procList()\name) = LCase(identifierBuffer)
; filter definition lines and recursive references
If @lines() < procList()\start Or @lines() > procList()\endproc
If Not declarationLine(l)
procList()\used + 1
Break
EndIf
EndIf
EndIf
Next x
identifierBuffer = ""
EndIf
Else
identifierBuffer = ""
EndIf
EndIf
EndIf
Next i
; Not sure why I had this in here at all; no identifier hanging off the end
; of a line is going to be a procedure name, and it ended up creating a false
; call.
; If Len(identifierBuffer) > 0
; ResetList(procList())
; For x = 1 To ListSize(procList())
; If Not NextElement(procList())
; PrintN("Error in markProcs()! Linked list procList() error!")
; End 1
; EndIf
; If procList()\name = identifierBuffer
; procList()\used + 1
; Break
; EndIf
; Next x
; EndIf
EndProcedure
Procedure blankToEndProcedure(List lines.s(), List procList.procstruct())
For i = ListIndex(lines()) To ListSize(lines())
Define found.l = FindString(LCase(lines()), "endprocedure",1)
lines() = "; unused Procedure " + procList()\name + "() was here"
If found
Break
EndIf
If Not NextElement(lines())
PrintN("Error in blankToEndProcedure()! Linked list lines() error!")
End 1
EndIf
Next i
EndProcedure
Procedure purgeUnusedProcedures(List lines.s())
NewList procList.procstruct()
buildProcList(lines(), procList())
;
; check each line for procedure references
;
ResetList(lines())
For i = 1 To ListSize(lines())
If Not NextElement(lines())
PrintN("Error in purgeUnusedProcedures()! Linked list lines() error!")
End 1
EndIf
markProcs(lines(), procList())
Next i
;
; replace unused procedure lines with blank lines
;
Define foundUnused
ResetList(procList())
For i = 1 To ListSize(procList())
If Not NextElement(procList())
PrintN("Error in purgeUnusedProcedures()! Linked list procList() error!")
End 1
EndIf
If Not procList()\used
ChangeCurrentElement(lines(), procList()\start)
blankToEndProcedure(lines(), procList())
foundUnused + 1
EndIf
Next i
ProcedureReturn foundUnused
EndProcedure
; keep purging until nothing to purge
While purgeUnusedProcedures(lineList())
Wend
;
; save to file with name formed by mainFile + "TRIMMED"
;
Define outfile.s
ResetList(lineList())
For i = 1 To ListSize(lineList())
If Not NextElement(lineList())
; uh-oh, should never get here
PrintN("My linked list got foobared!")
End 1
EndIf
If Len(lineList()) > 0
outfile + lineList() + Chr(13) + Chr(10)
EndIf
Next i
ClearList(lineList())
Define outfilename.s = mainFile + "TRIMMED"
f = CreateFile(#PB_Any, outfilename)
If Not f
PrintN("Unable to create trimmed file: " + outfilename)
End 1
EndIf
WriteString(f, outfile)
CloseFile(f)
End 0
PB doesn't include procedures when they are not called by an other procedures or code. Unfortunately this results in procedures being included becaused they are called in other procedures that themselves are never called. In other words let say you had procedures A through F where A calls B and C calls D and E calls A and F calls C. If nowhere in your code do you ever call A, B, C, D, E, or F in the first place, they will all be included even though they were never used. Whew!DoubleDutch wrote:I think that PB doesn't include unused procedures in the exe. Is this just to shorten the source code?