mirror of https://github.com/flysand7/ciabatta.git
Auto-generate ciabatta.c
This commit is contained in:
parent
4c2ed6e7e4
commit
13ba60e338
|
@ -1,5 +1,6 @@
|
|||
bin
|
||||
/a
|
||||
/src/ciabatta.c
|
||||
a.out
|
||||
*.a
|
||||
*.so
|
||||
|
|
57
build.lua
57
build.lua
|
@ -2,6 +2,7 @@
|
|||
|
||||
local path = require 'path'
|
||||
local argparse = require 'argparse'
|
||||
local mf_parser = require 'scripts.manifest-parser'
|
||||
|
||||
-- Parse command line arguments
|
||||
local parser = argparse('build.lua', 'Ciabatta build script')
|
||||
|
@ -109,14 +110,16 @@ tinyrt_iface_hdr:write('// This file is AUTO-GENERATED\n')
|
|||
tinyrt_iface_hdr:write('// See tinyrt.mf\n')
|
||||
tinyrt_iface_hdr:write('\n')
|
||||
n = 1
|
||||
tinyrt_apis = {}
|
||||
for line in io.lines(tinyrt_manifest_path) do
|
||||
if line:len() ~= 0 and line:sub(1,1) ~= '#' and line:gsub('%s+', '') ~= '' then
|
||||
local line_it = line:gmatch('[_a-zA-Z0-9]+')
|
||||
local api_name = line_it():upper()
|
||||
local api_name = line_it()
|
||||
local has_impl = line_it()
|
||||
if has_impl == '0' or has_impl == '1' then
|
||||
local api_define = '#define ' .. api_name .. ' '..has_impl..'\n'
|
||||
local api_define = '#define ' .. (api_name:upper()) .. ' '..has_impl..'\n'
|
||||
tinyrt_iface_hdr:write(api_define)
|
||||
table.insert(tinyrt_apis, api_name)
|
||||
else
|
||||
print('SYNTAX ERROR AT LINE '..n..': Expected 1 or 0 for the value')
|
||||
end
|
||||
|
@ -125,6 +128,50 @@ for line in io.lines(tinyrt_manifest_path) do
|
|||
n = n+1
|
||||
end
|
||||
io.close(tinyrt_iface_hdr)
|
||||
-- Parse manifest and generate ciabatta.c
|
||||
local function has_value(tab, val)
|
||||
for index, value in ipairs(tab) do
|
||||
if value == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
print('==> Generating ciabatta.c')
|
||||
cia_h = io.open(path.join('src', 'ciabatta.c'), 'wb')
|
||||
mf = parse_mf(path.join('src', 'library.mf'))
|
||||
cia_h:write('\n')
|
||||
cia_h:write('// THIS FILE IS AUTO-GENERATED. SEE library.mf FOR DETAILS\n')
|
||||
cia_h:write('\n// global includes\n')
|
||||
for index,include in ipairs(mf.includes) do
|
||||
cia_h:write('#include <'..include..'>\n')
|
||||
end
|
||||
for index,decl_platform in ipairs(mf.platforms) do
|
||||
if decl_platform.name == platform then
|
||||
cia_h:write(('\n// platform %s\n'):format(decl_platform.name))
|
||||
for index,include in ipairs(decl_platform.includes) do
|
||||
cia_h:write('#include "'..include..'"\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
for index, api in ipairs(mf.apis) do
|
||||
supported = true
|
||||
for index, dep in ipairs(api.deps) do
|
||||
if not has_value(tinyrt_apis, dep) then
|
||||
supported = false
|
||||
end
|
||||
end
|
||||
if supported then
|
||||
cia_h:write(('\n// module %s\n'):format(api.name))
|
||||
print(' - Exporting module: ' .. api.name)
|
||||
for index, include in ipairs(api.includes) do
|
||||
cia_h:write('#include "'..include..'"\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
io.close(cia_h)
|
||||
|
||||
|
||||
-- Figure out compiler flags
|
||||
local cflags = table.concat(compiler_flags, ' ')..' '..
|
||||
|
@ -165,12 +212,12 @@ end
|
|||
path.mkdir('lib')
|
||||
path.mkdir('bin')
|
||||
|
||||
assemble('src/linux/crt_entry.asm', 'bin/crt_entry.o')
|
||||
compile({'src/linux/crt_ctors.c'}, 'bin/crt_ctors.o', '-fpic -c')
|
||||
assemble('src/linux/crt-entry.asm', 'bin/crt-entry.o')
|
||||
compile({'src/linux/crt-ctors.c'}, 'bin/crt-ctors.o', '-fpic -c')
|
||||
compile({'src/ciabatta.c'}, 'bin/ciabatta.o', '-fpic -c')
|
||||
|
||||
if library_type == 'static' then
|
||||
archive({'bin/ciabatta.o', 'bin/crt_ctors.o', 'bin/crt_entry.o'}, 'lib/'..ciabatta_lib)
|
||||
archive({'bin/ciabatta.o', 'bin/crt-ctors.o', 'bin/crt-entry.o'}, 'lib/'..ciabatta_lib)
|
||||
elseif library_type == 'shared' then
|
||||
print('SHARED OBJECTS NOT SUPPORTED YET')
|
||||
os.exit(1)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cia-def.h>
|
||||
|
||||
int atexit(void (*func)(void));
|
||||
int at_quick_exit(void (*func)(void));
|
||||
noreturn void abort(void);
|
||||
noreturn void exit(int code);
|
||||
noreturn void _Exit(int code);
|
||||
noreturn void quick_exit(int code);
|
|
@ -0,0 +1,265 @@
|
|||
#!/usr/bin/env lua
|
||||
|
||||
local path = require 'path'
|
||||
|
||||
levels = {}
|
||||
parser_obj = {}
|
||||
parser_cur_ind = 0
|
||||
parser_ind_lv = 0
|
||||
|
||||
-- Tokens
|
||||
Token = {}
|
||||
function Token.new(kind, value)
|
||||
return {kind = kind, value = value}
|
||||
end
|
||||
function Token.keyword(kw) return Token.new('keyword', kw) end
|
||||
function Token.string(str) return Token.new('string', str) end
|
||||
function Token.lparen() return Token.new('(', nil) end
|
||||
function Token.rparen() return Token.new(')', nil) end
|
||||
function Token.lbrace() return Token.new('{', nil) end
|
||||
function Token.rbrace() return Token.new('}', nil) end
|
||||
function Token.colon() return Token.new(':', nil) end
|
||||
function Token.comma() return Token.new(',', nil) end
|
||||
function Token.eof() return Token.new('eof', nil) end
|
||||
|
||||
Parser = {
|
||||
file = nil,
|
||||
filename = nil,
|
||||
row = 0,
|
||||
col = 0,
|
||||
ind_spaces = 0,
|
||||
ind_levels = {0},
|
||||
c = nil, -- Last Read Character
|
||||
t = nil, -- Last Read Token
|
||||
}
|
||||
-- Character functions
|
||||
function Parser.char_next(parser, c)
|
||||
char = parser.file:read(1)
|
||||
parser.col = parser.col + 1
|
||||
if char == '\n' then
|
||||
parser.row = parser.row + 1
|
||||
parser.col = 0
|
||||
end
|
||||
parser.c = char
|
||||
end
|
||||
function Parser.char(parser)
|
||||
return parser.c
|
||||
end
|
||||
function Parser.char_is(parser, pattern)
|
||||
match = parser:char():match(pattern)
|
||||
if match then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
function Parser.char_match(parser, pattern)
|
||||
if parser:char_is(pattern) then
|
||||
parser:char_next()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
-- Token functions
|
||||
function Parser.token(parser)
|
||||
return parser.t
|
||||
end
|
||||
function Parser.token_next(parser)
|
||||
-- Skip over the whitespace
|
||||
while true do
|
||||
if parser.c == nil then
|
||||
parser.t = Token.eof()
|
||||
return
|
||||
elseif parser:char_match('#') then
|
||||
while not parser:char_match('\n') do
|
||||
parser:char_next()
|
||||
end
|
||||
elseif parser:char_is('%s') then
|
||||
parser:char_next()
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
-- print(1+parser.row, 1+parser.col, parser:char())
|
||||
-- Keyword/identifier
|
||||
if parser:char_is('[%a-_]') then
|
||||
ident = ''
|
||||
while parser:char_is('[%a%d-_]') do
|
||||
ident = ident .. parser:char()
|
||||
parser:char_next()
|
||||
end
|
||||
parser.t = Token.keyword(ident)
|
||||
return
|
||||
-- String
|
||||
elseif parser:char_match('"') then
|
||||
string = ''
|
||||
while not parser:char_match('"') do
|
||||
string = string .. parser:char()
|
||||
parser:char_next()
|
||||
end
|
||||
parser.t = Token.string(string)
|
||||
return
|
||||
-- Single-char tokens
|
||||
elseif parser:char_match('%(') then
|
||||
parser.t = Token.lparen()
|
||||
elseif parser:char_match('%)') then
|
||||
parser.t = Token.rparen()
|
||||
elseif parser:char_match('{') then
|
||||
parser.t = Token.lbrace()
|
||||
elseif parser:char_match('}') then
|
||||
parser.t = Token.rbrace()
|
||||
elseif parser:char_match(':') then
|
||||
parser.t = Token.colon()
|
||||
elseif parser:char_match(',') then
|
||||
parser.t = Token.comma()
|
||||
end
|
||||
end
|
||||
function Parser.token_is(parser, kind)
|
||||
return parser:token().kind == kind
|
||||
end
|
||||
function Parser.token_kw_is(parser, name)
|
||||
return parser:token().kind == 'keyword' and parser:token().value == name
|
||||
end
|
||||
function Parser.token_match(parser, kind)
|
||||
if parser:token().kind == kind then
|
||||
parser:token_next()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function Parser.token_kw_match(parser, name)
|
||||
if parser:token().kind == 'keyword' and parser:token().value == name then
|
||||
parser:token_next()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Parser.new(filename)
|
||||
file = io.open(filename, 'rb')
|
||||
parser = Parser
|
||||
parser.file = file
|
||||
parser.filename = filename
|
||||
parser:char_next()
|
||||
parser:token_next()
|
||||
return parser;
|
||||
end
|
||||
|
||||
function parse_mf(mf_path)
|
||||
parser = Parser.new(mf_path)
|
||||
includes = {}
|
||||
platforms = {}
|
||||
apis = {}
|
||||
while parser:token().kind ~= 'eof' do
|
||||
if parser:token().kind ~= 'keyword' then
|
||||
print(('%s(%d, %d): Expected keyword'):format(parser.filename, 1+parser.row, 1+parser.col))
|
||||
end
|
||||
-- TODO: add error handling
|
||||
if parser:token_kw_match('include') then
|
||||
parser:token_match('{')
|
||||
while not parser:token_match('}') do
|
||||
inc = parser:token().value
|
||||
table.insert(includes, inc)
|
||||
parser:token_next()
|
||||
parser:token_match(',')
|
||||
end
|
||||
elseif parser:token_kw_match 'platform' then
|
||||
platform_name = parser:token().value
|
||||
parser:token_next()
|
||||
parser:token_match('(')
|
||||
platform_path = parser:token().value
|
||||
if platform_path:sub(1,1) == '/' then
|
||||
platform_path = platform_path:sub(2, -1)
|
||||
end
|
||||
parser:token_next()
|
||||
parser:token_match(')')
|
||||
parser:token_match('{')
|
||||
platform_includes = {}
|
||||
while not parser:token_match('}') do
|
||||
if parser:token_kw_match('tinyrt') then
|
||||
table.insert(platform_includes, path.join(platform_path, 'tinyrt.iface.h'))
|
||||
table.insert(platform_includes, 'tinyrt.h')
|
||||
parser:token_match('{')
|
||||
while not parser:token_match('}') do
|
||||
inc = parser:token().value
|
||||
table.insert(platform_includes, path.join(platform_path, inc))
|
||||
parser:token_next()
|
||||
end
|
||||
else
|
||||
inc = parser:token().value
|
||||
table.insert(platform_includes, path.join(platform_path, inc))
|
||||
parser:token_next()
|
||||
end
|
||||
parser:token_match(',')
|
||||
end
|
||||
table.insert(platforms, {
|
||||
name = platform_name,
|
||||
includes = platform_includes
|
||||
})
|
||||
elseif parser:token_kw_match 'api' then
|
||||
api_name = parser:token().value
|
||||
parser:token_next()
|
||||
parser:token_match('(')
|
||||
api_path = parser:token().value
|
||||
if api_path:sub(1,1) == '/' then
|
||||
api_path = api_path:sub(2, -1)
|
||||
end
|
||||
parser:token_next()
|
||||
parser:token_match(')')
|
||||
parser:token_match('{')
|
||||
api_includes = {}
|
||||
rt_deps = {}
|
||||
while not parser:token_match('}') do
|
||||
if parser:token_kw_match('tinyrt') then
|
||||
parser:token_match('{')
|
||||
while not parser:token_match('}') do
|
||||
dep = parser:token().value
|
||||
table.insert(rt_deps, dep)
|
||||
parser:token_next()
|
||||
end
|
||||
else
|
||||
inc = parser:token().value
|
||||
table.insert(api_includes, path.join(api_path, inc))
|
||||
parser:token_next()
|
||||
end
|
||||
parser:token_match(',')
|
||||
end
|
||||
table.insert(apis, {
|
||||
name = api_name,
|
||||
deps = rt_deps,
|
||||
includes = api_includes
|
||||
})
|
||||
else
|
||||
print(('%s(%d, %d): Unknown directive: %s'):format(parser.filename, 1+parser.row, 1+parser.col, parser:token().value))
|
||||
end
|
||||
end
|
||||
io.close(parser.file)
|
||||
return {
|
||||
includes = includes,
|
||||
platforms = platforms,
|
||||
apis = apis,
|
||||
}
|
||||
end
|
||||
|
||||
function print_r(arr, indentLevel)
|
||||
local str = ""
|
||||
local indentStr = ""
|
||||
if(indentLevel == nil) then
|
||||
print(print_r(arr, 0))
|
||||
return
|
||||
end
|
||||
for i = 0, indentLevel do
|
||||
indentStr = indentStr.." "
|
||||
end
|
||||
for index,value in pairs(arr) do
|
||||
if type(value) == "table" then
|
||||
str = str..indentStr..index..": \n"..print_r(value, (indentLevel + 1))
|
||||
else
|
||||
str = str..indentStr..index..": "..value.."\n"
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
-- mf = parse_mf('src/library.mf')
|
||||
-- print(print_r(mf, 0))
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
#include <cia_definitions.h>
|
||||
|
||||
#if _CIA_OS_LINUX()
|
||||
#include "linux/syscall.c"
|
||||
#include "linux/errno.c"
|
||||
#include "linux/entry.c"
|
||||
// TinyRT interface
|
||||
#include "linux/tinyrt.iface.h"
|
||||
#include "tinyrt.h"
|
||||
#include "linux/tinyrt.c"
|
||||
#elif _CIA_OS_WINDOWS()
|
||||
#error "Not implemented yet"
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#define MAX_ATEXIT_HANDLERS 32
|
||||
#define MAX_AT_QUICK_EXIT_HANDLERS 32
|
||||
|
||||
static void (*atexit_handlers[MAX_ATEXIT_HANDLERS])(void);
|
||||
static void (*at_quick_exit_handlers[MAX_AT_QUICK_EXIT_HANDLERS])(void);
|
||||
static u64 n_atexit_handlers = 0;
|
||||
static u64 n_at_quick_exit_handlers = 0;
|
||||
|
||||
int atexit(void (*func)(void)) {
|
||||
if(n_atexit_handlers == MAX_ATEXIT_HANDLERS) {
|
||||
return MAX_ATEXIT_HANDLERS;
|
||||
}
|
||||
atexit_handlers[n_atexit_handlers] = func;
|
||||
n_atexit_handlers += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at_quick_exit(void (*func)(void)) {
|
||||
if(n_at_quick_exit_handlers == MAX_AT_QUICK_EXIT_HANDLERS) {
|
||||
return MAX_AT_QUICK_EXIT_HANDLERS;
|
||||
}
|
||||
at_quick_exit_handlers[n_at_quick_exit_handlers] = func;
|
||||
n_at_quick_exit_handlers += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
noreturn void abort(void) {
|
||||
// TODO: Ideally do a debug trap if the process is being debugged
|
||||
_Exit(-1);
|
||||
}
|
||||
|
||||
noreturn void exit(int code) {
|
||||
for(u64 i = n_atexit_handlers-1; i-- != 0; ) {
|
||||
void (*handler)(void) = atexit_handlers[i];
|
||||
handler();
|
||||
}
|
||||
// TODO(bumbread): flush all the unflushed file streams
|
||||
// TODO(bumbread): close all file streams and delete temporary files
|
||||
rt_program_exit(code);
|
||||
}
|
||||
|
||||
noreturn void _Exit(int code) {
|
||||
rt_program_exit(code);
|
||||
}
|
||||
|
||||
noreturn void quick_exit(int code) {
|
||||
for(u64 i = n_at_quick_exit_handlers-1; i-- != 0; ) {
|
||||
void (*handler)(void) = at_quick_exit_handlers[i];
|
||||
handler();
|
||||
}
|
||||
rt_program_exit(code);
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
# Manifest file for the ciabatta build.
|
||||
|
||||
# Platform sections describe available platforms and
|
||||
# which files they need to include to function
|
||||
# API sections describe API subsets and their TinyRT
|
||||
# dependencies. If an API requires TinyRT module that
|
||||
# isn't implemented by the platform that API won't be included
|
||||
# in the final build
|
||||
|
||||
# This file is used to auto-generate ciabatta.c
|
||||
|
||||
# `include` section specifies the header files that ciabatta
|
||||
# implements
|
||||
# `platform` sections describe platforms, the directories
|
||||
# where the source code for these platforms resides and the
|
||||
# files that should be included in the build for that platform
|
||||
# (relative to "src" dir) as well as the location of TinyRT
|
||||
# implementation.
|
||||
# `api` sections describe modules, where the implementation resides,
|
||||
# the source files and the TinyRT modules, on which this module
|
||||
# depends. If the platform doesn't implement one of the dependencies
|
||||
# this module will not be included in the final build
|
||||
|
||||
include {
|
||||
"cia-def.h",
|
||||
"stdlib.h"
|
||||
}
|
||||
|
||||
platform linux("/linux") {
|
||||
"syscall.c",
|
||||
"errno.c",
|
||||
"entry.c",
|
||||
tinyrt {
|
||||
"tinyrt.c"
|
||||
}
|
||||
}
|
||||
|
||||
platform windows("/windows") {
|
||||
tinyrt {},
|
||||
}
|
||||
|
||||
api c11_program("/impl/c11-program") {
|
||||
"program.c",
|
||||
tinyrt {
|
||||
rt_api_program,
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include <cia_definitions.h>
|
||||
#include <cia-def.h>
|
||||
|
||||
// NOTE: These symbols are provided by the linker
|
||||
#define attribute_hidden __attribute__((__visibility__("hidden")))
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cia_definitions.h>
|
||||
|
||||
// Common errors
|
||||
#define RT_STATUS_OK 0 // No errors
|
||||
#define RT_ERROR_NOT_IMPLEMENTED -1 // Function not implemented
|
||||
|
|
Loading…
Reference in New Issue