C Backend Creating static libs (Windows)

Everything else that doesn't fall into one of the other PB categories.
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

C Backend Creating static libs (Windows)

Post by Justin »

I managed to create a simple static lib with pb, following are the steps:

Install mingw, the official installer is broken, go to:
https://winlibs.com/#download-release
Download the UCRT runtime MinGW-w64 version.
Unzip it, in my case i have it in c:\mingw64
Add c:\mingw64\bin to your environment path variable.
Check that it's working, open a command prompt and type gcc --version

Now the pb lib code:

Code: Select all

EnableExplicit

ProcedureDLL add(a.l, b.l)
	ProcedureReturn a + b
EndProcedure
I compiled as a shared library, whe need the c output, use this tool
https://www.purebasic.fr/german/viewtop ... 93#p361093

The generated purebasic.c is:

Code: Select all

#pragma warning(disable: 4024)
typedef long long quad;
typedef quad integer;
#define PB_INFINITY (1.0 / 0.0)
#define PB_NEG_INFINITY (-1.0 / 0.0)
typedef struct pb_array { void *a; } pb_array;
typedef struct pb_array2 { void *a; integer b[2]; } pb_array2;
typedef struct pb_array3 { void *a; integer b[3]; } pb_array3;
typedef struct pb_array4 { void *a; integer b[4]; } pb_array4;
typedef struct pb_array5 { void *a; integer b[5]; } pb_array5;
typedef struct pb_array6 { void *a; integer b[6]; } pb_array6;
typedef struct pb_array7 { void *a; integer b[7]; } pb_array7;
typedef struct pb_array8 { void *a; integer b[8]; } pb_array8;
typedef struct pb_array9 { void *a; integer b[9]; } pb_array9;
typedef struct pb_listitem { void *a; void *b; void *c;} pb_listitem;
typedef struct pb_list { void *a; pb_listitem *b; } pb_list;
typedef struct pb_mapitem { void *a; void *b; void *c;} pb_mapitem;
typedef struct pb_pbmap { pb_mapitem *a; } pb_pbmap;
typedef struct pb_map { pb_pbmap *a; } pb_map;
static integer s_s[]={0, -1};
#define M_SYSFUNCTION(a) a
#define M_PBFUNCTION(a) a
#define M_CDECL
typedef void TCHAR;
static integer SYS_BankerRound    (double i) { return i >= 0 ? i+0.5 : i-0.5; }
static quad    SYS_BankerRoundQuad(double i) { return i >= 0 ? i+0.5 : i-0.5; }
integer f_exitprocess_(integer) asm("ExitProcess");
integer f_heapcreate_(integer,integer,integer) asm("HeapCreate");
integer f_heapdestroy_(integer) asm("HeapDestroy");
integer f_getmodulehandle_(integer) asm("GetModuleHandleW");
static char *tls;
int PB_ExitCode=0;
integer PB_MemoryBase=0;
integer PB_Instance=0;
unsigned char *pb_datapointer;
void SYS_Quit();
M_SYSFUNCTION(void) SYS_InitPureBasic();
int PB_DEBUGGER_LineNumber=-1;
int PB_DEBUGGER_IncludedFiles=0;
char *PB_DEBUGGER_FileName=0;
integer f_add(int v_a,int v_b);

static integer ms_s[]={0,-1};
integer f_add(int v_a,int v_b) {
integer r=0;
r=(v_a+v_b);
goto end;
r=0;
end:
return r;
}
int PB_Compiler_Unicode=1;
int PB_Compiler_Thread=0;
int PB_Compiler_Purifier=0;
int PB_Compiler_Debugger=0;
int PB_Compiler_DPIAware=1;
int PB_Compiler_XPSkins=1;
int PB_ExecutableType=0;
void PB_EndFunctions() {
}
int WinMain(void *instance, void *prevInstance, void *cmdLine, int cmdShow) {
PB_Instance = f_getmodulehandle_(0);
PB_MemoryBase = f_heapcreate_(0,4096,0);
//
SYS_Quit();
}

void SYS_Quit() {
PB_EndFunctions();
f_heapdestroy_(PB_MemoryBase);
f_exitprocess_(PB_ExitCode);
}
Save it for example in c:\pb\purebasic.c
We need the obj file from this c file, open a command prompt and go to the pb compilers folder, and use its gcc compiler, adapt the paths to the files, command line thanks to tool from idle.
gcc -O0 -fsigned-char -Wno-incompatible-pointer-types -Wno-overflow -Wno-int-conversion -c -o c:\pb\PureBasic.obj c:\pb\purebasic.c

This will create c:\pb\PureBasic.obj

Now use the ar.exe tool from the mingw installation to create a static lib:
ar rcs c:\pb\PureBasic.a c:\pb\PureBasic.obj

This will create the static lib c:\pb\PureBasic.a

Now use it from pb:

Code: Select all

Import "purebasic.a"
	add(a.l, b.l) As "f_add"
EndImport

MessageRequester("", Str(add(2,3)))
I haven't tried more complex libs but i think it's a good start.
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

Strings don't work, trying this:

Code: Select all

EnableExplicit

ProcedureDLL add(a.s, b.s)
	ProcedureReturn a + b
EndProcedure
POLINK complains about not finding Sys_PushStringBasePosition and other string functions even if i use strings before calling the function. Any hints?
Btw i got better results using gcc from mingw to create the obj file.
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

Well it actually works, i was not compiling with the c backend when testing, so this worked:

Code: Select all

Import "purebasic.a"
	add.i(a.s, b.s) As "f_add"
EndImport

MessageRequester("", PeekS(add("w", "r")))
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

There's still a few issues to sort out. you will need to strip out the preamble or you'll end up with multiple definitions and you will also need to tell it which libs to include or not. The question is how to wrap the c or asm includes so they don't conflict then you can have very small archives of precompiled objects usable by pb with just the object code as the linking will be deferred to the client pb code.

If the intent is to make static libs for PB only, you just need to compile the code to an object rather than archiving the object. This is my idea for making precompiled object code, if you follow a pattern like this, assume the tools written to respond to the precompile instruction.

Code: Select all

CompilerIf #PB_Compiler_IsMainFile 
  
  ;!//precompile /tmp/foolib.o;
  
  ProcedureDLL foo_mul(a,b)
    ProcedureReturn a * b 
  EndProcedure 
  
  ProcedureDLL foo_div(a,b) 
    If (a And b)
      ProcedureReturn a / b 
    EndIf  
  EndProcedure  
  
CompilerElse 
  
  ImportC "/tmp/foolib.o"
    foo_mul(a,b)
    foo_div(a,b) 
  EndImport 
  
CompilerEndIf   
You will get a precompiled object which you can import when you include it in a project.

Code: Select all

XIncludeFile "foolib.pbi" 
Debug foo_mul(2,3)
The problem is how to gather the required pb libs for the linker and pass them on, they are included as comments in the asm or c output, when the pb compiler sees these it extracts the libs into the temp folder for linking. like
// Thread
// Object
// SimpleList
// System
// Memory
// Array
// Math
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

About wich libs to include, adding the commented names manually in the c code seems a good solution to me, is there any drawback using this method?

About stripping out the preamble can you elaborate? wich part can i remove? for example in the previous c code i posted

I think WinMain() and SYS_Quit() will be created in the main program anyways so i removed then and seems to work, what do you think?
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

ideally you would remove anything that's unreferenced by your code so for this example

Code: Select all

CompilerIf #PB_Compiler_IsMainFile 
    
  ProcedureDLL Foo_Mul(a,b) 
    ProcedureReturn a * b 
  EndProcedure 
  
  ProcedureDLL Foo_Div(a,b) 
    ProcedureReturn a / b        
  EndProcedure 
   
CompilerElse 
  
  ImportC "libFoo.o"
    Foo_Mul(a,b) As "f_foo_mul"
    Foo_Div(a,b) As "f_foo_div"
  EndImport    
  
CompilerEndIf   

the c output is

Code: Select all

 
 #pragma warning(disable: 4024)
typedef long long quad;
typedef quad integer;
#define PB_INFINITY (1.0 / 0.0)
#define PB_NEG_INFINITY (-1.0 / 0.0)
typedef struct pb_array { void *a; } pb_array;
typedef struct pb_array2 { void *a; integer b[2]; } pb_array2;
typedef struct pb_array3 { void *a; integer b[3]; } pb_array3;
typedef struct pb_array4 { void *a; integer b[4]; } pb_array4;
typedef struct pb_array5 { void *a; integer b[5]; } pb_array5;
typedef struct pb_array6 { void *a; integer b[6]; } pb_array6;
typedef struct pb_array7 { void *a; integer b[7]; } pb_array7;
typedef struct pb_array8 { void *a; integer b[8]; } pb_array8;
typedef struct pb_array9 { void *a; integer b[9]; } pb_array9;
typedef struct pb_listitem { void *a; void *b; void *c;} pb_listitem;
typedef struct pb_list { void *a; pb_listitem *b; } pb_list;
typedef struct pb_mapitem { void *a; void *b; void *c;} pb_mapitem;
typedef struct pb_pbmap { pb_mapitem *a; } pb_pbmap;
typedef struct pb_map { pb_pbmap *a; } pb_map;
static integer s_s[]={0, -1};
#define M_SYSFUNCTION(a) a
#define M_PBFUNCTION(a) a
#define M_CDECL
typedef void TCHAR;
static integer SYS_BankerRound    (double i) { return i >= 0 ? i+0.5 : i-0.5; }
static quad    SYS_BankerRoundQuad(double i) { return i >= 0 ? i+0.5 : i-0.5; }
integer f_heapcreate_(integer,integer,integer) asm("HeapCreate");
integer f_heapdestroy_(integer) asm("HeapDestroy");
integer f_getmodulehandle_(integer) asm("GetModuleHandleW");
static char *tls;
int PB_ExitCode=0;
integer PB_MemoryBase=0;
integer PB_Instance=0;
unsigned char *pb_datapointer;
void SYS_Quit();
M_SYSFUNCTION(void) SYS_InitPureBasic();
M_SYSFUNCTION(void) SYS_CleanupThreadCallbacks();
static void pb_dllinit();
int PB_DEBUGGER_LineNumber=-1;
int PB_DEBUGGER_IncludedFiles=0;
char *PB_DEBUGGER_FileName=0;
integer f_foo_mul(integer v_a,integer v_b);
integer f_foo_div(integer v_a,integer v_b);

static integer ms_s[]={0,-1};
integer f_foo_mul(integer v_a,integer v_b) {
integer r=0;
r=(v_a*v_b);
goto end;
r=0;
end:
return r;
}
integer f_foo_div(integer v_a,integer v_b) {
integer r=0;
r=(v_a/v_b);
goto end;
r=0;
end:
return r;
}
char PB_OpenGLSubsystem=1;
int PB_Compiler_Unicode=1;
int PB_Compiler_Thread=0;
int PB_Compiler_Purifier=0;
int PB_Compiler_Debugger=0;
int PB_Compiler_DPIAware=0;
int PB_Compiler_XPSkins=0;
int PB_ExecutableType=0;
void PB_EndFunctions() {
}
int DllMain(integer instance, int reason, void *reserved) {
switch (reason) {
case 1:
PB_Instance=instance;
pb_dllinit();
break;
case 2:
break;
case 0:
SYS_Quit();
break;
case 3:
break;
} return 1; }
static void pb_dllinit() {
PB_MemoryBase = f_heapcreate_(0,4096,0);
//precompile /tmp/foolib.o;
return;
}

void SYS_Quit() {
PB_EndFunctions();
SYS_CleanupThreadCallbacks();
f_heapdestroy_(PB_MemoryBase);
}


of which you only this need to compile the following with
gcc -fsigned-char -o c:\srcpath\libfoo.o c:\srcpath\purbasic.c -c

Code: Select all

 
#pragma warning(disable: 4024)
typedef long long quad;
typedef quad integer;

integer f_foo_mul(integer v_a,integer v_b);
integer f_foo_div(integer v_a,integer v_b);

static integer ms_s[]={0,-1};
integer f_foo_mul(integer v_a,integer v_b) {
integer r=0;
r=(v_a*v_b);
goto end;
r=0;
end:
return r;
}
integer f_foo_div(integer v_a,integer v_b) {
integer r=0;
r=(v_a/v_b);
goto end;
r=0;
end:
return r;
}



Then you can include it in your project or share it as an object or archive it.

The tricky part once you've stripped the c output down is how to tell the pbcompiler using the object which libs are required.

will continue ...
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

with string

Code: Select all

CompilerIf #PB_Compiler_IsMainFile 
    
  ProcedureDLL Foo_Mul(a,b) 
    ProcedureReturn a * b 
  EndProcedure 
  
  ProcedureDLL Foo_Div(a,b) 
    ProcedureReturn a / b        
  EndProcedure 
  
  ProcedureDLL.s Foo_Add(a.s, b.s)
	  ProcedureReturn a + b
  EndProcedure
    
CompilerElse 
  
  ImportC "libFoo.o"
    Foo_Mul(a,b) As "f_foo_mul"
    Foo_Div(a,b) As "f_foo_div"
    Foo_Add(a,b) As "f_foo_add"   
  EndImport    
  
CompilerEndIf   

the c code compiled as

gcc -fsigned-char -o c:\srcpath\libfoo.o c:\srcpath\purbasic.c -c

Code: Select all

#pragma warning(disable: 4024)
typedef long long quad;
typedef quad integer;

static integer s_s[]={0, -1};
#define M_SYSFUNCTION(a) a
typedef void TCHAR;
static char *tls;

M_SYSFUNCTION(void) SYS_CopyString(const void *String);
M_SYSFUNCTION(void) SYS_FastAllocateString4(TCHAR **Address, const TCHAR *String);
M_SYSFUNCTION(void) SYS_FreeString(TCHAR *String);
extern void *PB_StringBase;
extern integer PB_StringBasePosition;
M_SYSFUNCTION(void) SYS_InitString(void);
M_SYSFUNCTION(void) SYS_FreeStrings(void);
M_SYSFUNCTION(void) SYS_PushStringBasePosition(void);
M_SYSFUNCTION(integer) SYS_PopStringBasePosition(void);
M_SYSFUNCTION(integer) SYS_PopStringBasePositionUpdate(void);
M_SYSFUNCTION(void *) SYS_PopStringBasePositionValue(void);
M_SYSFUNCTION(void *) SYS_PopStringBasePositionValueNoUpdate(void);
M_SYSFUNCTION(integer) SYS_GetStringBasePosition(void);
M_SYSFUNCTION(void) SYS_SetStringBasePosition(integer Position);
M_SYSFUNCTION(integer) SYS_StringBasePositionNoPop(void);
M_SYSFUNCTION(char *) SYS_GetStringBase(void);

integer f_foo_mul(integer v_a,integer v_b);
integer f_foo_div(integer v_a,integer v_b);
void* f_foo_add(void* v_a,void* v_b);

static integer ms_s[]={0,-1};
integer f_foo_mul(integer v_a,integer v_b) {
integer r=0;
r=(v_a*v_b);
goto End;
r=0;
End:
return r;
}
integer f_foo_div(integer v_a,integer v_b) {
integer r=0;
r=(v_a/v_b);
goto End;
r=0;
End:
return r;
}
void* f_foo_add(void* v_a,void* v_b) {
void* r=0;
SYS_FastAllocateString4(&v_a,v_a);
SYS_FastAllocateString4(&v_b,v_b);
SYS_PushStringBasePosition();
SYS_CopyString(v_a);
SYS_CopyString(v_b);
r=SYS_PopStringBasePositionValueNoUpdate();
goto End;
SYS_PushStringBasePosition();
SYS_CopyString("\0");
r=SYS_PopStringBasePositionValueNoUpdate();
End:
SYS_FreeString(v_a);
SYS_FreeString(v_b);
return r;
}
The test code

Code: Select all

XIncludeFile "foolib.pbi" 

Debug Foo_Mul(2,3)

Debug PeekS(Foo_add("hello","World")) 
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

I will try it later, all small tests i've done work except a very big and complicated lib that crashes (invalid write memory i think) when creating a control with CreateWindowEx but a small test doing that works, i'll investigate it further, the commented lib names tip helped a lot, thanks.
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

Check your PM's, I've sent you a modification of the compiler flag tool to automate the object creation.
you just need to set the precompile flag as the last flag and specify the location to save it.

Code: Select all

CompilerIf #PB_Compiler_IsMainFile 
  
  !//precompile e:/idle/pbstuff/libfoo.o;
  
  ProcedureCDLL Foo_Mul(a,b) 
    ProcedureReturn a * b 
  EndProcedure 
  
  ProcedureCDLL Foo_Div(a,b) 
    ProcedureReturn a / b        
  EndProcedure 
  
  ProcedureCDLL.s Foo_Add(a.s, b.s)
	  ProcedureReturn a + b
  EndProcedure
  
  CompilerIf #PB_Compiler_Debugger 
    Debug Foo_Mul(2,3)
    Debug Foo_add("hello","World") 
  CompilerEndIf   
      
CompilerElse 
  
  ImportC "libFoo.o"
    Foo_Mul(a,b) As "f_foo_mul"
    Foo_Div(a,b) As "f_foo_div"
    Foo_Add(a,b) As "f_foo_add"   
  EndImport    
  
CompilerEndIf   

Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

Hi idle, thanks for the tool i will make more tests with it later.

I spotted the bug in my big lib, it was using a module with automatic initialization, when using it as an include the code in the module section is called and the module is initialized but in a lib doesn't.

No code is executed in a lib, you need an init function and call it from the main program to init the lib, adding it solved the problem. It's a complex lib that register classes, uses hooks and subclasses the main window and it's working perfect.
User avatar
ChrisR
Addict
Addict
Posts: 1154
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: C Backend Creating static libs (Windows)

Post by ChrisR »

idle wrote: Tue Jun 07, 2022 12:16 am The tricky part once you've stripped the c output down is how to tell the pbcompiler using the object which libs are required.
I don't know if it can be of any help, I do not know much about static libs.
But it is possible to have the list of PB libraries by doing the same thing as your include c headers and modify compiler flags with inline c
By using a fake Polink.exe and listing the parameters, as in the example I had quickly done here.
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

@ChrisR
Yes thanks I made a fake polink to see what libs are being used.


I think the easiest solution is to just archive it like Justin wanted in the 1st place, I can do it later today or tomorrow.
What I really wanted to do was make a modular build system so the libs only got extracted and linked with the project
so it would need a compiler file. The only difference will be that your static lib could be MB's rather than kb's but the overall effect will be the same as the linker will only link what's required from the libs into your executable.
User avatar
idle
Always Here
Always Here
Posts: 5098
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C Backend Creating static libs (Windows)

Post by idle »

@Justin and ChrisR
check you PM's have sent through a test seems to work but
User avatar
ChrisR
Addict
Addict
Posts: 1154
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: C Backend Creating static libs (Windows)

Post by ChrisR »

Hi idle,
Strange, I didn't receive an email for your PM, However Email is checked in my setting: "Someone sends you a private message" !
Never mind here, I saw your post.

I tested on my Windows 10 x64 with success. The Static lib was well created and I can use it then:

Code: Select all

ImportC "E:\PureBasic\Portable\PureBasic_Windows_beta10_X64_6.00\FakeCompilers\libfoo.lib"
  Foo_Mul(a,b) As "f_foo_mul"
  Foo_Div(a,b) As "f_foo_div"
  Foo_Add(a,b) As "f_foo_add"   
EndImport

Debug Foo_Mul(2,3)
Debug PeekS(Foo_add("Hello ","World"))
Is there any way to avoid PeekS for ProcedureCDLL.s ?

Otherwise, tiny thing seen:
Gcc.pb

Code: Select all

libname = GetPathPart(libname) + GetFilePart(libname,#PB_FileSystem_NoExtension) + ".o"   ; In case there is a dot in the path
;pos = FindString(libname,".")
;libname = Left(libname,pos) + "o"
Polink.pb

Code: Select all

If Not bset
  Debug "reset"
  StartDir = RTrim(StartDir, #Cdir)   ; Optional, to avoid double backslash
.

Code: Select all

If ReadFile(0,"makestatic.txt")
  cmd = ReadString(0)
  name.s = GetFilePart(cmd,#PB_FileSystem_NoExtension) + ".o "    ; Instead of pos=FindString(name,".")...
  cmd = "/OUT:" + cmd + " " + name + " "
  CloseFile(0)
Justin
Addict
Addict
Posts: 832
Joined: Sat Apr 26, 2003 2:49 pm

Re: C Backend Creating static libs (Windows)

Post by Justin »

Strange, in my case it displays all the messages and out path but the library is not created.
Post Reply