From 1c1062a96ef1d1a978e109074c4453f53e5bafaf Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Fri, 25 Sep 2020 18:48:51 -0300 Subject: [PATCH] Add example of making C libraries in a test --- .gitignore | 3 ++- nelua/analyzer.lua | 8 ++++++-- nelua/ccompiler.lua | 14 +++++++------- nelua/cgenerator.lua | 2 +- nelua/configer.lua | 2 +- nelua/luacompiler.lua | 2 +- nelua/runner.lua | 13 +++++++++---- nelua/types.lua | 2 ++ spec/08-runner_spec.lua | 12 ++++++++++++ tests/libmylib.nelua | 31 +++++++++++++++++++++++++++++++ tests/mylib_test.nelua | 18 ++++++++++++++++++ tools/covreporter.lua | 2 +- 12 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 tests/libmylib.nelua create mode 100644 tests/mylib_test.nelua diff --git a/.gitignore b/.gitignore index 781aa291..f4ddbec0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ neluacfg.lua .neluacfg.lua /play nelua-lua -/pkg \ No newline at end of file +/pkg +/mylib_test diff --git a/nelua/analyzer.lua b/nelua/analyzer.lua index c037ab78..ee65af30 100644 --- a/nelua/analyzer.lua +++ b/nelua/analyzer.lua @@ -437,6 +437,8 @@ function visitors.Annotation(context, node, symbol) if objattr._type then objattr:update_fields() end + elseif name == 'codename' then + objattr.fixedcodename = params end node.done = true @@ -2163,8 +2165,10 @@ function visitors.FuncDef(context, node, polysymbol) if context.entrypoint and context.entrypoint ~= node then node:raisef("cannot have more than one function entrypoint") end - attr.codename = attr.name - attr.declname = attr.name + if not attr.fixedcodename then + attr.codename = attr.name + end + attr.declname = attr.codename context.entrypoint = node end end diff --git a/nelua/ccompiler.lua b/nelua/ccompiler.lua index 9f5cf36b..c0bba6fd 100644 --- a/nelua/ccompiler.lua +++ b/nelua/ccompiler.lua @@ -134,24 +134,24 @@ local function detect_binary_extension(ccinfo, cflags) if cflags:find('-shared') then return '.dll' else - return '.exe' + return '.exe', true end else if cflags:find('-shared') then return '.so' else - return '' + return '', true end end --luacov:enable end - function compiler.compile_binary(cfile, outfile, compileopts) local cflags = get_compiler_cflags(compileopts) local ccinfo = compiler.get_cc_info() - local binext = detect_binary_extension(ccinfo, cflags) - local binfile = outfile .. binext + local binext, isexe = detect_binary_extension(ccinfo, cflags) + local binfile = outfile + if not stringer.endswith(binfile, binext) then binfile = binfile .. binext end -- if the file with that hash already exists skip recompiling it if not config.no_cache then @@ -159,7 +159,7 @@ function compiler.compile_binary(cfile, outfile, compileopts) local binfile_mtime = fs.getmodtime(binfile) if cfile_mtime and binfile_mtime and cfile_mtime <= binfile_mtime then if not config.quiet then console.info("using cached binary " .. binfile) end - return binfile + return binfile, isexe end end @@ -178,7 +178,7 @@ function compiler.compile_binary(cfile, outfile, compileopts) io.stderr:write(stderr) end - return binfile + return binfile, isexe end function compiler.get_gdb_version() --luacov:disable diff --git a/nelua/cgenerator.lua b/nelua/cgenerator.lua index 30887171..736620a5 100644 --- a/nelua/cgenerator.lua +++ b/nelua/cgenerator.lua @@ -1464,7 +1464,7 @@ local function emit_main(ast, context) mainemitter:add_ln("}") mainemitter:dec_indent() - context:add_declaration('int nelua_main(int nelua_argc, char** nelua_argv);\n') + context:add_declaration('static int nelua_main(int nelua_argc, char** nelua_argv);\n') else mainemitter:inc_indent() mainemitter:add_traversal(ast) diff --git a/nelua/configer.lua b/nelua/configer.lua index 225213b0..0f2dfe0f 100644 --- a/nelua/configer.lua +++ b/nelua/configer.lua @@ -92,7 +92,6 @@ local function create_parser(args) local argparser = argparse("nelua", "Nelua 0.1") argparser:flag('-c --compile', "Compile the generated code only", defconfig.compile) argparser:flag('-b --compile-binary', "Compile the generated code and binaries only", defconfig.compile_binary) - --argparser:option('-o --output', "Output file when compiling") argparser:flag('-e --eval', 'Evaluate string code from input', defconfig.eval) argparser:flag('-l --lint', 'Only check syntax errors', defconfig.lint) argparser:flag('-q --quiet', "Don't print any information while compiling", defconfig.quiet) @@ -102,6 +101,7 @@ local function create_parser(args) argparser:flag('-d --debug', 'Run through GDB to get crash backtraces', defconfig.debug) argparser:flag('--no-cache', "Don't use any cached compilation", defconfig.no_cache) argparser:flag('--no-color', 'Disable colorized output in the terminal.', defconfig.no_color) + argparser:option('-o --output', 'Copy output file to desired path.') argparser:option('-D --define', 'Define values in the preprocessor') :count("*"):convert(convert_param, tabler.copy(defconfig.define or {})) argparser:option('-P --pragma', 'Set initial compiler pragma') diff --git a/nelua/luacompiler.lua b/nelua/luacompiler.lua index 0fe830dd..ea74f00d 100644 --- a/nelua/luacompiler.lua +++ b/nelua/luacompiler.lua @@ -34,7 +34,7 @@ function lua_compiler.compile_code(luacode, outfile) end function lua_compiler.compile_binary(luafile) - return luafile + return luafile, true end function lua_compiler.get_run_command(binaryfile, runargs) diff --git a/nelua/runner.lua b/nelua/runner.lua index cea0de54..84fb1fa7 100644 --- a/nelua/runner.lua +++ b/nelua/runner.lua @@ -105,9 +105,10 @@ local function run(argv, redirect) if not infile then infile = 'eval_' .. stringer.hash(code) end -- save the generated code - local outcachefile = fs.getcachepath(infile, config.cache_dir) + local outcacheprefix = fs.getcachepath(infile, config.cache_dir) + local outfile = config.output or outcacheprefix local compiler = generator.compiler - local sourcefile = compiler.compile_code(code, outcachefile, compileopts) + local sourcefile = compiler.compile_code(code, outcacheprefix, compileopts) if config.timing then console.debugf('compile code %.1f ms', timer:elapsedrestart()) @@ -117,10 +118,14 @@ local function run(argv, redirect) local dobinarycompile = config.compile_binary or dorun -- compile the generated code - local binaryfile + local binaryfile, isexe if dobinarycompile then - binaryfile = compiler.compile_binary(sourcefile, outcachefile, compileopts) + binaryfile, isexe = compiler.compile_binary(sourcefile, outfile, compileopts) + if not isexe then + if not config.quiet then console.info('library compiled!') end + return 0 + end if config.timing then console.debugf('compile binary %.1f ms', timer:elapsedrestart()) end diff --git a/nelua/types.lua b/nelua/types.lua index 30c1b980..eba9b4d6 100644 --- a/nelua/types.lua +++ b/nelua/types.lua @@ -58,6 +58,8 @@ Type.shape = shaper.shape { nickname = shaper.string:is_optional(), -- The actual name of the type used in the code generator when emitting C code. codename = shaper.string, + -- Fixed custom codename used in annotation. + fixedcodename = shaper.string:is_optional(), -- Symbol that defined the type, not applicable for primitive types. symbol = shaper.symbol:is_optional(), -- Node that defined the type. diff --git a/spec/08-runner_spec.lua b/spec/08-runner_spec.lua index 54db1d29..1f126739 100644 --- a/spec/08-runner_spec.lua +++ b/spec/08-runner_spec.lua @@ -139,4 +139,16 @@ it("program arguments", function() ]], 'a', 'b', 'c'}) end) +it("C libraries", function() + assert.run({'-o', 'libmylib', 'tests/libmylib.nelua'}) + assert.run({'-o', 'mylib_test', 'tests/mylib_test.nelua'}, [[mylib - init +mylib - in top scope +mylib - sum +the sum is: +3 +mylib - terminate]]) + os.remove('libmylib.so') + os.remove('mylib_test') +end) + end) diff --git a/tests/libmylib.nelua b/tests/libmylib.nelua new file mode 100644 index 00000000..84e5c91c --- /dev/null +++ b/tests/libmylib.nelua @@ -0,0 +1,31 @@ +-- Disable the garbage collector, +-- because libraries should not and cannot use the garbage collector, +-- this means that you are responsible to manually manage the memory. +## pragmas.nogc = true +-- Prefix used when declaring C symbols for this file. +-- With this an exported function named 'sum' will be exported as 'mylib_sum' +## pragmas.unitname = 'mylib' +-- Compilation flags needed for compiling C libraries. +## cflags '-shared -fPIC' + +print 'mylib - in top scope' + +local function sum(x: integer, y: integer): integer + print 'mylib - sum' + return x + y +end + +-- Initialize the library. +-- This function is marked as entry point, initialization code should be done here. +local function init() + -- Import and call nelua_main on initialization. + -- This function run all code in top scope and initializes global variables. + local function nelua_main(argc: cint, argv: *cstring): cint end + print 'mylib - init' + nelua_main(0, nilptr) +end + +-- Terminate the library. +local function terminate() + print 'mylib - terminate' +end diff --git a/tests/mylib_test.nelua b/tests/mylib_test.nelua new file mode 100644 index 00000000..bad31d81 --- /dev/null +++ b/tests/mylib_test.nelua @@ -0,0 +1,18 @@ +-- Add the current folder to the linker's library search path, +-- we expect that 'mylib' library is available in the current path. +## cflags "-L. -Wl,-rpath,'$ORIGIN'" +-- Link mylib C library. +## linklib 'mylib' + +-- Import mylib functions. +local function mylib_sum(x: integer, y: integer): integer end +local function mylib_init() end +local function mylib_terminate() end + +-- Run example. +mylib_init() +local a = mylib_sum(1, 2) +print('the sum is:') +print(a) +assert(a == 3) +mylib_terminate() diff --git a/tools/covreporter.lua b/tools/covreporter.lua index 9f8f8aed..20467f68 100644 --- a/tools/covreporter.lua +++ b/tools/covreporter.lua @@ -28,7 +28,7 @@ local function report_coverage(reportfile) body <- {| sfile* |} summary sfile <- {| {:name: '' -> 'file' :} '='+%s+!'Summary'{:file: [-/_.%w]+ :}%s+'='+%nl (miss / sline / eline)* |} miss <- !div '*'+'0 '{[^%nl]+} %nl -sline <- !div ' '* %d* ' '* {''} [^%nl]+ %nl +sline <- !div ' '* %d* ' '* {''} [^%nl]* %nl eline <- [ ]* {''} %nl div <- '==='+%s+[-/_.%w]+%s+'==='+%nl summary <- {| heading {| line+ |} footer |}