PureLibrary Creator - PB 6.20

Share your advanced PureBasic knowledge/code with the community.
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

i think fred will add a keyword
I suggested “Public”, but why not “External”?

constants are rarely isolated, most of them are (or should be) in an enumeration block
you can therefore put “external” before an enumeration block
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Lib - PB 6.20

Post by ChrisR »

I think that having one or more Externals-EndExternals (or Resident-EndResident) blocks is probably the most flexible, allowing you to add whatever you want, constants, enumerations, structures, macros... between the 2 tags.
And so, no need to search for every keyword: Structure-EndStructure, Enumeration-EndEnumeration,...

To rebuild the resident and avoid the message: Structure already declared: xxx (in a resident file), it must be removed first or saved.
Also, I think it would be better to compile the resident first and if compiled successfully, compile the library with the lines between Externals, EndExternals removed in tmp.pb, to avoid the same message, already declared.
User avatar
le_magn
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Aug 24, 2005 12:11 pm
Location: Italia

Re: Lib - PB 6.20

Post by le_magn »

I have a question, in the past the creation of personal libraries was done via tailbite or other systems, but the end result was always the same, that is, after a few years these libraries no longer worked on the new versions of purebasic, see the problem symbol case, all Gnozal libraries with time became obsolete and incompatible, my question is, this new feature integrated to purebasic how will it prevent the same thing from happening? Because otherwise I can't see the benefits, personally I am terrified to use libraries created by third parties given the very bad experience I had in the past with the ones from Gnozal...
Image
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

I see no problem if the source is available but I would be reluctant to use a 3rd party lib without it.
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

External is a bit more familiar.
And using blocks beginresidents endresidents would make a lot more sense
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Lib - PB 6.20

Post by ChrisR »

idle wrote: Sun Dec 15, 2024 6:53 pm I see no problem if the source is available but I would be reluctant to use a 3rd party lib without it.
I fully agree :)

I've made a few changes so that the resident and library files can be rebuilt without error: xxx already declared: yyy (in a resident file)
And using case-sensitive blocks Residents-EndResidents

Code: Select all

;Residents
#Hello = 10

Structure Name
  String.s
  Value.i
EndStructure
;.....
;EndResidents

Code: Select all

EnableExplicit

Structure residents 
  *residents 
  len.i 
EndStructure   

#separateurs=" =:;,()[]{}+-/*.\<>?%|&"+#CR$+#LF$+Chr(34)

Procedure.s lirefic(nom.s,nobom=1)
  Protected txt.s,n,sf
  n=ReadFile(-1,nom)
  sf=ReadStringFormat(n)
  txt=ReadString(n,sf|#PB_File_IgnoreEOL)
  CloseFile(n)   
  ProcedureReturn txt
EndProcedure

Procedure ecrirefic(nom.s,txt.s,format=#PB_UTF8)
  Protected n
  If FileSize(nom)>=0:DeleteFile(nom):EndIf
  n=OpenFile(-1,nom,format):WriteString(n,txt,format):CloseFile(n)   
EndProcedure

Procedure.s Stringparse(t.s,before.s,after.s,pi=0)
  Protected pf
  pi=FindString(t,before,pi+1)
  If pi=0:ProcedureReturn:EndIf 
  pf=FindString(t,after,pi):If pf=0:pf=Len(t):EndIf
  pi+Len(before)
  ProcedureReturn Mid(t,pi,pf-pi)
EndProcedure

Procedure FindStringRev(t.s,find.s,p=0,mode=#PB_String_CaseSensitive)
  Protected l=Len(find)
  If p=0:p=Len(t):EndIf
  Repeat
    If p=0 Or Mid(t,p,l)=find:Break:EndIf
    p-1
  ForEver
  ProcedureReturn p
EndProcedure

Procedure.i FindStringww(txt.s,mot.s,p=1,mode=0); findstring whole word
  Repeat
    p=FindString(txt,mot,p,mode):If p=0:Break:EndIf
    If FindString(#separateurs,Mid(txt,p-1,1))>0 And FindString(#separateurs,Mid(txt,p+Len(mot),1))>0:ProcedureReturn p:EndIf
    p+Len(mot)
  ForEver
EndProcedure

Procedure.s ReplaceStringww(txt.s,mot.s,rep.s,p=1,mode=0); ReplaceString whole word
  Protected lm,lr
  
  lm=Len(mot)
  lr=Len(rep)
  Repeat
    p=FindStringww(txt,mot,p+1,mode):If p=0:Break:EndIf
    txt=Left(txt,p-1)+rep+Mid(txt,p+lm)
    p+lm-lr+1
  ForEver
  ProcedureReturn txt
EndProcedure

;####################################################################################################################################################

Procedure.s codefiltre(txt.s) ;-------- crée une version du code contenant des ";"  à la place des commentaires et des """ dans les strings pour faciliter l'analyse du code
  Protected i,n,ch,co,c.w
  
  n=Len(txt)
  Dim t.w(n-1)
  CopyMemory(@ txt,@ t(0),n*2)
  For i=0 To n-1
    c=t(i)
    Select c
      Case 34:ch=~ch
      Case 59:co=1
      Case 13:co=0
    EndSelect      
    If ch:c=34 :EndIf
    If co:c=59:EndIf
    t(i)=c
  Next
  CopyMemory(@ t(0),@ txt,n*2)
  ProcedureReturn txt
EndProcedure

Procedure.s listeparam(List p.s(),txt.s,pi=1) ;-------- met les parametres de proc dans une liste et retourne un string contenant les parametres 
  Protected i,np,ch,pd,pdc,finc,c.s
  ClearList(p())
  
  For i=pi To Len(txt)
    c=Mid(txt,i,1)
    If ch=0
      If c="(":np+1:If np=1:pd=i:pdc=i:EndIf:EndIf
      If c=")":np-1:If np=0:finc=1:EndIf:EndIf
      If c="," And np=1:finc=1:EndIf
      If finc:
        AddElement(p()):p()=Trim(Mid(txt,pdc+1,i-pdc-1)):pdc=i
        If finc And np=0:Break:EndIf
        finc=0
      EndIf
    EndIf
    If c=#DQUOTE$:ch=~ch:EndIf
  Next
  ;ForEach p():Debug p():Next:Debug "---"+Mid(txt,pd+1,i-pd-1):End
  ProcedureReturn Mid(txt,pd+1,i-pd-1)
EndProcedure

Procedure.s paramx(List p.s(), n, nval,declaration) ; <- retourne les parametres sous forme litérales
  Protected i,r.s,v.s
  ForEach p()
    i+1:If i>n:Break:EndIf
    v=StringField(p(),Bool(i>nval)+1,"=")
    If declaration=0 And FindString(v,"("):v=Stringparse(v," ","(")+"()":EndIf
    r+","+v
  Next
  ProcedureReturn Mid(r,2)
EndProcedure

Procedure.s parametres_optionels(fic.s,*Residents.residents) ;-------- gestion des parametres optionnels du fichier fic (il y a tjrs la possibilité de les gérer nous même (si on met les versions numerotées)
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows :#lgsep=#CRLF$
    CompilerCase #PB_OS_Linux   :#lgsep=#LF$
    CompilerCase #PB_OS_MacOS   :#lgsep=#CR$
  CompilerEndSelect
  
  Protected i,p,pp,pi,pl,pf,num,npo,npf,ok,rl,lr
  Protected.s param,paramo,nom,nomnum,proccode,proccodei,remplace,nums,ret,deb,QuickHelp,ts
  NewList l.s() ; liste parametre
  NewList ll.s(); liste parametre
  
  pf=1
  Repeat
    ;========================================== recup Residents
    pi=FindString(fic,";Residents",pf):If pi=0:Break:EndIf
    pf=FindString(fic,";EndResidents",pi+10)+13:If pf=13:Break: EndIf   
    ts=Mid(fic,pi,pf-pi)
    fic=ReplaceString(fic,ts,"") ; the extracted code must be deleted in fic to avoid the error: xxx already declared: xxx (in a resident file)
    pf=pi
    ts=Mid(ts,11,Len(ts)-10-13)  ; extracted code without both flags
    lr=rl 
    rl+PokeS(*Residents\residents+rl,ts,Len(ts))
    *residents\len=rl
  ForEver
  
  pf=1
  Repeat     
    ;========================================== recup de la ProcedureDLL
    pi=FindString(fic,"ProcedureDLL",pf):If pi=0:Break:EndIf
    pf=FindString(fic,"EndProcedure",pi)+12:If pf=12:Break:EndIf
    proccode=Mid(fic,pi,pf-pi)
    proccodei=proccode
    
    pl=FindString(proccode,#lgsep)
    nom=Trim(Stringparse(proccode," ","("))
    ok=1:For i=1 To Len(nom):If FindString(#separateurs,Mid(nom,i,1)):ok=0:EndIf:Next:If ok=0 Or nom="":Continue:EndIf
    num=Val(Right(nom,1))
    param=listeparam(l(),proccode)
    
    ;========================================== QuickHelp 
    QuickHelp=" QuickHelp "+nom+"("+param+") - "
    p=FindStringRev(fic,#lgsep,pi-3)+Len(#lgsep)
    If Mid(fic,p,1)=";":p=p+1:Else:p=pi:QuickHelp=";"+QuickHelp+#lgsep:EndIf
    fic =Left(fic ,p-1)+QuickHelp+Mid(fic ,p)
    pi+Len(QuickHelp)
    pf+Len(QuickHelp)
    
    If num:Continue:EndIf; on abandonne si n° à la 1er occurence
    
    ;========================================== suppression de ses valeurs par defaut
    paramo=param
    While FindString(paramo,"="):paramo=ReplaceString(paramo,"="+Stringparse(paramo+",","=",","),""):Wend
    proccode=ReplaceString(proccode,param,paramo)
    
    npo=CountString(param,"="); !!!
    If npo=0:Continue:EndIf   ; on abandonne si nombre de param optionel=0
    
    ;========================================== creation des versions numérotées 
    npf=ListSize(l())-npo
    
    deb=Left(proccode,FindString(proccode,"(")-1)
    nomnum=nom+Str(npo+1)
    remplace=ReplaceString(proccode,nom,nomnum,0 ,1,1)
    If FindString(proccode,"ProcedureReturn")>0:ret="ProcedureReturn ":Else:ret="":EndIf
    For i=npo To 1 Step -1
      If i>1:nums=Str(i):Else:nums="":EndIf
      remplace+#lgsep+deb+nums+"("+paramx(l(),npf+i-1,100,1)+")"+#lgsep+ret+nomnum+"("+paramx(l(),npf+npo,npf+i-1,0)+")"+#lgsep+"EndProcedure"
    Next
    
    fic =Left(fic ,pi-1)+remplace+Mid(fic ,pf)
    pf+Len(remplace)-Len(proccodei)
    
    ;========================================== remplacement des appels de la ProcedureDLL (et DeclareDLL) par leur version numérotée
    p=0
    Repeat
      p=FindStringww(fic,nom,p+1,#PB_String_NoCase):If p=0:Break:EndIf
      pp=FindStringRev(fic,#lgsep,p)+Len(#lgsep)
      If Mid(fic,pp,12)="ProcedureDLL" Or Mid(fic,pp,1)=";" Or Mid(fic,p+Len(nom),1)<>"(":Continue:EndIf  ; !!!     
      
      paramo=listeparam(ll(),fic,p)
      If Bool(Mid(fic,pp,10)="DeclareDLL")
        param=paramx(l(),npf+npo,100,1)
      Else
        paramx(l(),npf+npo,npf,0)
        param=paramo       
        For i=ListSize(ll()) To ListSize(l()) -1
          SelectElement(l(),i)
          param+","+Mid(l(),FindString(l(),"=")+1)
        Next
      EndIf
      
      nums=Str(npo+1)
      fic=ReplaceString(fic,nom,nom+nums,#PB_String_NoCase,p,1)
      fic=ReplaceString(fic,paramo,param,#PB_String_NoCase,p,1)
      If p<pf:pf+Len(nums)+Len(param)-Len(paramo):EndIf
    ForEver
  
ForEver

ProcedureReturn fic
EndProcedure

;- Main
Define.s code,rescode,fic,chd,chs,s,libname,Flag,msg,pg.i
Global residents.residents 

chs=ProgramParameter(0)
If FileSize(chs) 
  residents\residents=AllocateMemory(FileSize(chs))
EndIf 

fic=GetFilePart(chs)
code=parametres_optionels(lirefic(chs),@residents)

If residents\len > 0  ;-------- write resident and compile
  chd=GetTemporaryDirectory()+"tmp.res"
  libname=Left(fic,FindString(fic,".")-1)+".res" 
  rescode = PeekS(residents\residents,residents\len>>1) 
  Debug libname+#CRLF$+"==>"+#CRLF$+rescode+#CRLF$+"----------"
  
  ecrirefic(chd,rescode)
  
  pg=RunProgram(#PB_Compiler_Home+"Compilers\pbcompiler",chd+" /IGNORERESIDENT "+libname+" /RESIDENT "+#PB_Compiler_Home+"\residents\"+libname,"", #PB_Program_Open|#PB_Program_Read)
  While ProgramRunning(pg):If AvailableProgramOutput(pg):s+ReadProgramString(pg)+#CRLF$:EndIf:Wend:CloseProgram(pg)
  If s > ""
    MessageRequester("Create Resident Error:","Resident Source File: %Temp%\tmp.res"+#CRLF$+#CRLF$+s)
    End 1
  Else
    msg="Your Resident has been Created as "+#DQUOTE$+libname+#DQUOTE$+#CRLF$+#CRLF$
  EndIf
EndIf 

;-------- write library and compile
chd=GetTemporaryDirectory()+"tmp.pb"
libname=Left(fic,FindString(fic,".")-1)

ecrirefic(chd,code)
Debug libname+#CRLF$+"==>"+#CRLF$+code+#CRLF$+"----------"

pg = FindString(code,"; IDE Options = PureBasic",2)   ;Get compiler options, If Save Settings is to the end of the source file
If pg
  code=Mid(code,pg);+50
  If FindString(code,"EnableThread",2) :Flag+" /THREAD"       :EndIf
  If FindString(code,"EnableOnError",2):Flag+" /LINENUMBERING":EndIf
  If FindString(code,"DPIAware",2)     :Flag+" /DPIAWARE"     :EndIf
  If FindString(code,"EnableXP",2)     :Flag+" /XP"           :EndIf
  If FindString(code,"EnableAdmin",2)  :Flag+" /ADMINISTRATOR":EndIf
  If FindString(code,"EnableUser",2)   :Flag+" /USER"         :EndIf
  If FindString(code,"DllProtection",2):Flag+" /DLLPROTECTION":EndIf
  If FindString(code,"SharedUCRT",2)   :Flag+" /UCRT"         :EndIf
EndIf
Flag+" /OPTIMIZER /PURELIBRARY /OUTPUT "
Debug "Flag: "+Flag

pg=RunProgram(#PB_Compiler_Home+"Compilers\pbcompilerc",chd+Flag+libname,"", #PB_Program_Open|#PB_Program_Read)
While ProgramRunning(pg):If AvailableProgramOutput(pg):s+ReadProgramString(pg)+#CRLF$:EndIf:Wend:CloseProgram(pg)
If s>""
  MessageRequester("Create Lib Error:","Library Source File: %Temp%\tmp.pb"+#CRLF$+#CRLF$+s)
  End 1
Else
  If msg
    msg+"Your Library has been Created as "+#DQUOTE$+libname+#DQUOTE$+#CRLF$+#CRLF$+"Restart your Compiler to use them"
  Else
    msg+"Your Library has been Created as "+#DQUOTE$+libname+#DQUOTE$+#CRLF$+#CRLF$+"Restart your Compiler to use it"
  EndIf
  MessageRequester("Create Lib",msg)
EndIf
Last edited by ChrisR on Mon Dec 16, 2024 6:47 pm, edited 6 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lib - PB 6.20

Post by idle »

Much easier thanks
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

in relation to structure and constant/enumeration :
I could automatically determine what is used by “procedureDLL”.

I don't know if you can do the same with prototypes and interfaces (I don't know them, I don't use them)
Fred
Administrator
Administrator
Posts: 18153
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Lib - PB 6.20

Post by Fred »

You can use the /IGNORERESIDENT flag to ignore the load of a resident (instead of deleting it)
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Lib - PB 6.20

Post by ChrisR »

Fred wrote: Mon Dec 16, 2024 3:03 pm You can use the /IGNORERESIDENT flag to ignore the load of a resident (instead of deleting it)
Oh yes, thanks :)
I've updated the above code with also the compiler options for the Library, If Save Settings is to the end of the Library source file.
Exept IncludeFile, this nice tool must be pretty complete now I guess.
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

Fred should add a keyword (external), or see if we can do without it (see my previous post).

moreover, resident management must be done in a separate pass. here, as it is, procedureDLL must be positioned after “external”.
for includes, I think it's simple (a recursive function that incurs includes)

I'll make a v2 with your modifications shortly
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Lib - PB 6.20

Post by ChrisR »

pf shadoko wrote: Mon Dec 16, 2024 4:52 pm moreover, resident management must be done in a separate pass. here, as it is, procedureDLL must be positioned after “external”.
for includes, I think it's simple (a recursive function that incurs includes)
Yes indeed, add pf=1 at the end of pi=FindString(fic,";Residents",pf) : If pi : ..... : pf=1 : Else
should do it, but a second loop with position would be better.
While waiting for your v2, I updated on the post above

#
pf shadoko wrote: Mon Dec 16, 2024 4:52 pm Fred should add a keyword (external), or see if we can do without it (see my previous post).
Yes, I'd seen it and it would be a good thing.
Maybe you can anticipate it so you don't have to update it for the next beta, with something like:

Code: Select all

#HandleExternal=#True
CompilerIf #HandleExternal
  pi=FindString(fic,";Residents",pf)
Well, that was just my 2 cents, I'll just leave you to prepare v2 :)

#
pf shadoko wrote: Thu Dec 12, 2024 7:01 pm je trouve plus explicite comme ça: là, on connait les valeurs par défaut
Oui, finalement je suis d'accord, c'est plus explicite en effet avec les valeurs par défaut
dcr3
Enthusiast
Enthusiast
Posts: 181
Joined: Fri Aug 04, 2017 11:03 pm

Re: Lib - PB 6.20

Post by dcr3 »

Although this is a great tool, this should be taken into consideration.

List, Array and Map parameters can cause issues when exported with ProcedureDLL.

Otherwise the tool will hang.
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Lib - PB 6.20

Post by pf shadoko »

no, these limitations no longer apply
Have you tested it?
morosh
Enthusiast
Enthusiast
Posts: 329
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: Lib - PB 6.20

Post by morosh »

Trying your tool right now (creating Createlib.exe then testing it with your example), I got:
Error: Linker
Error at line 50: Can't create the library file:
c:\Program Files\PureBasic\purelibrairies/userlibrairies/Ext2D
with both \ and /
what it could be?
edit:
Trying also Fred example in a command line window, I got:
E:\my_data\prog\pb\TEST>"C:\Program Files\PureBasic\Compilers\pbcompilerc" test.pb --purelibrary --output test
PureBasic 6.20 Beta 1 - C Backend (Windows - x64)
Compiling test.pb
Loading external libraries...
Starting compilation...
69 lines processed.
Creating threaded version of the PureLibrary...
Starting compilation...
69 lines processed.
Error: Linker
Error at line 65: Can't create the library file:
C:\Program Files\PureBasic\purelibraries/userlibraries/Test
thanks
PureBasic: Surprisingly simple, diabolically powerful
Post Reply