mirror of https://github.com/flysand7/ciabatta.git
Rewrite build script in python, use JSON5 for configuration
This commit is contained in:
parent
7329ca1f92
commit
7e6066ff85
|
@ -1,4 +1,5 @@
|
|||
bin
|
||||
/bin
|
||||
/lib
|
||||
/a
|
||||
/src/ciabatta.c
|
||||
a.out
|
||||
|
@ -13,6 +14,7 @@ a.out
|
|||
*.o
|
||||
*.4coder
|
||||
*.rdbg
|
||||
*.iface.h
|
||||
/.venv
|
||||
tinyrt-iface.h
|
||||
test/
|
||||
test_folder/
|
228
build.lua
228
build.lua
|
@ -1,228 +0,0 @@
|
|||
#!/usr/bin/env lua
|
||||
|
||||
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')
|
||||
parser:flag('-c --clean', 'Remove all the binaries before recompiling')
|
||||
parser:flag('-o --only', 'Do not compile ciabatta')
|
||||
parser:flag('-r --release', 'Compile the release version (without it will compile everything in debug mode)')
|
||||
parser:option('-p --platform', 'OS to compile for (linux, windows)')
|
||||
parser:option('-l --library', 'Type of library to compile (static, shared)')
|
||||
parser:option('-t --test', 'Compile a C file and link the library against it')
|
||||
parser:option('--options', 'Additional options to provide to the executable')
|
||||
local args = parser:parse()
|
||||
local is_clean = args.clean or false
|
||||
local is_only = args.only or false
|
||||
local is_release = args.release or false
|
||||
local platform = args.platform or 'linux'
|
||||
local library_type = args.library or 'static'
|
||||
local test_file = args.test
|
||||
local compiler_options = args.options or ''
|
||||
|
||||
-- Clean the build files if needed
|
||||
function rmdir(p)
|
||||
if not path.exists(p) then
|
||||
return
|
||||
end
|
||||
path.each(path.join(p,"*"), function(P) path.remove(P) end,
|
||||
{param = "f",delay = true,recurse = true,reverse = true})
|
||||
path.remove(p)
|
||||
end
|
||||
if is_clean then
|
||||
print('Cleaning files..')
|
||||
rmdir('lib')
|
||||
rmdir('bin')
|
||||
os.remove('a')
|
||||
os.remove('a.exe')
|
||||
end
|
||||
|
||||
-- If we only needed to clean the build files, just exit here
|
||||
if is_only then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
local assembler = 'nasm'
|
||||
local compiler = 'clang'
|
||||
local linker = 'ld'
|
||||
local includes = {'./include'}
|
||||
local compiler_defines = {}
|
||||
local compiler_flags = {'-nostdlib'}
|
||||
local ciabatta_lib = ''
|
||||
|
||||
-- Figure out additional flags
|
||||
if is_release then
|
||||
table.insert(compiler_flags, '-O2')
|
||||
table.insert(compiler_defines, 'NDEBUG')
|
||||
else
|
||||
table.insert(compiler_flags, '-g')
|
||||
table.insert(compiler_flags, '-O0')
|
||||
table.insert(compiler_defines, 'CIA_DEBUG')
|
||||
end
|
||||
if library_type == 'static' then
|
||||
ciabatta_lib = 'ciabatta.a'
|
||||
if platform == 'windows' then
|
||||
ciabatta_lib = 'ciabatta.lib'
|
||||
end
|
||||
elseif library_type == 'shared' then
|
||||
ciabatta_lib = 'ciabatta.so'
|
||||
if platform == 'windows' then
|
||||
ciabatta_lib = 'ciabatta.dll'
|
||||
end
|
||||
else
|
||||
print('Invalid library type: ' .. library_type)
|
||||
end
|
||||
|
||||
-- Turn flags into table
|
||||
function map(t, f)
|
||||
local t1 = {}
|
||||
local t_len = #t
|
||||
for i = 1, t_len do
|
||||
t1[i] = f(t[i])
|
||||
end
|
||||
return t1
|
||||
end
|
||||
function quote(str)
|
||||
return '"'..str..'"'
|
||||
end
|
||||
function prefix(prefix)
|
||||
return function(str)
|
||||
return prefix..str
|
||||
end
|
||||
end
|
||||
function prefix_quote(prefix)
|
||||
return function(str)
|
||||
return prefix..'"'..str..'"'
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate TinyRT interface file for the platform
|
||||
print('==> Generating TinyRT interface definitions')
|
||||
local tinyrt_manifest_path = path.join('src', platform, 'tinyrt.mf')
|
||||
if not path.exists(tinyrt_manifest_path) then
|
||||
print('ERROR: tinyrt manifest wasnt found: '..tinyrt_manifest_path)
|
||||
end
|
||||
tinyrt_iface_hdr = io.open(path.join('src', platform, 'tinyrt.iface.h'), 'wb')
|
||||
tinyrt_iface_hdr:write('\n')
|
||||
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()
|
||||
local has_impl = line_it()
|
||||
if has_impl == '0' or has_impl == '1' then
|
||||
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
|
||||
|
||||
end
|
||||
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, ' ')..' '..
|
||||
table.concat(map(compiler_defines, prefix('-D ')), ' ')..' '..
|
||||
table.concat(map(includes, prefix_quote('-I ')), ' ')..' '
|
||||
|
||||
print('==> Compiling ciabatta')
|
||||
print('Flags: ' .. cflags)
|
||||
|
||||
-- Functions for compiling, linking and assembling individual files
|
||||
function assemble(src, out)
|
||||
local format = 'elf64'
|
||||
if platform == 'windows' then
|
||||
format = 'win64'
|
||||
end
|
||||
local cmdline = 'nasm -f '..format..' "'..src..'" -o "'..out..'"'
|
||||
print('> '..cmdline)
|
||||
os.execute(cmdline)
|
||||
end
|
||||
|
||||
function compile(srcs, out, additional_flags)
|
||||
local flags = (additional_flags or '')..' '..cflags
|
||||
local inputs = table.concat(map(srcs, quote), ' ')
|
||||
local cmdline = 'clang '..flags..' '..inputs..' -o '..quote(out)..''
|
||||
print('> '..cmdline)
|
||||
os.execute(cmdline)
|
||||
end
|
||||
|
||||
function archive(srcs, out)
|
||||
os.remove(out)
|
||||
local inputs = table.concat(map(srcs, quote), ' ')
|
||||
local cmdline = 'llvm-ar -rcs '..quote(out)..' '..inputs
|
||||
print('> '..cmdline)
|
||||
os.execute(cmdline)
|
||||
end
|
||||
|
||||
-- Build ciabatta
|
||||
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')
|
||||
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)
|
||||
elseif library_type == 'shared' then
|
||||
print('SHARED OBJECTS NOT SUPPORTED YET')
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
if test_file then
|
||||
compile({test_file, 'lib/'..ciabatta_lib}, 'a', '-pie')
|
||||
end
|
|
@ -0,0 +1,223 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse # Parsing command-line args
|
||||
import platform # Checking current OS
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
import pyjson5
|
||||
|
||||
dependencies = [
|
||||
'nasm',
|
||||
'llvm-ar'
|
||||
]
|
||||
|
||||
# Parse command line arguments
|
||||
arg_parser = argparse.ArgumentParser('build.py')
|
||||
arg_parser.add_argument('--clean', action='store_true', help='Remove all object files and binaries')
|
||||
arg_parser.add_argument('--test', help='Compile ciabatta executable with given file')
|
||||
arg_parser.add_argument('--mode', '-m', choices=['debug', 'release'], default='debug', help='Select build configuration')
|
||||
arg_parser.add_argument('--target', '-t', help='Select target OS (default=host)')
|
||||
arg_parser.add_argument('--compiler', '-c', choices=['clang', 'cuik'], default='clang', help='Select compiler')
|
||||
arg_parser.add_argument('--cflags', nargs='*', default=[], help='Pass additional compiler flags')
|
||||
args = arg_parser.parse_args()
|
||||
compiler = args.compiler
|
||||
|
||||
print('==> Performing basic checks')
|
||||
|
||||
# Perform cleaning if required
|
||||
if args.clean:
|
||||
shutil.rmtree('bin')
|
||||
shutil.rmtree('lib')
|
||||
os.remove(os.path.join('src', 'ciabatta.c'))
|
||||
os.remove('a')
|
||||
sys.exit(0)
|
||||
|
||||
# Check host OS
|
||||
target = args.target
|
||||
if target is None:
|
||||
host_os = platform.system().lower()
|
||||
print(f" -> Compiling for host OS: '{host_os}'")
|
||||
if not os.path.exists(os.path.join('src', host_os)):
|
||||
print(f" -> [ERROR] OS '{host_os}' isn't implemented.")
|
||||
sys.exit(1)
|
||||
target = host_os
|
||||
|
||||
# Add compiler to dependencies
|
||||
dependencies.append(args.compiler)
|
||||
|
||||
# Check dependencies
|
||||
print('==> Checking dependencies')
|
||||
for dependency in dependencies:
|
||||
if shutil.which(dependency) is None:
|
||||
print(f" -> [ERROR] Missing dependency: '{dependency}'")
|
||||
sys.exit(1)
|
||||
print(' -> Everything OK')
|
||||
|
||||
# Figure out the flags
|
||||
includes = ['include']
|
||||
cc = ''
|
||||
cc_defines = []
|
||||
cc_flags = ['-nostdlib']
|
||||
crt_file = 'crt.lib'
|
||||
lib_file = 'cia.lib'
|
||||
|
||||
cc = args.compiler
|
||||
if args.mode == 'release':
|
||||
cc_flags.append('-O2')
|
||||
cc_defines.append('NDEBUG')
|
||||
else: # 'debug'
|
||||
cc_flags.append('-g')
|
||||
cc_flags.append('-O0')
|
||||
cc_defines.append('DEBUG')
|
||||
|
||||
# Generate TinyRT headers for the target platform
|
||||
print(f"==> Generating TinyRT header for {target}")
|
||||
tinyrt_config_path = os.path.join('src', target, 'tinyrt.json')
|
||||
tinyrt_apis = []
|
||||
try:
|
||||
print(f" -> Reading file '{tinyrt_config_path}'")
|
||||
with open(tinyrt_config_path, 'r') as tinyrt_config_file:
|
||||
tinyrt_config = pyjson5.load(tinyrt_config_file)
|
||||
except Exception as error:
|
||||
print(f" -> [ERROR] reading file '{tinyrt_config_path}'")
|
||||
print(f" * {error}")
|
||||
sys.exit(1)
|
||||
tinyrt_header_path = os.path.join('src', target, 'tinyrt-iface.h')
|
||||
try:
|
||||
print(f" -> Writing to file '{tinyrt_header_path}'")
|
||||
with open(tinyrt_header_path, 'w') as tinyrt_header_file:
|
||||
tinyrt_header_file.write('\n')
|
||||
tinyrt_header_file.write(f'// This file is AUTO-GENERATED from {tinyrt_config_path}\n')
|
||||
tinyrt_header_file.write('\n')
|
||||
for tinyrt_api in tinyrt_config:
|
||||
api_name = tinyrt_api
|
||||
is_defined = tinyrt_config[tinyrt_api]
|
||||
if is_defined:
|
||||
tinyrt_apis.append(api_name)
|
||||
is_defined_int = 1 if is_defined else 0
|
||||
tinyrt_header_file.write(f'#define {api_name.upper()} {is_defined_int}\n')
|
||||
except Exception as error:
|
||||
print(f" -> [ERROR] writing to file '{tinyrt_header_path}'")
|
||||
print(f" * {error}")
|
||||
sys.exit(1)
|
||||
print(' -> TinyRT header generated')
|
||||
|
||||
# Generate ciabatta header for the target platform and configuration
|
||||
print(f"==> Generating ciabatta.c")
|
||||
library_config_path = os.path.join('src', 'library.json')
|
||||
try:
|
||||
print(f" -> Reading file '{library_config_path}'")
|
||||
with open(library_config_path, 'r') as library_config_file:
|
||||
library_config = pyjson5.load(library_config_file)
|
||||
except Exception as error:
|
||||
print(f" -> [ERROR] reading file '{library_config_path}'")
|
||||
print(f" * {error}")
|
||||
sys.exit(1)
|
||||
ciabatta_header_path = os.path.join('src', 'ciabatta.c')
|
||||
try:
|
||||
print(f" -> Writing to file '{ciabatta_header_path}'")
|
||||
with open(ciabatta_header_path, 'w') as ciabatta_header:
|
||||
# Write heading
|
||||
ciabatta_header.write('\n')
|
||||
ciabatta_header.write('// This file is AUTO-GENERATED by library.json\n')
|
||||
ciabatta_header.write('\n')
|
||||
# Write includes
|
||||
for include in library_config['includes']:
|
||||
ciabatta_header.write(f'#include <{include}>\n')
|
||||
ciabatta_header.write('\n')
|
||||
# Write platform includes
|
||||
platform_config = None
|
||||
for platform in library_config['platforms']:
|
||||
if platform['name'] == target:
|
||||
platform_config = platform
|
||||
if platform_config is None:
|
||||
print(f" -> [ERROR] library config doesn't contain configuration for platform {target}")
|
||||
for include in platform_config['includes']:
|
||||
include_path = os.path.join(platform_config['path'], include)
|
||||
ciabatta_header.write(f'#include "{include_path}"\n')
|
||||
ciabatta_header.write(f'#include "{target}/tinyrt-iface.h"\n')
|
||||
ciabatta_header.write(f'#include "tinyrt.h"\n')
|
||||
for tinyrt_source in platform_config['tinyrt']:
|
||||
ciabatta_header.write(f'#include "{target}/{tinyrt_source}"\n')
|
||||
# Write API includes
|
||||
ciabatta_header.write('\n')
|
||||
for api in library_config['apis']:
|
||||
api_name = api['name']
|
||||
api_path = api['path']
|
||||
tinyrt_satisfied = True
|
||||
for req in api['reqs']:
|
||||
if not (req in tinyrt_apis):
|
||||
tinyrt_satisfied = False
|
||||
break
|
||||
if not tinyrt_satisfied:
|
||||
print(f" -> Not exporting API '{api_name}'")
|
||||
else:
|
||||
print(f" * Exporting API '{api_name}'")
|
||||
ciabatta_header.write(f'// Module {api_name}\n')
|
||||
for include in api['includes']:
|
||||
ciabatta_header.write(f'#include "{api_path}/{include}"\n')
|
||||
except Exception as error:
|
||||
print(f" -> [ERROR] writing file '{ciabatta_header_path}'")
|
||||
print(f" * {error}")
|
||||
sys.exit(1)
|
||||
|
||||
def quote(s):
|
||||
return '"' + s + '"'
|
||||
def prefix(prefix):
|
||||
return (lambda s: prefix + s)
|
||||
def prefix_quote(prefix):
|
||||
return (lambda s: prefix + '"' + s + '"')
|
||||
|
||||
cc_flags_str = ' '.join(
|
||||
cc_flags +
|
||||
list(map(prefix('-D '), cc_defines)) +
|
||||
list(map(prefix_quote('-I '), includes)))
|
||||
|
||||
print(f"==> Compiling {lib_file}")
|
||||
print(' * Compiler flags:', cc_flags_str)
|
||||
|
||||
def assemble(src, out):
|
||||
format = 'elf64'
|
||||
if target == 'windows':
|
||||
format = 'win64'
|
||||
cmdline = f'nasm -f "{format}" "{src}" -o "{out}"'
|
||||
print(' >', cmdline)
|
||||
code = os.system(cmdline)
|
||||
if code != 0:
|
||||
sys.exit(code)
|
||||
|
||||
def compile(srcs, out, extra_flags = ''):
|
||||
flags = cc_flags_str + ' ' + extra_flags + ' '.join(args.cflags)
|
||||
inputs = ' '.join(map(quote, srcs))
|
||||
cmdline = f'{compiler} {flags} {inputs} -o {quote(out)}'
|
||||
print(' >', cmdline)
|
||||
code = os.system(cmdline)
|
||||
if code != 0:
|
||||
sys.exit(code)
|
||||
|
||||
def archive(srcs, out):
|
||||
inputs = ' '.join(map(quote, srcs))
|
||||
cmdline = f'llvm-ar -rcs {quote(out)} {inputs}'
|
||||
print(' >', cmdline)
|
||||
code = os.system(cmdline)
|
||||
if code != 0:
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
|
||||
# Ciabatta build spec
|
||||
if not os.path.exists('lib'):
|
||||
os.mkdir('lib')
|
||||
if not os.path.exists('bin'):
|
||||
os.mkdir('bin')
|
||||
|
||||
p = os.path.join
|
||||
|
||||
assemble(p('src', 'linux', 'crt-entry.asm'), p('bin', 'crt-entry.o'))
|
||||
compile([p('src', 'linux', 'crt-ctors.c')], p('bin', 'crt-ctors.o'), '-fpic -c')
|
||||
compile([p('src', 'ciabatta.c')], p('bin', 'ciabatta.o'), '-fpic -c')
|
||||
archive([p('bin', 'ciabatta.o'), p('bin', 'crt-ctors.o'), p('bin', 'crt-entry.o')], p('lib', lib_file))
|
||||
|
||||
if args.test:
|
||||
compile([args.test, p('lib', lib_file)], 'a', '-pie')
|
27
readme.md
27
readme.md
|
@ -44,13 +44,10 @@ Before proceeding please note that ciabatta can only be compiled and used
|
|||
with `clang`. It may be able to work with `gcc` with some minor adjustments
|
||||
but I didn't test.
|
||||
|
||||
For executing the script you will need lua and some lua dependencies that
|
||||
you can install with luarocks:
|
||||
For executing the script you will need at least python 3.3 and the pyjson5 package
|
||||
|
||||
```
|
||||
$ luarocks install luafilesystem
|
||||
$ luarocks install lua-path
|
||||
$ luarocks install argparse
|
||||
$ pip install pyjson5
|
||||
```
|
||||
|
||||
### Building
|
||||
|
@ -59,17 +56,17 @@ On linux you can simply run `./build.lua` script. On windows you have to run
|
|||
it like `lua build.lua`. Reference for command-line options:
|
||||
|
||||
```
|
||||
Usage: build.lua [-h] [-c] [-o] [-r] [-p <platform>] [-l <library>]
|
||||
[-t <test>] [--options <options>]
|
||||
Usage: build.py [-h] [--clean] [--test TEST]
|
||||
[--mode {debug,release}] [--target TARGET]
|
||||
[--compiler {clang,cuik}] [--cflags [CFLAGS ...]]
|
||||
Options:
|
||||
-h, --help Show this help message and exit.
|
||||
-c, --clean Remove all the binaries before recompiling
|
||||
-o, --only Do not compile ciabatta
|
||||
-r, --release Compile the release version (without it will compile everything in debug mode)
|
||||
-p --platform <platform> OS to compile for (linux, windows)
|
||||
-l --library <library> Type of library to compile (static, shared)
|
||||
-t --test <test> Compile a C file and link the library against it
|
||||
--options <options> Additional options to provide to the executable
|
||||
-h, --help show this help message and exit
|
||||
--clean Remove all object files and binaries
|
||||
--test TEST Compile ciabatta executable with given file
|
||||
--mode, -m {debug,release} Select build configuration
|
||||
--target, -t TARGET Select target OS (default=host)
|
||||
--compiler, -c {clang,cuik} Select compiler
|
||||
--cflags [CFLAGS ...] Pass additional compiler flags
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
|
|
@ -1,265 +0,0 @@
|
|||
#!/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))
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
|
||||
// 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
|
||||
|
||||
includes: [
|
||||
"cia-def.h",
|
||||
"stdlib.h"
|
||||
],
|
||||
|
||||
platforms: [
|
||||
{
|
||||
name: "linux",
|
||||
path: "linux",
|
||||
includes: [
|
||||
"syscall.c",
|
||||
"errno.c",
|
||||
"entry.c"
|
||||
],
|
||||
tinyrt: [
|
||||
"tinyrt.c"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "windows",
|
||||
path: "windows",
|
||||
includes: [],
|
||||
tinyrt: [],
|
||||
},
|
||||
],
|
||||
|
||||
apis: [
|
||||
{
|
||||
name: "stdlib_program",
|
||||
path: "impl/stdlib-program",
|
||||
includes: [
|
||||
"program.c",
|
||||
],
|
||||
reqs: [
|
||||
"rt_api_program",
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
// END
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
|
||||
# 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 stdlib_program("/impl/stdlib-program") {
|
||||
"program.c",
|
||||
tinyrt {
|
||||
rt_api_program,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
|
||||
// This file contains TinyRT API subsets that are exported
|
||||
// for this operating system, one name per line. This file
|
||||
// is used in a build script to generate tinyrt_macro file
|
||||
// and to resolve higher-level API dependencies
|
||||
|
||||
rt_api_file: true,
|
||||
rt_api_program: true,
|
||||
rt_api_shell: false,
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
# This file contains TinyRT API subsets that are exported
|
||||
# for this operating system, one name per line. This file
|
||||
# is used in a build script to generate tinyrt_macro file
|
||||
# and to resolve higher-level API dependencies
|
||||
|
||||
# For example file = 1 means that the file API is
|
||||
# implemented on this operating system and during building
|
||||
# a header file will be generated containing definition of
|
||||
# the form
|
||||
# #define RT_API_FILE
|
||||
|
||||
rt_api_file = 1
|
||||
rt_api_program = 1
|
||||
rt_api_shell = 0
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include <cia_definitions.h>
|
||||
#include <cia-def.h>
|
||||
|
||||
#define STDOUT_FILENO 1
|
||||
#define SYS_write 1
|
||||
|
|
Loading…
Reference in New Issue