From 67dcb2b7f6f1c5d81707c0273d0e45691934a8e7 Mon Sep 17 00:00:00 2001 From: Adam Barton <7876348-rosingrind@users.noreply.gitlab.com> Date: Tue, 28 Mar 2023 22:57:14 +0000 Subject: [PATCH] Haxe Toolkit support --- playground/haxe/bytecode/.haxerc | 4 - playground/haxe/bytecode/package.json | 18 -- playground/haxe/bytecode/src/main.hxml | 2 - playground/haxe/bytecode/src/wscript | 14 +- playground/haxe/bytecode/wscript | 2 +- playground/haxe/executable/.haxerc | 4 - playground/haxe/executable/README.md | 19 ++ playground/haxe/executable/bin/wscript | 45 +--- .../haxe/executable/haxe_libraries/readme.txt | 1 - playground/haxe/executable/include/readme.txt | 1 + playground/haxe/executable/lib/readme.txt | 2 +- playground/haxe/executable/package.json | 18 -- playground/haxe/executable/src/main.hxml | 2 - playground/haxe/executable/src/wscript | 12 +- playground/haxe/executable/wscript | 4 +- waflib/extras/haxe.py | 245 ++++++++++-------- 16 files changed, 184 insertions(+), 209 deletions(-) delete mode 100644 playground/haxe/bytecode/.haxerc delete mode 100644 playground/haxe/bytecode/package.json delete mode 100644 playground/haxe/bytecode/src/main.hxml delete mode 100644 playground/haxe/executable/.haxerc create mode 100644 playground/haxe/executable/README.md delete mode 100644 playground/haxe/executable/haxe_libraries/readme.txt create mode 100644 playground/haxe/executable/include/readme.txt delete mode 100644 playground/haxe/executable/package.json delete mode 100644 playground/haxe/executable/src/main.hxml diff --git a/playground/haxe/bytecode/.haxerc b/playground/haxe/bytecode/.haxerc deleted file mode 100644 index 093ce54c..00000000 --- a/playground/haxe/bytecode/.haxerc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "4.1.4", - "resolveLibs": "scoped" -} \ No newline at end of file diff --git a/playground/haxe/bytecode/package.json b/playground/haxe/bytecode/package.json deleted file mode 100644 index abe82fad..00000000 --- a/playground/haxe/bytecode/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Haxe", - "version": "1.0.0", - "description": "", - "main": "index.js", - "directories": { - "lib": "lib" - }, - "scripts": { - "postinstall": "lix download" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "lix": "^15.10.1" - } -} diff --git a/playground/haxe/bytecode/src/main.hxml b/playground/haxe/bytecode/src/main.hxml deleted file mode 100644 index a567cc29..00000000 --- a/playground/haxe/bytecode/src/main.hxml +++ /dev/null @@ -1,2 +0,0 @@ --dce full --main Main.hx diff --git a/playground/haxe/bytecode/src/wscript b/playground/haxe/bytecode/src/wscript index 6ad108d6..25348d65 100644 --- a/playground/haxe/bytecode/src/wscript +++ b/playground/haxe/bytecode/src/wscript @@ -1,7 +1,15 @@ def configure(ctx): ctx.load('haxe') + ctx.check_haxe(mini='4.0.0', maxi='4.2.5') + ctx.check_haxe_pkg( + libs=['hashlink'], + uselib_store='HLR', + fetch=False) def build(ctx): - ctx.haxe( - source = 'main.hxml', - target = 'main.hl') + ctx.env.HAXE_FLAGS = ['-dce', 'full', '-main', 'Main'] + ctx( + compiler = 'HL', + source = 'Main.hx', + target = 'out.hl', + use = ['HLR']) diff --git a/playground/haxe/bytecode/wscript b/playground/haxe/bytecode/wscript index a753e5dd..6edc0a0b 100644 --- a/playground/haxe/bytecode/wscript +++ b/playground/haxe/bytecode/wscript @@ -1,5 +1,5 @@ top = '.' -out = 'bin/waf' +out = 'build' def configure(ctx): ctx.recurse('src') diff --git a/playground/haxe/executable/.haxerc b/playground/haxe/executable/.haxerc deleted file mode 100644 index 093ce54c..00000000 --- a/playground/haxe/executable/.haxerc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "4.1.4", - "resolveLibs": "scoped" -} \ No newline at end of file diff --git a/playground/haxe/executable/README.md b/playground/haxe/executable/README.md new file mode 100644 index 00000000..fcc7af2a --- /dev/null +++ b/playground/haxe/executable/README.md @@ -0,0 +1,19 @@ +# Using `HLC` source generation with `clang` + +## Environment +In this particular case, you need to have a distribution of `hashlink` in your system. After installing, you need to perform additional steps to pass required files for binary generation (in this case - to `clang`): + +- either add hashlink's `lib` folder to `ctx.env.LIBPATH_HL` +- or replace `lib` folder with a symlink to hashlink's `lib` folder +- either add hashlink's `include` folder to `ctx.env.INCLUDES_HL` +- or replace `include` folder with a symlink to hashlink's `include` folder + +## Targets +In this particular case, generated `.c` files are placed in separate `bin` subdirectory. This enhances your build transparency and allows you to add desired checks or perform additional operations with generated `.c` sources if needed, while keeping things in parallel. Keep this in mind if you're planning to extend your build layout with additional Haxe targets + +## Running assembled binaries +Assuming that you have a `hashlink` distribution and all relevant system paths are adjusted, you could easily run your binary and see resulting output of `Main.hx:3: hello`. Keep in mind that if you're using an official `hashlink` distribution, it doesn't come with static libs for linking - this means that your produced binary requires paths to `libhl.dll` (or `.so`/`.dylib` - depends on your system). Of course, there may be a use case when you're building `hashlink` from sources or using it as a portable distribution - in these cases, you could run your binary while pointing paths to your dynamic libraries with adding correct paths (`$PWD/lib/` for example) to: + +- `PATH` on windows +- `LD_LIBRARY_PATH` on linux +- `DYLD_LIBRARY_PATH` on macOS \ No newline at end of file diff --git a/playground/haxe/executable/bin/wscript b/playground/haxe/executable/bin/wscript index a27e4c1a..8757fdbb 100644 --- a/playground/haxe/executable/bin/wscript +++ b/playground/haxe/executable/bin/wscript @@ -1,43 +1,14 @@ -from waflib.TaskGen import feature - def configure(ctx): - ctx.load('clang_cl') - ctx.env.CFLAGS.extend(['/EHsc', '/O12', '/TC', '/GL', '/w', '/U __llvm__']) - for lib in ['msvcrt']: - ctx.check( - compiler='c', - lib=lib, - uselib_store='SYSTEM') - for lib in ['libhl']: - ctx.check( - compiler='c', - lib=lib, - use='HL', - uselib_store='HL') + ctx.load('clang') + ctx.check( + compiler='c', + lib='hl', + use='HL', + uselib_store='HL') def build(ctx): - ctx.env.LINKFLAGS.extend(['/NODEFAULTLIB:libcmt']) ctx.program( - source = ['waf/src/main.c'], + source = [ctx.bldnode.make_node('src/main/main.c')], includes = [ctx.env.ROOT_INCLUDE_DIR], target = 'app', - use = ['SYSTEM', 'HL']) - -@feature('cxxprogram', 'cprogram') -def call_me_static(self): - attr_name = 'source' - attr = getattr(self, attr_name, []) - if len(attr): - setattr(self, attr_name, []) - for x in self.to_list(attr): - node = self.path.make_node(x) - tg = self.bld.get_tgen_by_name(node.name) - if not tg: - self.bld.fatal('Could not find a task generator by the name %r' % x) - tg.post() - for tsk in tg.tasks: - for out in tsk.outputs: - if out.name.endswith('.c'): - self.create_compiled_task('c', out) - if not self.compiled_tasks: - self.fatal('Could not find a source file for for %r' % self.name) + use = ['HL']) diff --git a/playground/haxe/executable/haxe_libraries/readme.txt b/playground/haxe/executable/haxe_libraries/readme.txt deleted file mode 100644 index e2bc21ff..00000000 --- a/playground/haxe/executable/haxe_libraries/readme.txt +++ /dev/null @@ -1 +0,0 @@ -this directory is served by lix automatically and stores versions of used haxe libraries diff --git a/playground/haxe/executable/include/readme.txt b/playground/haxe/executable/include/readme.txt new file mode 100644 index 00000000..03fce35a --- /dev/null +++ b/playground/haxe/executable/include/readme.txt @@ -0,0 +1 @@ +place hashlink includes here (e.g. hlc.h) or replace this directory with symlink if using package manager - dedicated directory is used when native hashlink includes are used diff --git a/playground/haxe/executable/lib/readme.txt b/playground/haxe/executable/lib/readme.txt index e5e14441..22946ac9 100644 --- a/playground/haxe/executable/lib/readme.txt +++ b/playground/haxe/executable/lib/readme.txt @@ -1 +1 @@ -place hashlink c libraries here (e.g. libhl.lib) - dedicated directory is used for a case when hashlink libs are statically linked +place hashlink libraries here (e.g. libhl.so) or replace this directory with symlink if using package manager - dedicated directory is used when native hashlink libs are linked diff --git a/playground/haxe/executable/package.json b/playground/haxe/executable/package.json deleted file mode 100644 index abe82fad..00000000 --- a/playground/haxe/executable/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Haxe", - "version": "1.0.0", - "description": "", - "main": "index.js", - "directories": { - "lib": "lib" - }, - "scripts": { - "postinstall": "lix download" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "lix": "^15.10.1" - } -} diff --git a/playground/haxe/executable/src/main.hxml b/playground/haxe/executable/src/main.hxml deleted file mode 100644 index a567cc29..00000000 --- a/playground/haxe/executable/src/main.hxml +++ /dev/null @@ -1,2 +0,0 @@ --dce full --main Main.hx diff --git a/playground/haxe/executable/src/wscript b/playground/haxe/executable/src/wscript index 41209563..d147a470 100644 --- a/playground/haxe/executable/src/wscript +++ b/playground/haxe/executable/src/wscript @@ -1,13 +1,15 @@ def configure(ctx): ctx.load('haxe') - ctx.ensure_lix_pkg( - compiler='hx', + ctx.check_haxe(mini='4.0.0', maxi='4.2.5') + ctx.check_haxe_pkg( libs=['hashlink'], uselib_store='HLR') def build(ctx): - ctx.haxe( - source = 'main.hxml', + ctx.env.HAXE_FLAGS = ['-dce', 'full', '-main', 'Main'] + ctx( + compiler = 'HLC', + source = 'Main.hx', res = ctx.env.ROOT_RES_DIR, - target = 'main.c', + target = 'main', use = ['HLR']) diff --git a/playground/haxe/executable/wscript b/playground/haxe/executable/wscript index a7e30565..4f99d22a 100644 --- a/playground/haxe/executable/wscript +++ b/playground/haxe/executable/wscript @@ -2,11 +2,11 @@ top = '.' out = 'bin/waf' def configure(ctx): - ctx.env.ROOT_INCLUDE_DIR = ctx.path.get_bld().make_node('src').abspath() + ctx.env.ROOT_INCLUDE_DIR = ctx.path.get_bld().make_node('src').make_node('main').abspath() ctx.env.ROOT_RES_DIR = ctx.path.make_node('res').abspath() ctx.env.LIBPATH_HAXE = ctx.path.make_node('haxe_libraries').abspath() ctx.env.LIBPATH_HL = ctx.path.make_node('lib').abspath() - ctx.env.INCLUDES_HL = ['%hashlink%/hl-1.11.0-win/include'] + ctx.env.INCLUDES_HL = ctx.path.make_node('include').abspath() ctx.recurse('src') ctx.recurse('bin') diff --git a/waflib/extras/haxe.py b/waflib/extras/haxe.py index cb3ba6a9..4ff37457 100644 --- a/waflib/extras/haxe.py +++ b/waflib/extras/haxe.py @@ -1,131 +1,154 @@ -import os, re -from waflib import Utils, Task, Errors -from waflib.TaskGen import extension, taskgen_method, feature +import re + +from waflib import Utils, Task, Errors, Logs from waflib.Configure import conf +from waflib.TaskGen import extension, taskgen_method + +HAXE_COMPILERS = { + 'JS': {'tgt': '--js', 'ext_out': ['.js']}, + 'LUA': {'tgt': '--lua', 'ext_out': ['.lua']}, + 'SWF': {'tgt': '--swf', 'ext_out': ['.swf']}, + 'NEKO': {'tgt': '--neko', 'ext_out': ['.n']}, + 'PHP': {'tgt': '--php', 'ext_out': ['.php']}, + 'CPP': {'tgt': '--cpp', 'ext_out': ['.h', '.cpp']}, + 'CPPIA': {'tgt': '--cppia', 'ext_out': ['.cppia']}, + 'CS': {'tgt': '--cs', 'ext_out': ['.cs']}, + 'JAVA': {'tgt': '--java', 'ext_out': ['.java']}, + 'JVM': {'tgt': '--jvm', 'ext_out': ['.jar']}, + 'PYTHON': {'tgt': '--python', 'ext_out': ['.py']}, + 'HL': {'tgt': '--hl', 'ext_out': ['.hl']}, + 'HLC': {'tgt': '--hl', 'ext_out': ['.h', '.c']}, +} @conf -def libname_haxe(self, libname): - return libname +def check_haxe_pkg(self, **kw): + self.find_program('haxelib') + libs = kw.get('libs') + if not libs or not (type(libs) == str or (type(libs) == list and all(isinstance(s, str) for s in libs))): + self.fatal('Specify correct libs value in ensure call') + return + fetch = kw.get('fetch') + if not fetch is None and not type(fetch) == bool: + self.fatal('Specify correct fetch value in ensure call') -@conf -def check_lib_haxe(self, libname, uselib_store=None): - haxe_libs = [node.name for node in self.root.find_node('haxe_libraries').ant_glob()] - changed = False - self.start_msg('Checking for library %s' % libname) - if libname + '.hxml' in haxe_libs: - self.end_msg('yes') - else: - changed = True - try: - cmd = self.env.LIX + ['+lib', libname] - res = self.cmd_and_log(cmd) - if (res): - raise Errors.WafError(res) - else: - self.end_msg('downloaded', color = 'YELLOW') - except Errors.WafError as e: - self.end_msg('no', color = 'RED') - self.fatal('Getting %s has failed' % libname) + libs = [libs] if type(libs) == str else libs + halt = False + for lib in libs: + try: + self.start_msg('Checking for library %s' % lib) + output = self.cmd_and_log(self.env.HAXELIB + ['list', lib]) + except Errors.WafError: + self.end_msg(False) + self.fatal('Can\'t run haxelib list, ensuring halted') + return - postfix = uselib_store if uselib_store else libname.upper() - self.env['LIB_' + postfix] += [self.libname_haxe(libname)] - return changed + if lib in output: + self.end_msg(lib in output) + else: + if not fetch: + self.end_msg(False) + halt = True + continue + try: + status = self.exec_command(self.env.HAXELIB + ['install', lib]) + if status: + self.end_msg(False) + self.fatal('Can\'t get %s with haxelib, ensuring halted' % lib) + return + else: + self.end_msg('downloaded', color='YELLOW') + except Errors.WafError: + self.end_msg(False) + self.fatal('Can\'t run haxelib install, ensuring halted') + return + postfix = kw.get('uselib_store') or lib.upper() + self.env.append_unique('LIB_' + postfix, lib) -@conf -def check_libs_haxe(self, libnames, uselib_store=None): - changed = False - for libname in Utils.to_list(libnames): - if self.check_lib_haxe(libname, uselib_store): - changed = True - return changed - -@conf -def ensure_lix_pkg(self, *k, **kw): - if kw.get('compiler') == 'hx': - if isinstance(kw.get('libs'), list) and len(kw.get('libs')): - changed = self.check_libs_haxe(kw.get('libs'), kw.get('uselib_store')) - if changed: - try: - cmd = self.env.LIX + ['download'] - res = self.cmd_and_log(cmd) - if (res): - raise Errors.WafError(res) - except Errors.WafError as e: - self.fatal('lix download has failed') - else: - self.check_lib_haxe(kw.get('lib'), kw.get('uselib_store')) - -@conf -def haxe(bld, *k, **kw): - task_gen = bld(*k, **kw) + if halt: + self.fatal('Can\'t find libraries in haxelib list, ensuring halted') + return class haxe(Task.Task): - vars = ['HAXE', 'HAXE_VERSION', 'HAXEFLAGS'] - ext_out = ['.hl', '.c', '.h'] + vars = ['HAXE_VERSION', 'HAXE_FLAGS'] + ext_in = ['.hx'] - def run(self): - cmd = self.env.HAXE + self.env.HAXEFLAGS - return self.exec_command(cmd, stdout = open(os.devnull, 'w')) + def run(self): + cmd = self.env.HAXE + self.env.HAXE_FLAGS_DEFAULT + self.env.HAXE_FLAGS + return self.exec_command(cmd) + +for COMP in HAXE_COMPILERS: + # create runners for each compile target + type("haxe_" + COMP, (haxe,), {'ext_out': HAXE_COMPILERS[COMP]['ext_out']}) @taskgen_method -def init_haxe_task(self, node): - def addflags(flags): - self.env.append_value('HAXEFLAGS', flags) +def init_haxe(self): + errmsg = '%s not found, specify correct value' + try: + compiler = HAXE_COMPILERS[self.compiler] + comp_tgt = compiler['tgt'] + comp_mod = '/main.c' if self.compiler == 'HLC' else '' + except (AttributeError, KeyError): + self.bld.fatal(errmsg % 'COMPILER' + ': ' + ', '.join(HAXE_COMPILERS.keys())) + return - if node.suffix() == '.hxml': - addflags(self.path.abspath() + '/' + node.name) - else: - addflags(['-main', node.name]) - addflags(['-hl', self.path.get_bld().make_node(self.target).abspath()]) - addflags(['-cp', self.path.abspath()]) - addflags(['-D', 'resourcesPath=%s' % getattr(self, 'res', '')]) - if hasattr(self, 'use'): - for dep in self.use: - if self.env['LIB_' + dep]: - for lib in self.env['LIB_' + dep]: addflags(['-lib', lib]) + self.env.append_value( + 'HAXE_FLAGS', + [comp_tgt, self.path.get_bld().make_node(self.target + comp_mod).abspath()]) + if hasattr(self, 'use'): + if not (type(self.use) == str or type(self.use) == list): + self.bld.fatal(errmsg % 'USE') + return + self.use = [self.use] if type(self.use) == str else self.use -@extension('.hx', '.hxml') -def haxe_file(self, node): - if len(self.source) > 1: - self.bld.fatal('Use separate task generators for multiple files') + for dep in self.use: + if self.env['LIB_' + dep]: + for lib in self.env['LIB_' + dep]: + self.env.append_value('HAXE_FLAGS', ['-lib', lib]) - try: - haxetask = self.haxetask - except AttributeError: - haxetask = self.haxetask = self.create_task('haxe') - self.init_haxe_task(node) + if hasattr(self, 'res'): + if not type(self.res) == str: + self.bld.fatal(errmsg % 'RES') + return + self.env.append_value('HAXE_FLAGS', ['-D', 'resourcesPath=%s' % self.res]) - haxetask.inputs.append(node) - haxetask.outputs.append(self.path.get_bld().make_node(self.target)) +@extension('.hx') +def haxe_hook(self, node): + if len(self.source) > 1: + self.bld.fatal('Use separate task generators for multiple files') + return + + src = node + tgt = self.path.get_bld().find_or_declare(self.target) + + self.init_haxe() + self.create_task('haxe_' + self.compiler, src, tgt) @conf -def find_haxe(self, min_version): - npx = self.env.NPX = self.find_program('npx') - self.env.LIX = npx + ['lix'] - npx_haxe = self.env.HAXE = npx + ['haxe'] - try: - output = self.cmd_and_log(npx_haxe + ['-version']) - except Errors.WafError: - haxe_version = None - else: - ver = re.search(r'\d+.\d+.\d+', output).group().split('.') - haxe_version = tuple([int(x) for x in ver]) +def check_haxe(self, mini=None, maxi=None): + self.start_msg('Checking for haxe version') + try: + curr = re.search( + r'(\d+.?)+', + self.cmd_and_log(self.env.HAXE + ['-version'])).group() + except Errors.WafError: + self.end_msg(False) + self.fatal('Can\'t get haxe version') + return - self.msg('Checking for haxe version', - haxe_version, haxe_version and haxe_version >= min_version) - if npx_haxe and haxe_version < min_version: - self.fatal('haxe version %r is too old, need >= %r' % (haxe_version, min_version)) - - self.env.HAXE_VERSION = haxe_version - return npx_haxe - -@conf -def check_haxe(self, min_version=(4,1,4)): - if self.env.HAXE_MINVER: - min_version = self.env.HAXE_MINVER - find_haxe(self, min_version) + if mini and Utils.num2ver(curr) < Utils.num2ver(mini): + self.end_msg('wrong', color='RED') + self.fatal('%s is too old, need >= %s' % (curr, mini)) + return + if maxi and Utils.num2ver(curr) > Utils.num2ver(maxi): + self.end_msg('wrong', color='RED') + self.fatal('%s is too new, need <= %s' % (curr, maxi)) + return + self.end_msg(curr, color='GREEN') + self.env.HAXE_VERSION = curr def configure(self): - self.env.HAXEFLAGS = [] - self.check_haxe() - self.add_os_flags('HAXEFLAGS', dup = False) + self.env.append_value( + 'HAXE_FLAGS_DEFAULT', + ['-D', 'no-compilation', '-cp', self.path.abspath()]) + Logs.warn('Default flags: %s' % ' '.join(self.env.HAXE_FLAGS_DEFAULT)) + self.find_program('haxe')