libk  Check-in [e794c5edef]

Overview
Comment:add a bunch of code, port the header mechanism to gpp; add a fuckton of definitions and compatibility header code; notably, add core function kstop and x86.lin.{32,64} impl. update docs accordingly
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e794c5edef00e56053792a71a97b4e5b24560627fba9624f86cc00e5357911ce
User & Date: lexi on 2019-06-29 09:31:50
Other Links: manifest | tags
Context
2019-07-26
09:51
major update. fix ridiculous old type size determination mechanism. mmap is still broken and i'm not sure why; the syscall does not appear to be going through correctly - see posix_mmap, kmheapa, and kcore/testbin.exe.fn check-in: 6479e060a3 user: lexi tags: trunk
2019-06-29
09:31
add a bunch of code, port the header mechanism to gpp; add a fuckton of definitions and compatibility header code; notably, add core function kstop and x86.lin.{32,64} impl. update docs accordingly check-in: e794c5edef user: lexi tags: trunk
2019-06-28
04:52
fix stupid bullshit that was including boot.o in libk.a which already HAS the runtime shit ohmyGOD check-in: de8e1eb5d2 user: lexi tags: trunk
Changes

Added grammar/grammar.gpp version [89005aac7c].





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define flag -#1
#define		exec x
#define mode flag(#1)#2
#define		user U
#define		meta M
#define add +#1
#define del -#1
#define		unix_lf z
#define 	keepnl n
#define		tri #1#2#3
#define 	comment c
#define 	string s
#define		quote q
#define		eval S
#define		evalquote Q
#define stack bal_stack bal_unstack
#define meta_grammar meta_start meta_end meta_arg_start meta_sep meta_arg_end stack
#define user_grammar call_start call_end arg_start arg_sep arg_end stack arg_num_char quote_char

Added grammar/makefile version [8c2d1af7ce].





>
>
1
2
${TMP}/%: %.gpp grammar.gpp
	${gpp} $< | tr "\n" ' ' > $@

Added grammar/precomp.g.gpp version [d33952f39e].



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include grammar.gpp
#mode comment "//" "\n"
// this file defines the "precomp" grammar, the grammar
// that's used to process C code templates
// vim: ft=c
#define call_start     "["
#define call_end       "]"
#define arg_start      " "
#define arg_sep        ",\\w"
#define arg_end        "]"
#define bal_stack      "\(["
#define bal_unstack    "\)]"
#define arg_num_char   "\\$"
#define quote_char     "|"
#define meta_start     "\\w["
#define meta_end       "]\\w"
#define meta_arg_start " "
#define meta_sep       "\\w:\\w"
#define meta_arg_end   "]\\W"
add(unix_lf)
add(comment "---" "\\n")
add(comment "(*" "*)")
flag(exec)
mode(user, user_grammar)
mode(meta, meta_grammar)

Added kcore/__stack_chk_fail.fn.c version [7f5547f66a].







>
>
>
1
2
3
#include <k/core.h>

void __stack_chk_fail(void) { kstop(-1); }

Modified kcore/boot.rt.x86.lin.64.s from [2671a43f32] to [22c891476d].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; vim: ft=nasm
bits 64
%include "../arch/x86.lin.64.s"
global _start:function
extern _boot
extern entry;

_start:
	mov rbp, rsp
	mov rdi, [rbp + 0] ; argc
	lea rsi, [rbp + 8] ; argv

	call _boot

	mov sys.reg.1, sys.reg.ret
	mov sys.reg.0, sys.exit
	sys.call





|











1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; vim: ft=nasm
bits 64
%include "../arch/x86.lin.64.s"
global _start:function
extern _boot
extern entry

_start:
	mov rbp, rsp
	mov rdi, [rbp + 0] ; argc
	lea rsi, [rbp + 8] ; argv

	call _boot

	mov sys.reg.1, sys.reg.ret
	mov sys.reg.0, sys.exit
	sys.call

Modified kcore/core.h from [cb5067b74b] to [ded1289884].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21





22









































































































#ifndef KIcore
#define KIcore
#include <k/type.h>
#include <k/io.h>
#include <k/str.h>

static void* const null = (void*)0;

typedef struct kvar {
	ksraw name;
	ksraw val;
	char* platform;
} kvar;

typedef struct kenv {
	kiochan std;
	kiochan err;
	sz argc; char** argv;
	kvar* vars;
} kenv;






#endif















































































































<
<













>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#ifndef KIcore
#define KIcore
#include <k/type.h>
#include <k/io.h>
#include <k/str.h>



typedef struct kvar {
	ksraw name;
	ksraw val;
	char* platform;
} kvar;

typedef struct kenv {
	kiochan std;
	kiochan err;
	sz argc; char** argv;
	kvar* vars;
} kenv;


/* i'm really sorry okay */
typedef
#if (__STDC_VERSION__ >= 199901L)
	_Bool bool;
#endif
enum
#if !(__STDC_VERSION__ >= 199901L)
	bool /* enum bool { */
#endif
{
	false = 0, no  = 0,
	true  = 1, yes = 1
}
#if !(__STDC_VERSION__ >= 199901L)
	bool /* } bool ; */
#endif
;

#ifndef KFclean
#	if (__STDC_VERSION__ >= 199901L) ||\
	   (__cplusplus      >= 201103L)
#		define KVvm_args __VA_ARGS__
#		define KVvm_spec ...
#		define KFfeat_variadic_macro
#	else
#		define KVvm_args K_TEMP_M2QD52
#		define KVvm_spec K_TEMP_M2QD52
#	endif
#	if defined(__GNUC__) || defined(__clang__)
#		define KA(KVvm_spec) __attribute__((KVvm_args))
#	else
#		define KA(KVvm_spec)
#	endif
#	define KAformat(KVvm_spec) KA(format(KVvm_args))
#	define KAexport(KVvm_spec) KA(visibility(KVvm_args))

#	define KAunused   KA(unused)
#	define KAnoreturn KA(noreturn)
#	define KApure     KA(const)
#	define KAinline   KA(always_inline)
#	define KAflatten  KA(flatten)
#	define KAexport_none  KAexport("hidden")
#	define KAexport_force KAexport("default")
/*  now we define standard version flags,
 *  to make it easy for the user to write
 *  more portable code. */
#	if (__STDC_VERSION__ >= 199901L) 
#		define KFstd_c89
#		define KFstd_c99
#		define KVstd c99
#	else
#		ifdef __STDC__
#			define KFstd_c89
#			define KVstd c89
#		else
#			define KVstd K&R /* UH OH */
#		endif
#	endif
#	if (__STDC_VERSION__ >= 201103L)
#		define KFstd_c11
#		define KVstd c11
#	endif
#	ifdef __cplusplus
#		define KFstd_cpp
#		define KVstd c++
#		if (__cplusplus >= 201103L)
#			define KFstd_cpp11
#			define KVstd c++11
#		endif /* TODO: add more */
#	endif
#endif

/* hooo boy. null. that one's got a storied
 * history across the versions and dialects.
 * below, we try to find the ideal way to
 * offer a "null" "keyword" depending on
 * dialect, version, and whether the user
 * has asked for macros to be suspended.
 * note that this may result in no "null"
 * being defined in C++ or K&R C. */
#if defined (__cplusplus) && ! defined(KFclean)
#	if __cplusplus >= 201103L
#		define null nullptr
#	else
#		define null ((void*)0)
#	endif
#elif defined __STDC__
	enum { null = 0 };
	/* believe it or not, this is actually
	 * completely legal. doesn't even raise
	 * a single warning. i was surprised too. */
#elif ! defined(KFclean)
#	define null ((void*)0)
#endif

#ifdef __cplusplus
#	define noreturn [[ noreturn ]]
#elif __STDC_VERSION__ >= 201103L
#	define noreturn _Noreturn
#else
#	define noreturn
#endif

noreturn void kstop(longstat code);

#ifdef KFclean
#	undef noreturn
#endif

#endif

Deleted kcore/def.fbsd.i version [4373f0187e].

1
2
#define KVos fbsd
typedef unsigned char stat;
<
<




Deleted kcore/def.h version [5f84d85008].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <k/type.h>
/* <k/def.h>
 * ~ lexi hale <lexi@hale.su>
 * define flags so we can reason about
 * our environment. */

#if (KVos == lin)  ||\
    (KVos == fbsd) ||\
    (KVos == obsd) ||\
    (KVos == nbsd) ||\
    (KVos == dar) ||\
    (KVos == and)  
#   define KFenv_unix
#endif

#if defined(KFenv_unix)  ||\
           (KVos == hai) ||\
           (KVos == mgw)
#   define KFenv_posix
#endif

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































Added kcore/def.h.m version [9abe158651].

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
--- kcore/def.h.m → <k/def.h>
--- ~ lexi hale <lexi@hale.su>
--- this file gathers information on the environment it's
--- being compiled in, setting macros that other headers
--- need. it will be emitted as <k/def.h>.
--- vim: ft=c

[ifdef atom_target_bits]
	[define target: [atom_target_arch].[atom_target_os].[atom_target_bits]]
	#define KVbits [atom_target_bits]
[else]
	[define target: [atom_target_arch].[atom_target_os]]
[endif]
#define KVtarget [target]
#define KVos [atom_target_os]
#define KVarch [atom_target_arch]
[if [target_unix] == yes]
	#define KFenv_unix
	#define KFenv_posix
[else]
	[if [target_posix] == yes]
		#define KFenv_posix
	[endif]
[endif]

#define Kpragma(p) _Pragma(#p)
#if defined(__GNUC__) || defined(__clang__)
#   define Kerror(msg) Kpragma(GCC error #msg) 
#else
#   define Kerror(msg) Kpragma(message #msg)
#endif
#define Knoimpl(fn) Kerror(no implementation of fn for platform [target])

Deleted kcore/def.lin.i version [0178d496a4].

1
2
#define KVos lin
typedef unsigned char stat;
<
<




Deleted kcore/def.win.i version [11ccc3c2e7].

1
2
#define KVos win
typedef u32 stat;
<
<




Added kcore/exit.fn.x86.lin.32.s version [4c4a17d7b6].





















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
bits 32
%include "../arch/x86.lin.32.s"
; vim: ft=nasm
global kio_posix_exit

kio_posix_exit:
	mov sys.reg.0, sys.call.exit
	mov sys.reg.1, [esp + 4] ; first C int argument
	sys.call
	; does not return

Added kcore/exit.fn.x86.lin.64.s version [4c49f79d26].























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
bits 64
%include "../arch/x86.lin.64.s"
%include "../arch/x86.cdecl.64.s"
; vim: ft=nasm

global kio_posix_exit
kio_posix_exit:
	mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi
	mov sys.reg.0, sys.exit
	sys.call
	; no return

Modified kcore/kcore.md from [d664110812] to [28cd81b166].

1
2
3
4
5
6
7
8
9
..
28
29
30
31
32
33
34































# kcore
**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function.

## entry
when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform.

## types
kcore contains fixed-width integer types (in <k/type.h>). note that the availability of each depends on your platform; compilation will fail if e.g. you try to use a u64 or a u128 on a 32-bit platform, so where exact lengths are not required, you may wish to use the built-in C types instead.

................................................................................
 * `kvar* vars` - a pointer into the program's environment

### struct kvar
`kvar` is a struct that abstracts over platform environment variables.
 * `kstr name` - the name of an environment variable
 * `kstr val` - the value of an environment variable
 * `char* platform` - a pointer into the platform's underlying representation
































|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
..
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# kcore
**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`.

## entry
when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform.

## types
kcore contains fixed-width integer types (in <k/type.h>). note that the availability of each depends on your platform; compilation will fail if e.g. you try to use a u64 or a u128 on a 32-bit platform, so where exact lengths are not required, you may wish to use the built-in C types instead.

................................................................................
 * `kvar* vars` - a pointer into the program's environment

### struct kvar
`kvar` is a struct that abstracts over platform environment variables.
 * `kstr name` - the name of an environment variable
 * `kstr val` - the value of an environment variable
 * `char* platform` - a pointer into the platform's underlying representation

## functions

### kstop()

`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in `<k/magic.h>`, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (`<sysexit.h`).

## definitions

kcore is the only module that defines any terms outside the k- namespace. the only terms it so defines are native C terms like `noreturn`, which are implemented as keywords in a reserved namespace (`_` followed by an uppercase letter; in this case, `_Noreturn`). macros are then defined in new headers for the "natural" version of the term in order to avoid breaking older code. examples of this technique are `<stdbool.h>` and `<stdnoreturn.h>`. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load `<k/core.h>`.

libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings.

(the one minor exception is `null`, a new keyword which libk uses in preference to the ugly, hard-to-type C macro `NULL`.)

`bool` is implemented as a `_Bool` typedef if `_Bool` is available, and as an enum typedef otherwise. either way, and regardless of whether `KFclean` has been defined, booleans can be used and set to the values `true`, `false`, `yes`, or `no`. `yes` and `true` are synonyms, as are `false` and `no`.

### macros

if the flag `KFclean` has not been defined, kcore defines a number of macros to abstract over nonstandard C features. for instance, the `KA*` macros can be used to specify GNU/clang attributes without worrying about compatibility, as they'll automatically be blanked under an incompatible compiler. the KA series includes:

	* `KApure` - marks a "pure" function that changes no state and only takes purely numeric arguments, allowing the compiler to avoid repetetive calls to it or even evaluate it at runtime.
	* `KAnoreturn` - the GNU/clang-specific version of `noreturn`.
	* `KAunused` - acknowledges that a variable is unused and tells the compiler to shut up about it.
	* `KAinline` - forces the compiler to inline a function whether it likes it or not.
	* `KAflatten` - placed on a function, will force-inline all function calls *made by* that function — sort of like `KAinline` in reverse.
	* `KAformat()` - specifies that the function has printf-style syntax for error-checking purposes. worst hack in history.

if you wish to add more (there are like, hundreds) please consider making a merge request. however, the generic macro KA() is also available in the interim: `KA(unused)`.

however, unlike C++ attributes, GNU-style attributes can only be placed on a function declaration, *not* its definition.

Added kcore/magic.h version [1f7f63c3e2].







































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* <k/magic.h>
 * ~ lexi hale <lexi@hale.su>
 * one of libk's biggest goals is standardization across
 * software. for this reason, we define here a variety
 * of "magical numbers" used to signal or describe
 * various states, messages, and errors. suggestions are
 * welcome.
 */
#include <k/type.h>

typedef enum kbad {
	// values to be returned by an entry function
	kbad_ok = 0,
		/* the requested operation completed
		 * successfully */

	/* these are synchronized with freebsd's existing
	 * sysexit.h conventions. */
	kbad_usage = 64,
		/* # fbsd EX_USAGE
		 * a usage screen was displayed to the user
		 * on request or due to improperly formed call */
	kbad_data = 65,
		/* # fbsd EX_DATAERR
		 * data supplied by the user was incorrect in
		 * some way */
	kbad_no_input = 66,
		/* # fbsd EX_NOINPUT
		 * input to the program is absent */
	kbad_user = 67,
		/* # fbsd EX_NOUSER
		 * these aren't the droids you're looking for */
	kbad_host = 68,
		/* # fbsd EX_NOHOST
		 * that host doesn't exist */
	kbad_avail = 69,
		/* # fbsd EX_UNAVAILABLE
		 * a necessary resource or support system isn't
		 * available, or something else went wrong */
	kbad_internal = 70,
		/* # fbsd EX_SOFTWARE
		 * "my bad" */
	kbad_os = 71,
		/* # fbsd EX_OSERR
		 * the OS did a fucksy-wucksy */
	kbad_osfile = 72,
		/* # fbsd EX_OSFILE
		 * a system file is fucked up or gone */
	kbad_creat = 73,
		/* # fbsd EX_CANTCREAT */
	kbad_io = 74,
		/* # fbsd EX_IOERR */
	kbad_try = 75,
		/* # fbsd EX_TEMPFAIL
		 * something went wrong this time. try again
		 * some other time */
	kbad_proto = 76,
		/* # fbsd EX_PROTOCOL
		 * failure to speak a protocol correctly */
	kbad_perm = 77,
		/* # fbsd EX_NOPERM
		 * you or the program do not have permissions
		 * to do the thing requested */
	kbad_conf = 78,
		/* # fbsd EX_CONFIG
		 * your configuration is fucked */
} kbad;

Modified kcore/makefile from [121a62ca71] to [c01fcd380e].

1
2
3
4










5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
## kcore/makefile
# kcore has to include, among other things, a replacement
# for stddef.h, and that can't be written in portable C,
# so we're generating it at build time.










#
# look, imma just be straight with you. the mechanism we're
# using to generate these headers is unbelievably heinous.
# it's inelegant, it's gross, and it's horrible. in the long
# term this NEEDS to be replaced with a bespoke solution
# instead of makefile gibberish. hopefully tho this will be
# enough in the short term for libk to get going, enough that
# someone more competent than me will someday be interested
# in fixing this horrorshow.
#
# until them: i'm sorry.
# very sincerely yours, lexi hale

gen-headers = type.h

include ../modmake

${OUT}/k/type.h: ${TMP}/type.${TARGET}.i
	cp $< $@

# generating C source in make… yaaay
define arch =
${TMP}/type.$(1).%.$(2).i: type.$(1).$(2).i def.%.i ${TMP}
	echo '#ifndef KItype' > $$@
	echo '#define KItype' >> $$@
	cat $$< >> $$@
	cat def.$$*.i >> $$@
	echo '#endif' >> $$@
endef

$(eval $(call arch,x86,32))
$(eval $(call arch,x86,64))



|

>
>
>
>
>
>
>
>
>
>












<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26





27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
## kcore/makefile
# kcore has to include, among other things, a replacement
# for stddef.h, and that can't be written in portable C.
# so we're generating it at build time.

# gen-headers = type.h

include ../modmake

## the below code predates the introduction of gpp
## to generate these headers from templates instead
## of trying to write one for everypossible arch 
## tuple. it is left as a monument to a terrible
## and now blissfully forgotten past.
#
# look, imma just be straight with you. the mechanism we're
# using to generate these headers is unbelievably heinous.
# it's inelegant, it's gross, and it's horrible. in the long
# term this NEEDS to be replaced with a bespoke solution
# instead of makefile gibberish. hopefully tho this will be
# enough in the short term for libk to get going, enough that
# someone more competent than me will someday be interested
# in fixing this horrorshow.
#
# until them: i'm sorry.
# very sincerely yours, lexi hale





# ${OUT}/k/type.h: ${TMP}/type.${TARGET}.i
# 	cp $< $@
#
# # generating C source in make… yaaay
# define arch =
# ${TMP}/type.$(1).%.$(2).i: type.$(1).$(2).i def.%.i ${TMP}
# 	echo '#ifndef KItype' > $$@
# 	echo '#define KItype' >> $$@
# 	cat $$< >> $$@
# 	cat def.$$*.i >> $$@
# 	echo '#endif' >> $$@
# endef
#
# $(eval $(call arch,x86,32))
# $(eval $(call arch,x86,64))
#

Added kcore/old/def.fbsd.i version [4373f0187e].





>
>
1
2
#define KVos fbsd
typedef unsigned char stat;

Added kcore/old/def.h version [074eb1cc6b].

























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <k/type.h>
/* <k/def.h>
 * ~ lexi hale <lexi@hale.su>
 * define flags so we can reason about
 * our environment. */

#if (KVos == lin)  ||\
    (KVos == fbsd) ||\
    (KVos == obsd) ||\
    (KVos == nbsd) ||\
    (KVos == dar) ||\
    (KVos == and)  
#   define KFenv_unix
#endif

#if defined(KFenv_unix)  ||\
           (KVos == hai) ||\
           (KVos == mgw)
#   define KFenv_posix
#endif

#define Kpragma(p) _Pragma(#p)
#if defined(__GNUC__) || defined(__clang__)
#   define Kerror(msg) Kpragma(GCC error #msg) 
#else
#   define Kerror(msg) Kpragma(message #msg)
#endif
#define Knoimpl(fn,p) Kerror(no implementation of fn for platform p)

Added kcore/old/def.lin.i version [0178d496a4].





>
>
1
2
#define KVos lin
typedef unsigned char stat;

Added kcore/old/def.win.i version [11ccc3c2e7].





>
>
1
2
#define KVos win
typedef u32 stat;

Added kcore/old/type.x86.32.i version [fe891b76db].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define KVarch x86
#define KVbits 32

typedef unsigned long sz;

typedef unsigned char  u8;
typedef signed char    s8;
typedef unsigned short u16;
typedef signed short   s16;
typedef unsigned long  u32;
typedef signed long    s32;

typedef u32  word;
typedef s32 sword;

Added kcore/old/type.x86.64.i version [03a8b8f372].





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define KVarch x86
#define KVbits 64

typedef unsigned long long sz;

typedef unsigned char      u8;
typedef signed char        s8;
typedef unsigned short     u16;
typedef signed short       s16;
typedef unsigned long      u32;
typedef signed long        s32;
typedef unsigned long long u64;
typedef signed long long   s64;
typedef __uint128_t        u128;
typedef __int128_t         s128;

typedef u64  word;
typedef s64 sword;

Added kcore/stop.fn.c version [c4373dead4].



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* kcore/stop.fn.c - kstop()
 * ~ lexi hale <lexi@hale.su>
 * this file defines a function that prematurely exits from
 * a running libk program with an appropriate exit status. */

#include <k/core.h>
#include <k/def.h> // so we know what system this is
#include <k/type.h>

#ifdef KFenv_posix
#	define STOPFN kio_posix_exit
	extern void STOPFN(int);
#else
	Knoimpl(kstop)
#endif

noreturn void kstop (longstat code) { STOPFN(code); }

Added kcore/testbin.exe.c version [f1bdb07ea2].





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <k/core.h>
#include <k/mem.h>
#include <k/io.h>
#include <k/magic.h>

kbad entry(kenv e) {
	const char msg[] = "hello from libk\n";
	ksraw ptr = { Kmsz(msg), msg };

	bool maybe = true;
	maybe = no;

	if (kiosend(e.std, ptr, null) == kiocond_ok) {
		return kbad_ok;
	} else {
		return kbad_io;
	}
}

Added kcore/type.h.m version [8b1f2e0cb2].































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
--- kcore/type.h.m → <k/def.h>
--- ~ lexi hale <lexi@hale.su>
--- this file gathers information on the environment it's
--- being compiled in, defining types that our code
--- needs. it will be emitted as <k/type.h>.
--- vim: ft=c

// arch bit length [atom_target_bits]
--- make some gigantic fucking assumptions
[if [atom_target_bits] >= 8]
    typedef unsigned char  u8;
    typedef signed char    s8;
    [if [atom_target_bits] >= 16]
        typedef unsigned short u16;
        typedef signed short   s16;
        [if [atom_target_bits] >= 32]
            typedef unsigned long  u32;
            typedef signed long    s32;
            [if [atom_target_bits] >= 64]
                typedef unsigned long long u64;
                typedef signed long long s64;
                [if [atom_target_arch] == x86]
                    #if defined(__GNUC__) || defined(__clang__)
                        typedef __uint128_t u128;
                        typedef __int128_t  s128;
                    #endif
                [endif]
            [endif]
        [endif]
    [endif]
[endif]

typedef u[atom_target_bits]  sz;
typedef s[atom_target_bits] ssz;

--- make sure something unlikely doesn't happen
[define [maxtype name, n]:
	[if [n] > [atom_target_bits]]
		typedef u[atom_target_bits] [name]
	[else]
		typedef u[n] [name]
	[endif]]

// exit status integer types - pls use kbad in <k/magic.h> instead
[if [target_posix] == yes]
	/* by convention, posix return values are 8-bit,
	 * but note that many modern UNIXes do technically
	 * support higher-bit values. for this reason,
	 * longstat is defined differently under posix. */
	typedef u8 stat;
	[maxtype longstat, 32];
[else]
	[if ([atom_target_os] == win) ||
		([atom_target_os] == vms)]
		[maxtype stat, 32]
	[else]
		typedef u8 stat;
		/* we don't know a specific exit status type
		 * for your arch so we're going with a sane
		 * default. if this is wrong, help us fix it! */
	[endif]
	typedef stat longstat;
[endif]

Deleted kcore/type.x86.32.i version [fe891b76db].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define KVarch x86
#define KVbits 32

typedef unsigned long sz;

typedef unsigned char  u8;
typedef signed char    s8;
typedef unsigned short u16;
typedef signed short   s16;
typedef unsigned long  u32;
typedef signed long    s32;

typedef u32  word;
typedef s32 sword;
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























Deleted kcore/type.x86.64.i version [03a8b8f372].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define KVarch x86
#define KVbits 64

typedef unsigned long long sz;

typedef unsigned char      u8;
typedef signed char        s8;
typedef unsigned short     u16;
typedef signed short       s16;
typedef unsigned long      u32;
typedef signed long        s32;
typedef unsigned long long u64;
typedef signed long long   s64;
typedef __uint128_t        u128;
typedef __int128_t         s128;

typedef u64  word;
typedef s64 sword;
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Added kio/send.c version [d88d1642fe].





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <k/io.h>
#include <k/core.h>
#include <k/def.h>
/* send.c - kiosend()
 * ~ lexi hale <lexi@hale.su>
 * kiosend() writes to a channel with an open out stream
 */

// we define all platform functions here,
// whether or not they're for the correct
// platform - only the ones actually called
// by the generated code will be linked
extern sz kio_posix_fd_write(int fd, const char* buf, sz len);

kiocond kiosend(kiochan target, ksraw string, sz* len) {
#	ifdef KFenv_posix
		sz size = kio_posix_fd_write(target.out.platform_fd, string.ptr, string.size);
		if (size == -1) return kiocond_fail; //TODO: retrieve errno and offer more specific errors
#	else
#		if KVos == win
#			error windows IO send function not yet defined
#		else
			Knoimpl(kiosend,KVos);
#			error missing implementation // boring error for plebs
#		endif
#	endif

	if (len != null) *len = size;
	return kiocond_ok;
}

Modified libk.md from [3c28c4d693] to [65938a7892].

25
26
27
28
29
30
31













32
33
34
35
36
37
38
 5. **intrinsic reentrancy.** because *jesus christ,* libc.
 6. **interprocess communication.** libc offers no useful IPC abstractions over the paltry array of tools POSIX &co. give us to work with. we can do better.
 7. **tooling.** libk is intended as more than just a library. it's also intended to work with some basic tooling to automate tasks that current binary tooling is inadequate for -- for instance, embedding binary data into a program binary. (see module [kgraft](kgraft))
 8. **modularity.** libk is not part of the C specification and it isn't always going to be practical for developers to expect the entire library to be present on the end-user's computer. so libk is designed to be usable in many different ways -- as a traditional library, as a static library, in full form or with only components needed by the developer, to be distributed either on its own or as part of a binary.
 9. **compatibility.** code that links against libk should be able to compile and run on any operating system. in the ideal case (Linux or FreeBSD) it will be able to do so without touching any other system libraries; for less ideal environments like Windows, libk will when necessary abstract over system libraries or libc itself.
 10. **sane error-handling.** every time you type `errno` god murders a puppy.














## naming conventions

one of the most frustrating things about libc is its complete and total *lack* of a naming convention. in C, every function and global is injected into a single global namespace, including macros. this means that every libc header you include scatters words all over that namespace, potentially clobbering your function with a macro!

libk is designed to fix this (in hindsight) glaring error.

however, a common problem with libraries is the proliferation of inordinately long and hard-to-type function names such as `SuperWidget_Widget_Label_Font_Size_Set()`. this may be tolerable in IDEs with robust auto-complete or when referencing a highly-specific, sparsely-used library; it is however completely intolerable in the case of a core library with heavily used functionality.







>
>
>
>
>
>
>
>
>
>
>
>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 5. **intrinsic reentrancy.** because *jesus christ,* libc.
 6. **interprocess communication.** libc offers no useful IPC abstractions over the paltry array of tools POSIX &co. give us to work with. we can do better.
 7. **tooling.** libk is intended as more than just a library. it's also intended to work with some basic tooling to automate tasks that current binary tooling is inadequate for -- for instance, embedding binary data into a program binary. (see module [kgraft](kgraft))
 8. **modularity.** libk is not part of the C specification and it isn't always going to be practical for developers to expect the entire library to be present on the end-user's computer. so libk is designed to be usable in many different ways -- as a traditional library, as a static library, in full form or with only components needed by the developer, to be distributed either on its own or as part of a binary.
 9. **compatibility.** code that links against libk should be able to compile and run on any operating system. in the ideal case (Linux or FreeBSD) it will be able to do so without touching any other system libraries; for less ideal environments like Windows, libk will when necessary abstract over system libraries or libc itself.
 10. **sane error-handling.** every time you type `errno` god murders a puppy.

## dependencies

libk is designed to be as portable and depedency-free as possible. ideally, it will be possible to compile code against libk using nothing but libk itself.

compiling libk is also designed to be as easy as possible. it has only two external dependencies, the macro processor [gpp], needed for compile-time header generation , and the [GNU make] utility, whose advanced features are needed to perform the relatively complex task of building all of libk from the ground up. 

 [gpp]: http://en.nothingisreal.com/wiki/GPP
 [GNU make]: http://www.gnu.org/software/make

while gpp is a very small program that builds quickly and has no major dependencies of its own, it is an obscure program not likely to be found in any repositories and with an uncertain future. for these reasons, adding m4 translations of the gpp headers should be a long-term priority. being able to be built with both a very small, easily built macro processor, and a very large but extremely well-supported processor, should make libk maximally buildable and future-proof.

while this project will include gpp tooling and GNU makefiles designed to ease the task of writing and building libk code (as well as tools in many other languages, including native binaries that compile against libk), none of them are required for the task.

## naming conventions

one of the most frustrating things about libc is its complete and total *lack* of a naming convention. in C, every function and global is injected into a single global namespace, including macros. this means that every libc header you include scatters words all over that namespace, potentially clobbering your function with a macro!

libk is designed to fix this (in hindsight) glaring error.

however, a common problem with libraries is the proliferation of inordinately long and hard-to-type function names such as `SuperWidget_Widget_Label_Font_Size_Set()`. this may be tolerable in IDEs with robust auto-complete or when referencing a highly-specific, sparsely-used library; it is however completely intolerable in the case of a core library with heavily used functionality.

Modified makefile from [ac03aab83b] to [d63468c69f].

1
2

3
4
5
6
7

8



9

10
11
12
13
14
15
16
17
18
19
20

21
22
23
24

25
26
27
28
29
30
31
32
33

34
35








36
37
38
39
40

41
42





43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90



91
92
93
94
95
96
97
..
98
99
100
101
102
103
104
105
106
export OUT = $(PWD)/out


export ARCH = x86
export OS = lin
export BITS = 64
export TMP = $(PWD)/tmp


export TARGET = $(ARCH).$(OS).$(BITS)





export lin-headers = /usr/include/asm
export fbsd-headers = /usr/include/sys

moddirs = $(wildcard k*)
binaries = $(wildcard k*/*.exe.c)
functions = $(wildcard k*/*.fn.c) 
assemblies = $(wildcard k*/*.fn.${TARGET}.s)
binmods = $(sort $(dir $(binaries)))

# i'm sorry
collect = $(strip $(foreach dir,$(moddirs),$(addprefix $(OUT)/$(dir).,$(notdir $(wildcard $(dir)/$1)))))


cfnsources = $(call collect,*.fn.c)
sfnsources = $(call collect,*.fn.${TARGET}.s)
crtsources = $(call collect,*.rt.c)

srtsources = $(call collect,*.rt.${TARGET}.s)
fnsources = $(cfnsources) $(sfnsources)
rtsources = $(crtsources) $(srtsources)
sources = $(fnsources) $(rtsources)

cfnobjects = $(cfnsources:%.c=%.o)
sfnobjects = $(sfnsources:%.s=%.o)
crtobjects = $(crtsources:%.c=%.o)
srtobjects = $(srtsources:%.s=%.o)

fnobjects = $(cfnobjects) $(sfnobjects)
rtobjects = $(crtobjects) $(srtobjects)








objects = $(fnobjects) $(rtobjects)

header-dir = /usr/include
lib-dir = /usr/lib


posix-oses = lin fbsd dar and hai mgw






ifeq ($(findstring $(OS),$(posix-oses)),$(OS))
export POSIX = yes
else
export POSIX = no

endif

# include libgcc.a in gcc builds, just in case
ifeq ($(CC),gcc)
export COMPLIB = -lgcc
endif

all: $(OUT) defs obj tool lib.static $(OUT)/boot.o lib.shared
lib.static: defs obj $(OUT)/libk.a
lib.shared: defs obj $(OUT)/libk.so
obj: $(moddirs:%=%.obj)
defs: $(moddirs:%=%.def)
tool: $(OUT)/libk.a $(binmods:%=%.tool) 
clean:
	rm -rf $(TMP) $(OUT)

install: all
	install -d $(header-dir)/k -o root -g wheel
	install $(OUT)/k/* $(header-dir)/k/ -o root -g wheel -m 0644
	install -d $(lib-dir)/k -o root -g wheel
	install $(OUT)/libk.a $(OUT)/libk.so $(OUT)/boot.o \
		$(lib-dir)/k/ -o root -g wheel -m 0644

uninstall: $(header-dir)/k $(lib-dir)/k
	rm -rf $^

lists = moddirs functions assemblies cfnobjects sfnobjects crtobjects srtobjects rtobjects binaries binmods POSIX
dbg:
	@echo -e lists: $(foreach var, $(lists), "\\n - \\e[1m$(var)\\e[m = $($(var))")

%.obj: %/makefile ${TARGET}.calls $(OUT)
	cd $* && $(MAKE) obj

%.tool: %/makefile $(OUT)
	cd $* && $(MAKE) tool

%.dbg: %/makefile $(OUT)
	cd $* && $(MAKE) dbg

%.def: %/makefile $(OUT) $(OUT)/k
	cd $* && $(MAKE) def

%.calls: arch/makefile
	cd arch && $(MAKE) $(TMP)/calls.$*.s




$(OUT)/libk.so: $(fnobjects) 
	ld -shared $(COMPLIB) -o $@ $^
	@# $(CC) -shared -fPIC -nostdlib $(COMPLIB) -o $@ $(OUT)/*.o

$(OUT)/boot.o: $(rtobjects)
	ld -r $^ -o $(OUT)/boot.o
................................................................................

$(OUT)/libk.a: $(fnobjects) $(rtobjects)
	@# using `ar rc` and ranlib here instead of
	@# `ar rcs` in case `ar` isn't the GNU version
	ar rc $@ $^
	ranlib $@

$(OUT) $(OUT)/k:
	mkdir -p $@


>





>
|
>
>
>

>






|



|
>

|
|
|
>
|
<
<
|

|
|
|
<
>
|
|
>
>
>
>
>
>
>
>





>


>
>
>
>
>
|
|
|
|
>










|
|
|













|



|


|





|




>
>
>







 







|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
122
123
124
125
126
127
128
129
130
export OUT = $(PWD)/out

# TODO: calculate these using $(MAKE_HOST)
export ARCH = x86
export OS = lin
export BITS = 64
export TMP = $(PWD)/tmp

ifneq ($(BITS),)
    export TARGET = $(ARCH).$(OS).$(BITS)
else
    export TARGET = $(ARCH).$(OS)
endif

export gpp = gpp
export lin-headers = /usr/include/asm
export fbsd-headers = /usr/include/sys

moddirs = $(wildcard k*)
binaries = $(wildcard k*/*.exe.c)
functions = $(wildcard k*/*.fn.c) 
assemblies = $(wildcard k*/*.fn.$(TARGET).s)
binmods = $(sort $(dir $(binaries)))

# i'm sorry
collect = $(strip $(foreach dir,$(moddirs),$(wildcard $(dir)/*.$1)))
transform = $(strip $(foreach dir,$(moddirs),$(patsubst $(dir)/%.$1,$(subst @,$(dir),$2),$(wildcard $(dir)/*.$1))))

m-hdr-macs = $(call collect,h.m)
m-c-src-macs = $(call collect,c.m)
m-s-src-macs = $(call collect,$(TARGET).s.m)

m-c-sources = $(call transform,c.m,$(TMP)/@.%.c)


m-s-sources = $(call transform,$(TARGET).s.m,$(TMP)/@.%.$(TARGET).s)

m-headers = $(call transform,h.m,$(OUT)/k/%.h)
m-c-objects = $(call transform,c.m,$(OUT)/@.%.o)
m-s-objects = $(call transform,$(TARGET).s.m,$(OUT)/@.%.$(TARGET).o)


fnobjects = $(call transform,fn.c,$(OUT)/@.%.fn.o) \
            $(call transform,fn.c.m,$(OUT)/@.%.fn.o) \
            $(call transform,fn.$(TARGET).s,$(OUT)/@.%.fn.$(TARGET).o) \
            $(call transform,fn.$(TARGET).s.m,$(OUT)/@.%.fn.$(TARGET).o)

rtobjects = $(call transform,rt.c,$(OUT)/@.%.rt.o) \
            $(call transform,rt.c.m,$(OUT)/@.%.rt.o) \
            $(call transform,rt.$(TARGET).s,$(OUT)/@.%.rt.$(TARGET).o) \
            $(call transform,rt.$(TARGET).s.m,$(OUT)/@.%.rt.$(TARGET).o)

objects = $(fnobjects) $(rtobjects)

header-dir = /usr/include
lib-dir = /usr/lib

unix-oses = lin fbsd dar and
posix-oses = lin fbsd dar and hai mgw

ifeq ($(findstring $(OS),$(unix-oses)),$(OS))
    export UNIX = yes
    export POSIX = yes
else
    export UNIX = no
    ifeq ($(findstring $(OS),$(posix-oses)),$(OS))
        export POSIX = yes
	else
        export POSIX = no
	endif
endif

# include libgcc.a in gcc builds, just in case
ifeq ($(CC),gcc)
export COMPLIB = -lgcc
endif

all: $(OUT) defs obj tool lib.static $(OUT)/boot.o lib.shared
lib.static: defs obj $(OUT)/libk.a
lib.shared: defs obj $(OUT)/libk.so
obj: $(moddirs:%=%.obj) 
defs: $(moddirs:%=%.def) 
tool: $(OUT)/boot.o $(OUT)/libk.a $(binmods:%=%.tool) 
clean:
	rm -rf $(TMP) $(OUT)

install: all
	install -d $(header-dir)/k -o root -g wheel
	install $(OUT)/k/* $(header-dir)/k/ -o root -g wheel -m 0644
	install -d $(lib-dir)/k -o root -g wheel
	install $(OUT)/libk.a $(OUT)/libk.so $(OUT)/boot.o \
		$(lib-dir)/k/ -o root -g wheel -m 0644

uninstall: $(header-dir)/k $(lib-dir)/k
	rm -rf $^

lists = moddirs functions assemblies fnobjects rtobjects binaries binmods POSIX
dbg:
	@echo -e lists: $(foreach var, $(lists), "\\n - \\e[1m$(var)\\e[m = $($(var))")

%.obj: %/makefile $(TMP)/precomp.g $(TARGET).calls $(OUT)
	cd $* && $(MAKE) obj

%.tool: %/makefile $(TMP)/precomp.g $(OUT)
	cd $* && $(MAKE) tool

%.dbg: %/makefile $(OUT)
	cd $* && $(MAKE) dbg

%.def: %/makefile $(TMP)/precomp.g $(OUT) $(OUT)/k
	cd $* && $(MAKE) def

%.calls: arch/makefile
	cd arch && $(MAKE) $(TMP)/calls.$*.s

$(TMP)/precomp.g: grammar/precomp.g.gpp $(TMP)
	cd grammar && $(MAKE) $@

$(OUT)/libk.so: $(fnobjects) 
	ld -shared $(COMPLIB) -o $@ $^
	@# $(CC) -shared -fPIC -nostdlib $(COMPLIB) -o $@ $(OUT)/*.o

$(OUT)/boot.o: $(rtobjects)
	ld -r $^ -o $(OUT)/boot.o
................................................................................

$(OUT)/libk.a: $(fnobjects) $(rtobjects)
	@# using `ar rc` and ranlib here instead of
	@# `ar rcs` in case `ar` isn't the GNU version
	ar rc $@ $^
	ranlib $@

$(OUT) $(OUT)/k $(TMP):
	mkdir -p $@

Modified modmake from [3c50b488d5] to [eb2aedd9b1].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

16











17
18
19
20
21
22
23
24
25

26
27
28
29
30



31
32










33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70

71
72
73
74





75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93







#- modmake
# this is the master makefile that controls the building of each
# libk module. it is included from each k*/makefile.
# vim: ft=make

mod = $(notdir $(PWD))
src = $(wildcard *.c) $(wildcard *.s)
bare = $(mod:k%=%)
headers = $(wildcard *.h) $(gen-headers)

tools    = $(filter     %.exe.c,   $(src))
nontools = $(filter-out %.exe.c,   $(src))
cobjects = $(filter     %.c,       $(nontools))
sobjects = $(filter %.${TARGET}.s, $(nontools))


cflags = -isystem ${OUT} -fPIC -nostdlib ${COMPLIB} -L${OUT} -lk












obj: $(cobjects:%.c=${OUT}/$(mod).%.o) \
	 $(sobjects:%.s=${OUT}/$(mod).%.o)
tool: $(tools:%.exe.c=${OUT}/$(mod).%) \
	  ${OUT}/libk.a

def: $(headers:%=${OUT}/k/%)

dbg:

	@echo tools = $(tools)
	@echo TARGET = ${TARGET}
	@echo cobjects = $(cobjects)
	@echo sobjects = $(sobjects)
	@echo headers = $(headers)



	@echo mod = $(mod)











${OUT}/$(mod).%.o: %.c
	$(CC) $(cflags) -c $< -o $@

${OUT}/k/%.h: %.h
	cp $< $@

${OUT}/$(mod).%: %.exe.c
	$(CC) $(cflags) $< -o $@

${TMP}:
	mkdir -p ${TMP}

#- assembly
# compiling  the assembly  code will  be  faster but  a lot  more
# complex, given the  nature of assembly and the  large number of
................................................................................
# that  requires ugly  make  rules,  we're just  going  to use  a
# function to generate these.

# ${OUT} = ultimate build directory
# $(mod) = module name
#      % = function name
#   $(1) = arch tuple
arch = ${OUT}/$(mod).%.$(1).o: %.$(1).s
# invoke with $(call arch,tuple). do not
# put spaces between either term though!

ifeq ($(debug),yes)
yasm-flags = -gdwarf2
endif



#-- linux
# linux uses the ELF{32,64} binary format,  and generating these
# from yasm is trivial.  linux only supports one ABI per format,
# at least with ELF, so that's all we need to do.

#${OUT}/$(mod).%.x86.lin.32.o: %.x86.lin.32.s

$(call arch,x86.lin.32)
	yasm $(yasm-flags) -felf32 -i${TMP} $< -o $@

#${OUT}/$(mod).%.x86.lin.64.o: %.x86.lin.64.s





$(call arch,x86.lin.64)
	yasm $(yasm-flags) -felf64 -i${TMP} $< -o $@


#-- freebsd
# the freebsd ABI is different, so it will require different code
# (though there might be ways to minimize that). freebsd uses the
# same binary format as Linux  (though it also supports a.out and
# COFF) but because freebsd can interpret multiple different ABIs
# the  object files  need to  be "branded"  with the  correct one
# using the tool brandelf (`brandelf -t [ABI]`)

$(call arch,x86.fbsd.32)
	yasm -felf32 $< -o $@
	brandelf -t FreeBSD $@

$(call arch,x86.fbsd.64)
	yasm -felf64 $< -o $@
	brandelf -t FreeBSD $@














|

|






>
|
>
>
>
>
>
>
>
>
>
>
>









>





>
>
>


>
>
>
>
>
>
>
>
>
>
|






|







 







|







>
>





<
>
|
|

|
>
>
>
>
>
|
<
>









|
|


|
|


>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#- modmake
# this is the master makefile that controls the building of each
# libk module. it is included from each k*/makefile.
# vim: ft=make

mod = $(notdir $(PWD))
src = $(wildcard *.c) $(wildcard *.s) $(filter-out %.h,$(patsubst %.m,%,$(wildcard *.m)))
bare = $(mod:k%=%)
headers = $(wildcard *.h) $(gen-headers) $(patsubst %.m,%,$(wildcard *.h.m))

tools    = $(filter     %.exe.c,   $(src))
nontools = $(filter-out %.exe.c,   $(src))
cobjects = $(filter     %.c,       $(nontools))
sobjects = $(filter %.${TARGET}.s, $(nontools))

gpp = gpp
cflags = -std=c11 -isystem ${OUT} -fPIC -nostdlib ${COMPLIB} -L${OUT}

m-env = atom_target_arch=${ARCH}
m-env += atom_target_os=${OS}
ifneq (${BITS},) #!!! ifdef does NOT work with environment variables
    m-env += atom_target_bits=${BITS}
endif
m-env += target_posix=${POSIX}
m-env += target_unix=${UNIX}

m-grammar = $(file < ${TMP}/precomp.g)
m-comp = $(gpp) $(m-grammar) $(m-env:%=-D%)

obj: $(cobjects:%.c=${OUT}/$(mod).%.o) \
	 $(sobjects:%.s=${OUT}/$(mod).%.o)
tool: $(tools:%.exe.c=${OUT}/$(mod).%) \
	  ${OUT}/libk.a

def: $(headers:%=${OUT}/k/%)

dbg:
	@echo src = $(src)
	@echo tools = $(tools)
	@echo TARGET = ${TARGET}
	@echo cobjects = $(cobjects)
	@echo sobjects = $(sobjects)
	@echo headers = $(headers)
	@echo m-comp = $(m-comp)
	@echo m-grammar = $(m-grammar)
	@echo m-env = $(m-env) "$(m-env:%=-D%)"
	@echo mod = $(mod)

${OUT}/k/%.h: %.h.m
	$(m-comp) $< > $@

.PRECIOUS: ${TMP}/$(mod).%
${TMP}/$(mod).%: %.m ${TMP}
	$(m-comp) $< > $@

${OUT}/$(mod).%.o: ${TMP}/$(mod).%.c
	$(CC) $(cflags) -c $< -o $@

${OUT}/$(mod).%.o: %.c $(bare).h
	$(CC) $(cflags) -c $< -o $@

${OUT}/k/%.h: %.h
	cp $< $@

${OUT}/$(mod).%: %.exe.c
	$(CC) $(cflags) $< ${OUT}/libk.a -o $@

${TMP}:
	mkdir -p ${TMP}

#- assembly
# compiling  the assembly  code will  be  faster but  a lot  more
# complex, given the  nature of assembly and the  large number of
................................................................................
# that  requires ugly  make  rules,  we're just  going  to use  a
# function to generate these.

# ${OUT} = ultimate build directory
# $(mod) = module name
#      % = function name
#   $(1) = arch tuple
arch = ${OUT}/$(mod).%.$(1).o: $2%.$(1).s
# invoke with $(call arch,tuple). do not
# put spaces between either term though!

ifeq ($(debug),yes)
yasm-flags = -gdwarf2
endif

yasm = yasm $(yasm-flags) -f$1 -i${TMP} $< -o $@

#-- linux
# linux uses the ELF{32,64} binary format,  and generating these
# from yasm is trivial.  linux only supports one ABI per format,
# at least with ELF, so that's all we need to do.



$(call arch,x86.lin.32,)
	$(call yasm,elf32)

$(call arch,x86.lin.64,)
	$(call yasm,elf64)

$(call arch,x86.lin.32,${TMP}/$(mod).)
	$(call yasm,elf32)

$(call arch,x86.lin.64,${TMP}/$(mod).)

	$(call yasm,elf64)

#-- freebsd
# the freebsd ABI is different, so it will require different code
# (though there might be ways to minimize that). freebsd uses the
# same binary format as Linux  (though it also supports a.out and
# COFF) but because freebsd can interpret multiple different ABIs
# the  object files  need to  be "branded"  with the  correct one
# using the tool brandelf (`brandelf -t [ABI]`)

$(call arch,x86.fbsd.32,)
	$(call yasm,elf32)
	brandelf -t FreeBSD $@

$(call arch,x86.fbsd.64,)
	$(call yasm,elf64)
	brandelf -t FreeBSD $@

$(call arch,x86.fbsd.32,${TMP}/$(mod).)
	$(call yasm,elf32)
	brandelf -t FreeBSD $@

$(call arch,x86.fbsd.64,${TMP}/$(mod).)
	$(call yasm,elf64)
	brandelf -t FreeBSD $@