Page 1 of 2
C Backend Creating static libs (Windows)
Posted: Sun Jun 05, 2022 12:27 pm
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.
Re: C Backend Creating static libs (Windows)
Posted: Sun Jun 05, 2022 4:05 pm
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.
Re: C Backend Creating static libs (Windows)
Posted: Sun Jun 05, 2022 5:17 pm
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")))
Re: C Backend Creating static libs (Windows)
Posted: Mon Jun 06, 2022 3:31 am
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
Re: C Backend Creating static libs (Windows)
Posted: Mon Jun 06, 2022 9:02 pm
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?
Re: C Backend Creating static libs (Windows)
Posted: Tue Jun 07, 2022 12:16 am
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 ...
Re: C Backend Creating static libs (Windows)
Posted: Tue Jun 07, 2022 2:11 am
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"))
Re: C Backend Creating static libs (Windows)
Posted: Tue Jun 07, 2022 2:28 pm
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.
Re: C Backend Creating static libs (Windows)
Posted: Wed Jun 08, 2022 1:38 am
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
Re: C Backend Creating static libs (Windows)
Posted: Wed Jun 08, 2022 11:26 am
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.
Re: C Backend Creating static libs (Windows)
Posted: Wed Jun 08, 2022 5:36 pm
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.
Re: C Backend Creating static libs (Windows)
Posted: Wed Jun 08, 2022 9:16 pm
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.
Re: C Backend Creating static libs (Windows)
Posted: Fri Jun 10, 2022 12:30 am
by idle
@Justin and ChrisR
check you PM's have sent through a test seems to work but
Re: C Backend Creating static libs (Windows)
Posted: Fri Jun 10, 2022 3:12 pm
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)
Re: C Backend Creating static libs (Windows)
Posted: Fri Jun 10, 2022 8:37 pm
by Justin
Strange, in my case it displays all the messages and out path but the library is not created.