From 7e6066ff85c7d9cab72d472f3599d6dd28c3f603 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Thu, 27 Jul 2023 09:17:57 +1100 Subject: [PATCH] Rewrite build script in python, use JSON5 for configuration --- .gitignore | 6 +- build.lua | 228 ------------------------------- build.py | 223 ++++++++++++++++++++++++++++++ readme.md | 27 ++-- scripts/manifest-parser.lua | 265 ------------------------------------ src/library.json | 66 +++++++++ src/library.mf | 48 ------- src/linux/tinyrt.json | 12 ++ src/linux/tinyrt.mf | 15 -- tests/empty.c | 2 +- 10 files changed, 318 insertions(+), 574 deletions(-) delete mode 100755 build.lua create mode 100755 build.py delete mode 100755 scripts/manifest-parser.lua create mode 100644 src/library.json delete mode 100644 src/library.mf create mode 100644 src/linux/tinyrt.json delete mode 100644 src/linux/tinyrt.mf diff --git a/.gitignore b/.gitignore index b0d82b7..0c97ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/build.lua b/build.lua deleted file mode 100755 index 73ec5fb..0000000 --- a/build.lua +++ /dev/null @@ -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 diff --git a/build.py b/build.py new file mode 100755 index 0000000..611a275 --- /dev/null +++ b/build.py @@ -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') diff --git a/readme.md b/readme.md index 385a885..3b2da85 100644 --- a/readme.md +++ b/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 ] [-l ] - [-t ] [--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 OS to compile for (linux, windows) - -l --library Type of library to compile (static, shared) - -t --test Compile a C file and link the library against it - --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 diff --git a/scripts/manifest-parser.lua b/scripts/manifest-parser.lua deleted file mode 100755 index bb9e633..0000000 --- a/scripts/manifest-parser.lua +++ /dev/null @@ -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)) diff --git a/src/library.json b/src/library.json new file mode 100644 index 0000000..ffd94ea --- /dev/null +++ b/src/library.json @@ -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 +} \ No newline at end of file diff --git a/src/library.mf b/src/library.mf deleted file mode 100644 index 007515f..0000000 --- a/src/library.mf +++ /dev/null @@ -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, - } -} diff --git a/src/linux/tinyrt.json b/src/linux/tinyrt.json new file mode 100644 index 0000000..58f809b --- /dev/null +++ b/src/linux/tinyrt.json @@ -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, + +} \ No newline at end of file diff --git a/src/linux/tinyrt.mf b/src/linux/tinyrt.mf deleted file mode 100644 index 6d02155..0000000 --- a/src/linux/tinyrt.mf +++ /dev/null @@ -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 diff --git a/tests/empty.c b/tests/empty.c index 925a5b1..9354f3c 100644 --- a/tests/empty.c +++ b/tests/empty.c @@ -1,5 +1,5 @@ -#include +#include #define STDOUT_FILENO 1 #define SYS_write 1