What am I talking about? Having ASCII, Unicode, ThreadSafe and UnicodeThreadsafe versions of functions in the one Userlib file - no more UserLibUnicode etc subsystems needed.
I need more people to test this out, because I have a very basic knowledge of ASM - so I could be doing something wrong here.
What this code does is compile the Userlib 4 times (one time in each mode), it then modifies the Unicode/Threadsafe/UnicodeThreadsafe compiles to have the appropriate suffixes to ALL Public declarations (i.e. Procedures, variables) to separate completely the different compiles of the lib.
Then it merges them into the (unmodified) Ascii compile's folder, and then compiles it all into one userlib and puts it in the userlibraries folder.
So, it does NOT examine a function to see if it even uses strings - so the lib will be the same size as an Ascii userlib plus its subsystem counterparts.
I'm trying to figure out how to code this directly into TailBite, but I feel this should be tested properly first. (I also need time to figure out what does what in the TB sources!)
As usual with my test code, there are a bunch of constants at the top of the file. They more or less explain themselves.
The one that doesn't: #do64. Set it to 1 if you are compiling a 64 bit userlibrary, set it to 0 if you are doing an x86 one. Then edit the appropriate CompilerIf block. (Sorry non 64 bit people, I've left my 64 bit windows paths to PB x86 in)
WARNING: There is little error checking, and a LOT of debug info. Line 122 and 76 are the main culprits.
Single core users: If you want to use your computer while this runs (it takes a long while depending on the number of functions) you will want to add a few Delay calls, especially in the RenamePublicAndCopy procedure, as it maxes out one of my dual cores.
Your Tailbite NEEDS to be properly configured - including/especially the Asm source files folder, as this is the destination for the in work files (and where they will all be left once it is finished.
I have tested this with Droopy's Lib, and it seems to work well (but with 320 exported functions (there are more non exported ones) it takes quite a while - go-make-a-cup-of-coffee while)
If I've left anything out, don't shoot me please I'm a bit tired and headached..
Code: Select all
Procedure Error(errormsg.s)
MessageRequester("Error", errormsg, #MB_ICONERROR)
EndProcedure
#testfile = "H:\Documents\droopy svn\working copy\Droopy.pb"
#chm = "droopy"
#tb_libsrc = "C:\Documents and Settings\lex.XANDER\My Documents\TailBite Library Sources\";MUST BE EXACTLY THE SAME AS CONFIGURED IN TB
Global libname.s = RemoveString(GetFilePart(#testfile), ".pb")
#do64 = 0
CompilerIf #do64
#tailbitepath = "C:\Program Files\PureBasic4.30\TailBite\"
#fasm = "C:\Program Files\PureBasic4.30\Compilers\FAsm.exe"
#polib = "C:\Program Files\PureBasic4.30\Compilers\polib.exe"
#librarymaker = "C:\Program Files\PureBasic4.30\SDK\LibraryMaker.exe"
#purelibrariesfolder = "C:\Program Files\PureBasic4.30\PureLibraries\UserLibraries\"
CompilerElse
#tailbitepath = "C:\Program Files (x86)\PureBasic4.30\TailBite\"
#fasm = "C:\Program Files (x86)\PureBasic4.30\Compilers\FAsm.exe"
#polib = "C:\Program Files (x86)\PureBasic4.30\Compilers\polib.exe"
#librarymaker = "C:\Program Files (x86)\PureBasic4.30\SDK\LibraryMaker.exe"
#purelibrariesfolder = "C:\Program Files (x86)\PureBasic4.30\PureLibraries\UserLibraries\"
CompilerEndIf
RunProgram(#tailbitepath+"tailbite.exe", #DQUOTE$+#testfile+#DQUOTE$+" /DONT /KEEP /CHM:"+#chm, GetCurrentDirectory(),#PB_Program_Wait)
RunProgram(#tailbitepath+"tailbite.exe", #DQUOTE$+#testfile+#DQUOTE$+" /UCOD /DONT /KEEP /CHM:"+#chm+" /LIBN:"+libname+"_UNICODE", GetCurrentDirectory(),#PB_Program_Wait)
RunProgram(#tailbitepath+"tailbite.exe", #DQUOTE$+#testfile+#DQUOTE$+" /UCOD /THRD /DONT /KEEP /CHM:"+#chm+" /LIBN:"+libname+"_THREAD_UNICODE", GetCurrentDirectory(),#PB_Program_Wait)
RunProgram(#tailbitepath+"tailbite.exe", #DQUOTE$+#testfile+#DQUOTE$+" /THRD /DONT /KEEP /CHM:"+#chm+" /LIBN:"+libname+"_THREAD", GetCurrentDirectory(),#PB_Program_Wait)
Prototype RecurseProto(filename.s, subdir.s)
Procedure Recurse(dir.s, Func.RecurseProto,subdir.s, dirnum=1)
If subdir : subdir+"\" :EndIf
If ExamineDirectory(dirnum, dir, "*.*");enumerate thru to find Public's
While NextDirectoryEntry(dirnum)
If DirectoryEntryType(dirnum) = #PB_DirectoryEntry_Directory
If Left(DirectoryEntryName(dirnum), 1) <> ".";skip . and .. dirs.
Recurse(dir+"\"+DirectoryEntryName(dirnum), Func, subdir+DirectoryEntryName(dirnum), dirnum+1)
EndIf
Else
Func(dir+"\"+DirectoryEntryName(dirnum), subdir)
EndIf
Wend
FinishDirectory(dirnum)
EndIf
EndProcedure
Global NewList PublicFuncs.s()
Procedure MakePublicList(filename.s, subdir.s)
If LCase(GetExtensionPart(filename)) = "asm"
Debug filename
fileno = ReadFile(#PB_Any, filename)
If fileno
ReadStringFormat(fileno)
While Eof(fileno) = 0
line.s = ReadString(fileno)
If FindString(line, "Public", 1)
AddElement(PublicFuncs())
PublicFuncs() = RemoveString(line, "Public ")
EndIf
Wend
CloseFile(fileno)
EndIf
EndIf
EndProcedure
Global renamemode.s
Global NewList NewObjFiles.s()
Procedure RenamePublicAndCopy(filename.s, subdir.s)
If LCase(Right(filename, 3)) <> "asm" : ProcedureReturn : EndIf ; so we only process asm files.
destname.s = #tb_libsrc+libname+"\Functions\"+subdir+ReplaceString(GetFilePart(filename), libname+renamemode, libname)
destname = Left(destname, Len(destname)-4)+renamemode+".asm";replace .asm with _MODE.asm eg. FunctionName.asm -> FunctionName_UNICODE.asm
;Debug destname
AddElement(NewObjFiles())
NewObjFiles() = RemoveString(destname, #tb_libsrc+libname+"\")
Debug NewObjFiles()
;ProcedureReturn
sourcefile = ReadFile(#PB_Any, filename)
If sourcefile
destfile = CreateFile(#PB_Any, destname)
If destfile
While Eof(sourcefile) = 0
line.s = ReadString(sourcefile)
If FindString(LCase(line), "init",1);it could be an init func
line = ReplaceString(line, "PB_"+libname+"_Init", "PB_"+libname+"_Init"+renamemode, #PB_String_NoCase)
EndIf
If FindString(LCase(line), "end",1);it could be an end func
line = ReplaceString(line, "PB_"+libname+"_End", "PB_"+libname+"_End"+renamemode, #PB_String_NoCase)
EndIf
found = 0
For x = 0 To ListSize(PublicFuncs())-1
SelectElement(PublicFuncs(),x)
If MatchRegularExpression(x, line)
found = 1
Break
EndIf
Next x
If found
line = ReplaceString(line, ReplaceString(PublicFuncs(), libname, libname+renamemode), PublicFuncs()+renamemode)
;Debug line
EndIf
WriteStringN(destfile, line)
Wend
CloseFile(sourcefile)
CloseFile(destfile)
Else
Error("Could not open destfile"+#CRLF$+destname)
End
EndIf
Else
Error("Could not open sourcefile"+#CRLF$+filename)
End
EndIf
EndProcedure
Procedure CompileASM(filename.s, subdir.s)
If LCase(Right(filename, 3)) <> "asm" : ProcedureReturn : EndIf ; so we only process asm files.
fasm = RunProgram(#fasm, #DQUOTE$+filename+#DQUOTE$+" "+#DQUOTE$+ReplaceString(filename, ".asm", ".obj", #PB_String_NoCase)+#DQUOTE$, GetPathPart(filename), #PB_Program_Hide|#PB_Program_Open|#PB_Program_Read|#PB_Program_Error)
If fasm
While ProgramRunning(fasm)
If AvailableProgramOutput(fasm)
Debug ReadProgramString(fasm)
Else
errorstring.s = ReadProgramError(fasm)
If errorstring : Debug errorstring : EndIf
EndIf
Wend
Else
Error("Could not run fasm")
EndIf
EndProcedure
Recurse(#tb_libsrc+libname+"\functions", @MakePublicList(), "")
ForEach PublicFuncs()
Debug PublicFuncs()
Next
regex.s = "($|\]|,|:|\+| )"
renamemode = "_UNICODE"
ForEach PublicFuncs()
If Not CreateRegularExpression(ListIndex(PublicFuncs()), ReplaceString(PublicFuncs(), libname, libname+renamemode)+regex)
Error("could not create reg expression")
End
EndIf
Next
Recurse(#tb_libsrc+libname+renamemode+"\functions", @RenamePublicAndCopy(), "")
renamemode = "_THREAD"
ForEach PublicFuncs()
If Not CreateRegularExpression(ListIndex(PublicFuncs()), ReplaceString(PublicFuncs(), libname, libname+renamemode)+regex)
Error("could not create reg expression")
End
EndIf
Next
Recurse(#tb_libsrc+libname+renamemode+"\functions", @RenamePublicAndCopy(), "")
renamemode = "_THREAD_UNICODE"
ForEach PublicFuncs()
If Not CreateRegularExpression(ListIndex(PublicFuncs()), ReplaceString(PublicFuncs(), libname, libname+renamemode)+regex)
Error("could not create reg expression")
End
EndIf
Next
Recurse(#tb_libsrc+libname+renamemode+"\functions", @RenamePublicAndCopy(), "")
libnameObjFiles = OpenFile(#PB_Any, #tb_libsrc+libname+"\"+libname+"ObjFiles.txt")
If libnameObjFiles
FileSeek(libnameObjFiles, Lof(libnameObjFiles));so we can append
ForEach NewObjFiles()
WriteStringN(libnameObjFiles, ReplaceString(NewObjFiles(), ".asm", ".obj", #PB_String_NoCase))
Next
CloseFile(libnameObjFiles)
Else
Error("Could not open "+#tb_libsrc+libname+"\"+libname+"ObjFiles.txt")
End
EndIf
NewList DescFileContent.s()
descFile = OpenFile(#PB_Any, #tb_libsrc+libname+"\"+libname+".Desc")
If descFile
While Eof(descFile) = 0
AddElement(DescFileContent())
DescFileContent() = ReadString(descFile)
If FindString(DescFileContent(), "|", 1); we need to add the extra flags to this line
DescFileContent() = DescFileContent()+" | THREAD | UNICODE"
EndIf
Wend
FileSeek(descFIle, 0)
TruncateFile(descFile)
ForEach DescFileContent()
WriteStringN(descFile, DescFileContent())
Next
CloseFile(descFile)
Else
Error("Could not open "+#tb_libsrc+libname+"\"+libname+".Desc")
EndIf
Recurse(#tb_libsrc+libname+"\functions", @CompileASM(), "")
CompilerIf #do64
polib = RunProgram(#polib, "/MACHINE:X64 /OUT:"+#DQUOTE$+libname+".lib"+#DQUOTE$+" @"+#DQUOTE$+libname+"ObjFiles.txt"+#DQUOTE$, #tb_libsrc+libname, #PB_Program_Hide|#PB_Program_Open|#PB_Program_Read|#PB_Program_Error)
CompilerElse
polib = RunProgram(#polib, "/OUT:"+#DQUOTE$+libname+".lib"+#DQUOTE$+" @"+#DQUOTE$+libname+"ObjFiles.txt"+#DQUOTE$, #tb_libsrc+libname, #PB_Program_Hide|#PB_Program_Open|#PB_Program_Read|#PB_Program_Error)
CompilerEndIf
If polib
While ProgramRunning(polib)
If AvailableProgramOutput(polib)
Debug ReadProgramString(polib)
Else
errorstring.s = ReadProgramError(polib)
If errorstring : Debug errorstring : EndIf
EndIf
Wend
Else
Error("Could not run polib")
EndIf
RunProgram(#librarymaker, #DQUOTE$+libname+".Desc"+#DQUOTE$+" /TO "+#DQUOTE$+#purelibrariesfolder+#DQUOTE$+" /NOUNICODEWARNING", #tb_libsrc+libname, #PB_Program_Hide|#PB_Program_Wait)