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
|
/a
|
||||||
/src/ciabatta.c
|
/src/ciabatta.c
|
||||||
a.out
|
a.out
|
||||||
|
@ -13,6 +14,7 @@ a.out
|
||||||
*.o
|
*.o
|
||||||
*.4coder
|
*.4coder
|
||||||
*.rdbg
|
*.rdbg
|
||||||
*.iface.h
|
/.venv
|
||||||
|
tinyrt-iface.h
|
||||||
test/
|
test/
|
||||||
test_folder/
|
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
|
with `clang`. It may be able to work with `gcc` with some minor adjustments
|
||||||
but I didn't test.
|
but I didn't test.
|
||||||
|
|
||||||
For executing the script you will need lua and some lua dependencies that
|
For executing the script you will need at least python 3.3 and the pyjson5 package
|
||||||
you can install with luarocks:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ luarocks install luafilesystem
|
$ pip install pyjson5
|
||||||
$ luarocks install lua-path
|
|
||||||
$ luarocks install argparse
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building
|
### 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:
|
it like `lua build.lua`. Reference for command-line options:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: build.lua [-h] [-c] [-o] [-r] [-p <platform>] [-l <library>]
|
Usage: build.py [-h] [--clean] [--test TEST]
|
||||||
[-t <test>] [--options <options>]
|
[--mode {debug,release}] [--target TARGET]
|
||||||
|
[--compiler {clang,cuik}] [--cflags [CFLAGS ...]]
|
||||||
Options:
|
Options:
|
||||||
-h, --help Show this help message and exit.
|
-h, --help show this help message and exit
|
||||||
-c, --clean Remove all the binaries before recompiling
|
--clean Remove all object files and binaries
|
||||||
-o, --only Do not compile ciabatta
|
--test TEST Compile ciabatta executable with given file
|
||||||
-r, --release Compile the release version (without it will compile everything in debug mode)
|
--mode, -m {debug,release} Select build configuration
|
||||||
-p --platform <platform> OS to compile for (linux, windows)
|
--target, -t TARGET Select target OS (default=host)
|
||||||
-l --library <library> Type of library to compile (static, shared)
|
--compiler, -c {clang,cuik} Select compiler
|
||||||
-t --test <test> Compile a C file and link the library against it
|
--cflags [CFLAGS ...] Pass additional compiler flags
|
||||||
--options <options> Additional options to provide to the executable
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## 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 STDOUT_FILENO 1
|
||||||
#define SYS_write 1
|
#define SYS_write 1
|
||||||
|
|
Loading…
Reference in New Issue