From a8bb2fc4b1386414b4f6ead9f6a5929f8cc44787 Mon Sep 17 00:00:00 2001 From: Alejandro Ramallo Date: Fri, 3 Nov 2023 16:30:16 -0400 Subject: [PATCH 1/2] Waf generator and some tests --- extensions/generators/Waf.py | 742 ++++++++++++++++++ tests/waf/test_basic/conanfile.py | 13 + tests/waf/test_basic/main.cpp | 6 + tests/waf/test_basic/test_basic.py | 41 + tests/waf/test_basic/wscript | 14 + tests/waf/test_flatbuffers/car.fbs | 14 + tests/waf/test_flatbuffers/car1.bin | Bin 0 -> 76 bytes tests/waf/test_flatbuffers/car2.bin | Bin 0 -> 80 bytes tests/waf/test_flatbuffers/conanfile.py | 19 + tests/waf/test_flatbuffers/main.cpp | 56 ++ .../waf/test_flatbuffers/test_flatbuffers.py | 41 + tests/waf/test_flatbuffers/wscript | 33 + tests/waf/test_ndk_cross/README.md | 4 + .../waf/test_ndk_cross/android_arm64.profile | 11 + tests/waf/test_ndk_cross/car.fbs | 14 + tests/waf/test_ndk_cross/conanfile.py | 14 + tests/waf/test_ndk_cross/mylib.cpp | 45 ++ tests/waf/test_ndk_cross/test_ndk_cross.py | 35 + tests/waf/test_ndk_cross/wscript | 25 + tests/waf/test_ndk_cross_advanced/README.md | 56 ++ .../android_ndk.profile | 9 + tests/waf/test_ndk_cross_advanced/car.fbs | 14 + .../waf/test_ndk_cross_advanced/conanfile.py | 14 + tests/waf/test_ndk_cross_advanced/mylib.cpp | 45 ++ .../test_ndk_cross_advanced.py | 38 + .../variants/arm32_debug.profile | 3 + .../variants/arm32_release.profile | 3 + .../variants/arm64_debug.profile | 3 + .../variants/arm64_release.profile | 3 + .../variants/x86_64_debug.profile | 3 + .../variants/x86_64_release.profile | 3 + .../variants/x86_debug.profile | 3 + .../variants/x86_release.profile | 3 + tests/waf/test_ndk_cross_advanced/wscript | 60 ++ tests/waf/test_sdl/conanfile.py | 18 + tests/waf/test_sdl/main.cpp | 40 + tests/waf/test_sdl/test_sdl.py | 36 + tests/waf/test_sdl/wscript | 22 + tests/waf/waf | 172 ++++ 39 files changed, 1675 insertions(+) create mode 100644 extensions/generators/Waf.py create mode 100644 tests/waf/test_basic/conanfile.py create mode 100644 tests/waf/test_basic/main.cpp create mode 100644 tests/waf/test_basic/test_basic.py create mode 100644 tests/waf/test_basic/wscript create mode 100644 tests/waf/test_flatbuffers/car.fbs create mode 100644 tests/waf/test_flatbuffers/car1.bin create mode 100644 tests/waf/test_flatbuffers/car2.bin create mode 100644 tests/waf/test_flatbuffers/conanfile.py create mode 100644 tests/waf/test_flatbuffers/main.cpp create mode 100644 tests/waf/test_flatbuffers/test_flatbuffers.py create mode 100644 tests/waf/test_flatbuffers/wscript create mode 100644 tests/waf/test_ndk_cross/README.md create mode 100644 tests/waf/test_ndk_cross/android_arm64.profile create mode 100644 tests/waf/test_ndk_cross/car.fbs create mode 100644 tests/waf/test_ndk_cross/conanfile.py create mode 100644 tests/waf/test_ndk_cross/mylib.cpp create mode 100644 tests/waf/test_ndk_cross/test_ndk_cross.py create mode 100644 tests/waf/test_ndk_cross/wscript create mode 100644 tests/waf/test_ndk_cross_advanced/README.md create mode 100644 tests/waf/test_ndk_cross_advanced/android_ndk.profile create mode 100644 tests/waf/test_ndk_cross_advanced/car.fbs create mode 100644 tests/waf/test_ndk_cross_advanced/conanfile.py create mode 100644 tests/waf/test_ndk_cross_advanced/mylib.cpp create mode 100644 tests/waf/test_ndk_cross_advanced/test_ndk_cross_advanced.py create mode 100644 tests/waf/test_ndk_cross_advanced/variants/arm32_debug.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/arm32_release.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/arm64_debug.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/arm64_release.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/x86_64_debug.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/x86_64_release.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/x86_debug.profile create mode 100644 tests/waf/test_ndk_cross_advanced/variants/x86_release.profile create mode 100644 tests/waf/test_ndk_cross_advanced/wscript create mode 100644 tests/waf/test_sdl/conanfile.py create mode 100644 tests/waf/test_sdl/main.cpp create mode 100644 tests/waf/test_sdl/test_sdl.py create mode 100644 tests/waf/test_sdl/wscript create mode 100755 tests/waf/waf diff --git a/extensions/generators/Waf.py b/extensions/generators/Waf.py new file mode 100644 index 0000000..97d56b8 --- /dev/null +++ b/extensions/generators/Waf.py @@ -0,0 +1,742 @@ +import os +from conan.internal import check_duplicated_generator +from conans.util.files import save + +class Waf(object): + def __init__(self, conanfile): + self.conanfile = conanfile + + def get_use_name(self, ref_name, parent_ref = None): + """ + see: https://github.com/conan-io/conan/issues/13611#issuecomment-1496300127 + + * :: refers to ALL components in a package + * refers to a component in the current package + * :: refers to a component in another package + """ + assert(type(ref_name) is str) + assert(not parent_ref or type(parent_ref) is str) + if '::' in ref_name: + sp = ref_name.split('::') + if sp[0] == sp[1]: + ref_name = sp[0] + else: + assert(len(sp[0])>0) #Conan bug? + ref_name = '_'.join(sp) + elif parent_ref: + ref_name = f'{parent_ref}_{ref_name}' + return ref_name \ + .replace('-', '_') \ + .replace('+','p') #"flac::libflac++" -> "flac_libflacpp" + + def generate(self): + check_duplicated_generator(self, self.conanfile) + + output = self.gen_deps() + + #add settings, which will be interpreted and applied at configuration time + settings = self.conanfile.settings.serialize() + print(settings) + output.update({ + "CONAN_SETTINGS": settings, + #paths that should be added to sys.path (only waf tools currently) + "DEP_SYS_PATHS": self._get_waftools_paths(), + #global Conan config/flags + "CONAN_CONFIG": self._get_conan_config() + }) + + filename = os.path.join( + self.conanfile.generators_folder, + "conan_deps.py" + ) + + set_conan_to_waf_arch(settings, output) + set_conan_to_waf_os(settings, output) + set_conan_to_waf_compiler(settings, output) + + deps = [] + for k,v in output.items(): + if type(v) is str: + deps.append(f'conf.env.{k} = "{v}"') + else: + deps.append(f'conf.env.{k} = {v}') + + #python syspath from deps (for distributing waf tools as conan packages) + syspaths = [] + for p in output['DEP_SYS_PATHS']: + syspaths.append(f'"{p}",') + + save(filename, waftool_src_template % ( + 'sys.path = [\n %s\n]+sys.path' % '\n '.join(syspaths), + '\n '.join(deps), + )) + + def gen_usedeps(self): + """ + Generate CONAN_USE_ vars for all components and packages + """ + out = {} + + def resolve_pkg(req, dep): + usename = self.get_use_name(dep.ref.name, dep.ref.name) + comp_deps = [] + pkg_deps = [] + if dep.has_components: + comp_deps = list(reversed(dep.cpp_info.get_sorted_components())) + + def gen_deps(self): + out = { + "ALL_CONAN_PACKAGES": [], + "ALL_CONAN_PACKAGES_BUILD": [], + } + depmap_host = {} + depmap_build = {} + + for req, dep in self.conanfile.dependencies.items(): + # print(dep.ref, f", direct={req.direct}, build={req.build}") + depmap = depmap_build if req.build else depmap_host + + if dep.cpp_info.has_components: + comp_depnames = [] + #generate "pkg::comp" for each comp + comps = dep.cpp_info.get_sorted_components().items() + for ref_name, cpp_info in comps: + use_name = self.get_use_name(ref_name, dep.ref.name) + comp_depnames.append(use_name) + depmap[use_name] = { + 'build': req.build, + 'cpp_info': cpp_info, + 'usename': use_name, + 'requires': [self.get_use_name(c, dep.ref.name) for c in cpp_info.requires], + 'package': self.get_use_name(dep.ref.name), + 'buildenv_info': dep.buildenv_info, + 'runenv_info': dep.runenv_info, + } + + #generate a parent "pkg::pkg" + use_name = self.get_use_name(dep.ref.name) + depmap[use_name] = { + 'build': req.build, + 'cpp_info': dep.cpp_info, + 'usename': use_name, + 'requires': comp_depnames, + 'package': self.get_use_name(dep.ref.name), + 'buildenv_info': dep.buildenv_info, + 'runenv_info': dep.runenv_info, + } + else: + #only generate "pkg" + use_name = self.get_use_name(dep.ref.name) + depmap[use_name] = { + 'build': req.build, + 'cpp_info': dep.cpp_info, + 'usename': use_name, + 'requires': [self.get_use_name(c, dep.ref.name) for c in dep.cpp_info.requires], + 'package': use_name, + 'buildenv_info': dep.buildenv_info, + 'runenv_info': dep.runenv_info, + } + + def toposort_deps(self, depmap, root, i): + out = [] + def visit(n): + if n.get('__visited', 0) == i: + return + assert n.get('__at', 0) != i, "Cyclic dependencies!\n\tusename: %s\n\trequires: %s" % (n['usename'], n['requires']) + n['__at'] = i + for req in n['requires']: + if req not in depmap: + continue + # assert req in depmap, "The following dependency for '%s' wasn't found: '%s'\n\tis the package broken, or am I broken?" % (n['usename'], req) + visit(depmap[req]) + n['__at'] = 0 + n['__visited'] = i + out.append(n) + visit(root) + return out + + sortit = 0 + + #generate host dep info (includes, flags, etc) + for name, info in depmap_host.items(): + sortit += 1 + sorted_deps = toposort_deps(self, depmap_host, info, sortit) + info['use'] = list(reversed(sorted_deps)) + self.proc_cpp_info(info, out) + + #collect bindirs + buildenv = {} + for name, info in depmap_build.items(): + sortit += 1 + sorted_deps = toposort_deps(self, depmap_build, info, sortit) + info['use'] = list(reversed(sorted_deps)) + self.proc_cpp_info(info, out) + + #add buildenv info + buildenv.update(info['buildenv_info'].vars(self.conanfile, scope="build")) + + buildenv['PATH'] = os.pathsep.join(out.get('CONAN_BUILD_BIN_PATH', set()) | {'$PATH'}) + buildenv['LD_LIBRARY_PATH'] = os.pathsep.join(out.get('CONAN_BUILD_LIB_PATH', set())) + buildenv['DYLD_LIBRARY_PATH'] = os.pathsep.join(out.get('CONAN_BUILD_LIB_PATH', set())) + + out['CONAN_BUILDENV'] = buildenv + + return out + + def proc_cpp_info(self, depinfo, out): + name = depinfo['usename'] + pkg_name = depinfo['package'] + cpp_info = depinfo['cpp_info'] + + def setvar(k, v): + if v: + out[f"{k}_{name}"] = v + + def setpath(k, v): + #convert relative paths from conan to absolute + if type(v) is list: + ret = [os.path.abspath(p) for p in v] + setvar(k, ret) + return ret + else: + assert type(v) is str + ret = [os.path.abspath(v)] + setvar(k, ret[0]) + return ret + + if depinfo['build']: + #add 'build_' prefix to all usenames for build items + #avoids name conflicts while making build graph available in scripts + name = f'build_{name}' + + out["ALL_CONAN_PACKAGES_BUILD"].append(name) + #CONAN_USE is used by waftool to expand deps for usenames + + setvar('CONAN_USE', ['build_%s' % d['usename'] for d in depinfo['use']]) + + #process build dependencies + abs_bindirs = setpath("BINPATH", cpp_info.bindirs) + if 'CONAN_BUILD_BIN_PATH' not in out: + out['CONAN_BUILD_BIN_PATH'] = set() + out['CONAN_BUILD_BIN_PATH'].update(abs_bindirs) + + abs_libdirs = setpath("LIBPATH", cpp_info.bindirs) + if 'CONAN_BUILD_LIB_PATH' not in out: + out['CONAN_BUILD_LIB_PATH'] = set() + out['CONAN_BUILD_LIB_PATH'].update(abs_libdirs) + else: + #process host dependencies + out["ALL_CONAN_PACKAGES"].append(name) + + #CONAN_USE is used by waftool to expand deps for usenames + setvar('CONAN_USE', [d['usename'] for d in depinfo['use']]) + + libs = cpp_info.libs + cpp_info.system_libs + cpp_info.objects + + #warning: default waf C/C++ tasks don't distinguish between exelink and + #sharedlink; will need to override `run_str`. For reference, see: + #`waflib.Tools.cxx.cxxshlib`. this could be handled by a waftool if it's + #ever needed + + linkflags = list(set(cpp_info.sharedlinkflags + cpp_info.exelinkflags)) + setvar("LINKFLAGS", linkflags) + setvar("LIB", libs) + setpath("LIBPATH", cpp_info.libdirs) + setvar("CFLAGS", cpp_info.cflags) + setvar("CXXFLAGS", cpp_info.cxxflags) + setvar("INCLUDES", cpp_info.includedirs) + setvar("DEFINES", cpp_info.defines) + setvar("FRAMEWORK", cpp_info.frameworks) + setpath("FRAMEWORKPATH", cpp_info.frameworkdirs) + + #Extra non-waf variables from Conan cpp_info + setpath("SRCPATH", cpp_info.srcdirs) + setpath("RESPATH", cpp_info.resdirs) + setpath("BUILDPATH", cpp_info.builddirs) + setpath("BINPATH", cpp_info.bindirs) + + #Unused waf variables: + # "ARCH" + # "STLIB" + # "STLIBPATH" + # "LDFLAGS" + # "RPATH" + # "CPPFLAGS" + + def _get_conan_config(self): + conf_info = self.conanfile.conf + out = { + "CFLAGS": conf_info.get('tools.build:cflags', [], check_type=list), + + "CXXFLAGS": conf_info.get('tools.build:cxxflags', [], check_type=list), + + "DEFINES": conf_info.get('tools.build:defines', [], check_type=list), + + "LINKFLAGS": + conf_info.get('tools.build:exelinkflags', [], check_type=list) + + conf_info.get('tools.build:sharedlinkflags', [], check_type=list), + } + return out + + def _get_waftools_paths(self): + #enables distributing waf tools inside of conan packages + #e.g. add a waf tool to the 'flatbuffers' package so that you can use + #the flatc compiler from wscripts, and not worry about versioning + out = [] + for require, dependency in self.conanfile.dependencies.items(): + if not require.build: + continue #only find waf tools from build environment + envvars = dependency.buildenv_info.vars(self.conanfile, scope="build") + if "WAF_TOOLS" not in envvars.keys(): + continue + + tools = envvars["WAF_TOOLS"].split(" ") + for entry in tools: + if not os.path.exists(entry): + self.outputs.warn(f"Waf tool entry not found: {entry}") + continue + + if os.path.isfile(entry): + entry = os.sep.join(entry.split(os.sep)[:-1]) + + if entry not in out: + out.append(entry) + return out + +def set_conan_to_waf_os(settings, env): + #try to map dest os to `waflib.Utils.unversioned_sys_platform()` outputs + #first, then fallback to just using the conan name directly + os = settings.get('os', None) + if os == None: + return + if os == 'Macos': + os = 'darwin' + elif os == 'Windows': + os = 'win32' + else: + os = os.lower() + env['DEST_OS'] = os + + version = settings.get('os.version') + if version: + env['DEST_OS_VERSION'] = version + + if os == 'win32' and 'os.subsystem' in settings: + env['WINDOWS_SUBSYSTEM'] = settings['os.subsystem'] + + if os == 'android': + env['ANDROID_MINSDKVERSION'] = settings.get('os.api_level') + + if os in ['ios', 'tvos', 'watchos']: + env['IOS_SDK_NAME'] = settings.get('os.sdk') + env['IOS_SDK_MINVER'] = settings.get('os.sdk_version') + +def set_conan_to_waf_compiler(settings, env): + compiler = settings.get('compiler', None) + if compiler == None: + return + libcxx = settings.get('compiler.libcxx') + runtime = settings.get('compiler.runtime') + runtime_type = settings.get('compiler.runtime_type') + threads = settings.get('compiler.threads') + exception = settings.get('compiler.exception') + + if threads or exception: + warn(f'WARNING: MinGW flags not handled yet!!') + + env['CXXFLAGS'] = env.get('CXXFLAGS', []) + + #GCC libstd ABI + if libcxx == 'libstdc++': + env['CXXFLAGS'].append('-D_GLIBCXX_USE_CXX11_ABI=0') + elif libcxx == 'libstdc++11': + env['CXXFLAGS'].append('-D_GLIBCXX_USE_CXX11_ABI=1') + + #Windows CRT + if runtime and runtime_type: + flag = 'M' + ('D' if runtime == 'dynamic' else 'T') + if runtime_type == 'Debug': + flag += 'd' + env['CXXFLAGS'].append(f'/{flag}') + + +def set_conan_to_waf_arch(settings, env): + #based on Conan settings.yml + `walib.Tools.c_config.MACRO_TO_DEST_CPU` + #note the original conan arch can be found in the 'CONAN_SETTINGS' key + arch = settings.get('arch', None) + if arch == None: + return + archmap = { + 'x86_64': [ + 'x86_64' + ], + 'x86': [ + 'x86' + ], + 'mips': [ + 'mips', + 'mips64' + ], + 'sparc': [ + 'sparc', + 'sparcv9', + ], + 'arm': [ + 'armv4', + 'armv4i', + 'armv5el', + 'armv5hf', + 'armv6', + 'armv7', + 'armv7hf', + 'armv7s', + 'armv7k', + 'armv8', + 'armv8_32', + 'armv8.3', + ], + 'powerpc': [ + 'ppc32be', + 'ppc32', + 'ppc64le', + 'ppc64', + ], + 'sh': [ + 'sh4le', + ], + 's390': [ + 's390', + ], + 's390x': [ + 's390x', + ], + 'xtensa': [ + 'xtensalx6', + 'xtensalx106', + 'xtensalx7', + ], + 'e2k': [ + 'e2k-v2', + 'e2k-v3', + 'e2k-v4', + 'e2k-v5', + 'e2k-v6', + 'e2k-v7', + ], + + #in waf, but not in standard conan settings.yml: + # '__alpha__' :'alpha', + # '__hppa__' :'hppa', + # '__convex__' :'convex', + # '__m68k__' :'m68k', + + #in conan, but not in waf + # avr + # asm.js + # wasm + } + env['DEST_CPU'] = arch + for wafname in archmap: + if arch in archmap[wafname]: + env['DEST_CPU'] = wafname + break + +waftool_src_template = """# AUTOGENERATED -- DO NOT MODIFY +import os, sys, pathlib +from waflib import Utils +from waflib.Logs import warn +from waflib.Configure import conf +from waflib.Tools.compiler_c import c_compiler +from waflib.Tools.compiler_cxx import cxx_compiler +from waflib.TaskGen import feature, before_method + +#---------------------python sys paths from dependencies-----------------------# +# ex: conf.load('some_tool', with_sys_path=True) +%s +#------------------------------------------------------------------------------# + +def configure(conf): + #-------------------configuration info from dependencies-------------------# + %s + #--------------------------------------------------------------------------# + + if conf.env.CONAN_BUILDENV: + import os + # os.environ.update(conf.env.CONAN_BUILDENV) + for k, v in conf.env.CONAN_BUILDENV.items(): + os.environ[k] = os.path.expandvars(v) + conf.environ[k] = os.path.expandvars(v) + + for p in conf.env.ALL_CONAN_PACKAGES: + conf.msg('Conan usename', p) + + _override_default_compiler_selection(conf, conf.env) + _apply_cppstd(conf, conf.env) + _apply_build_type(conf, conf.env) + + conf_info = conf.env.CONAN_CONFIG + def s(k): + if not conf.env[k]: + conf.env[k] = [] + conf.env[k].extend(conf_info[k]) + s('DEFINES') + s('CFLAGS') + s('CXXFLAGS') + s('LINKFLAGS') + +@feature("conan") +@before_method("process_use") +def expand_conan_targets(tg): + uselist = Utils.to_list(getattr(tg, 'use', [])) + for usename in uselist: + if (f'CONAN_USE_{usename}' in tg.env): + deps = tg.env[f'CONAN_USE_{usename}'] + else: + continue + [uselist.append(r) for r in deps if r not in uselist] + tg.use = uselist + + +# helper for installing files from conan packages +# +# def build(bld): +# bld.install_conan_package( +# '${PREFIX}', # install destination +# use = 'sdl', # list of conan packages to install +# recursive = False, # recursively install deps (default: True) +# bin = 'binaries', # install bins to '${PREFIX}/binaries' +# lib = 'lib', # (this is a default) +# include = None, # set to None to skip install +# res = None, +# framework = None +# ) + +@conf +def install_conan_package(bld, dest_root, **kw): + if bld.env.SKIP_INSTALL_CONAN_DEPS or not bld.is_install: + return + + use = Utils.to_list(kw.get('use')) + + dest_bin = kw.get('bin', 'bin') + dest_lib = kw.get('lib', 'lib') + dest_res = kw.get('res', 'res') + dest_include = kw.get('include', 'include') + dest_framework = kw.get('framework', 'framework') + + bin_out = set() + lib_out = set() + res_out = set() + include_out = set() + framework_out = set() + + reached = set() + def _get(name): + if name in reached: + return + reached.add(name) + if dest_bin: + bin_out.update(bld.env[f'BINPATH_{name}']) + if dest_lib: + lib_out.update(bld.env[f'LIBPATH_{name}']) + if dest_res: + res_out.update(bld.env[f'RESPATH_{name}']) + if dest_include: + include_out.update(bld.env[f'INCLUDES_{name}']) + if dest_framework: + framework_out.update(bld.env[f'FRAMEWORKPATH_{name}']) + + if kw.get('recursive', True): + dep_uselist = bld.env[f'CONAN_USE_{name}'] + for dep_name in dep_uselist: + _get(dep_name) + + for name in use: + _get(name) + + def _install(paths, dest): + for p in paths: + if os.path.isabs(p) and os.path.exists(p): + files = pathlib.Path(p).glob('**/*') + for file in files: + if file.is_dir(): + continue + d = file.relative_to(p) + node = bld.root.find_node(str(file)) + bld.install_as(f'{dest_root}/{dest}/{d}', node) + + _install(bin_out, dest_bin) + _install(lib_out, dest_lib) + _install(res_out, dest_res) + _install(include_out, dest_include) + _install(framework_out, dest_framework) + +def _override_default_compiler_selection(conf, env): + # The following adds the compiler name to the front of that c_config search + # list(s). This activates the auto-detection feature for the compiler name. + # Names are the names of waf tools (see waflib/Tools and waflib/extras for + # available compilers, e.g. "clangcxx.py") + + # If a compiler isn't supported, they can be configured manually with env + # variables, like [CC,CXX,AR,etc]. tool_requires packages with compilers can + # provide this in their `buildenv_info`, or in the [env] section of your + # build environment's Conan profile while calling `conan install` + + # NOTE: the following does nothing if using env for toolchain selection. + + compile_name_map = { + #Conan name: (C++ name, C name) + 'clang': ('clangxx', 'clang'), + 'apple-clang': ('clangxx', 'clang'), + 'gcc': ('gxx', 'gcc'), + 'msvc': ('msvc', 'msvc'), + 'sun-cc': ('suncxx', 'suncc'), + 'intel-cc': ('icpc', 'icc'), + 'qcc': (None, None), + 'mcst-lcc': (None, None), + } + compiler_name = env.CONAN_SETTINGS['compiler'] + (cxx, cc) = compile_name_map[compiler_name] + + os = Utils.unversioned_sys_platform() # waf uses build os for compiler detection + if cxx and os in cxx_compiler: + cxx_compiler[os].insert(0, cxx) + else: + assert 0, f"cxx: {cxx}, os: {os}" + + if cc and os in c_compiler: + c_compiler[os].insert(0, cc) + + # MSVC version and targets + if compiler_name == 'msvc': + # src: https://blog.knatten.org/2022/08/26/microsoft-c-versions-explained + # and: https://en.wikipedia.org/wiki/Microsoft_Visual_C++#Internal_version_numbering + version = str(env.CONAN_SETTINGS['compiler.version']) + msvc_vermap = { + '170': 11, + '180': 12, + '190': 14, + '191': 15, + '192': 16, + '193': 17, + } + arch = env.CONAN_SETTINGS['arch'] + if arch == 'x86_64': + arch = 'x64' + elif arch in ['armv4', 'armv4i', 'armv5el', 'armv5hf', 'armv6', 'armv7', 'armv7hf', 'armv7s', 'armv7k']: + arch = 'arm' + elif arch.startswith('arm'): + arch = 'arm64' + env['MSVC_VERSIONS'] = [f'msvc {msvc_vermap[version]}'] + env['MSVC_TARGETS'] = [arch] + +def _apply_cppstd(conf, env): + cppstd = env.CONAN_SETTINGS.get('compiler.cppstd', None) + if cppstd == None: + return + + compiler = env.CONAN_SETTINGS['compiler'] + + flags = { + 'gcc': { + '98': ['--std', 'c++98'], + 'gnu98': ['--std', 'gnu++98'], + '11': ['--std', 'c++11'], + 'gnu11': ['--std', 'gnu++11'], + '14': ['--std', 'c++14'], + 'gnu14': ['--std', 'gnu++14'], + '17': ['--std', 'c++17'], + 'gnu17': ['--std', 'gnu++17'], + '20': ['--std', 'c++20'], + 'gnu20': ['--std', 'gnu++20'], + '23': ['--std', 'c++23'], + 'gnu23': ['--std', 'gnu++23'], + }, + 'msvc': { + '14': ['/std:c++14'], + '17': ['/std:c++17'], + '20': ['/std:c++20'], + '23': ['/std:latest'] + }, + } + if compiler not in flags: + # gcc flags as fallback is probably fine... + env.append_value('CXXFLAGS', flags['gcc'][cppstd]) + else: + env.append_value('CXXFLAGS', flags[compiler][cppstd]) + +def _apply_build_type(conf, env): + build_type = env.CONAN_SETTINGS.get('build_type', None) + if build_type == None: + return + + os = env.CONAN_SETTINGS.get('os', '') + compiler = env.CONAN_SETTINGS.get('compiler', None) + if compiler == None: + _detect_default_waf_compiler_cxx(conf) + + cxxflags = [] + linkflags = [] + + if compiler == 'msvc': + if build_type == 'Debug': + cxxflags.extend([ + '/Zi', # generate PDBs + '/Od', # disable optimizations + ]) + linkflags.extend([ + '/debug' + ]) + elif build_type == 'Release': + cxxflags.extend([ + '/O2', # optimize speed + '/DNDEBUG' + ]) + linkflags.extend([ + '/incremental:no' # smaller output, functionally equivalent + ]) + elif build_type == 'RelWithDebInfo': + cxxflags.extend([ + '/Zi', + '/O2', + '/DNDEBUG' + ]) + linkflags.extend([ + '/debug' + ]) + elif build_type == 'MinSizeRel': + cxxflags.extend([ + '/O1', # optimize size + '/DNDEBUG' + ]) + linkflags.extend([ + '/incremental:no' + ]) + else: + # Use GCC flags for everything else + if build_type == 'Debug': + cxxflags.extend([ + '-g', # enable debug symbols + '-O0', # disable optimizations + ]) + elif build_type == 'Release': + cxxflags.extend([ + '-O3', # optimize speed + '-DNDEBUG', + ]) + elif build_type == 'RelWithDebInfo': + cxxflags.extend([ + '-g', + '-O2', + '-DNDEBUG', + ]) + elif build_type == 'MinSizeRel': + cxxflags.extend([ + '-Os', # optimize size + '-DNDEBUG', + ]) + + env.append_value('CXXFLAGS', cxxflags) + env.append_value('LINKFLAGS', linkflags) + +""" \ No newline at end of file diff --git a/tests/waf/test_basic/conanfile.py b/tests/waf/test_basic/conanfile.py new file mode 100644 index 0000000..347df7d --- /dev/null +++ b/tests/waf/test_basic/conanfile.py @@ -0,0 +1,13 @@ +import os, json +from conan import ConanFile +from conan.tools.files import copy + +class WafConanTestProject(ConanFile): + settings = "os", "compiler", "build_type", "arch" + requires = "spdlog/1.12.0" + + generators = ['Waf'] + # python_requires = "wafgenerator/0.1" + # def generate(self): + # gen = self.python_requires["wafgenerator"].module.Waf(self) + # gen.generate() \ No newline at end of file diff --git a/tests/waf/test_basic/main.cpp b/tests/waf/test_basic/main.cpp new file mode 100644 index 0000000..10fc535 --- /dev/null +++ b/tests/waf/test_basic/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char *argv[]){ + spdlog::info("Hello, waf! {}", argv[0]); + return 0; +} \ No newline at end of file diff --git a/tests/waf/test_basic/test_basic.py b/tests/waf/test_basic/test_basic.py new file mode 100644 index 0000000..73609f6 --- /dev/null +++ b/tests/waf/test_basic/test_basic.py @@ -0,0 +1,41 @@ +import shutil +import tempfile +import os, sys + +import pytest + +from tools import run + +@pytest.fixture(autouse=True) +def conan_test(): + old_env = dict(os.environ) + env_vars = {"CONAN_HOME": tempfile.mkdtemp(suffix='conans')} + os.environ.update(env_vars) + current = tempfile.mkdtemp(suffix="conans") + cwd = os.getcwd() + os.chdir(current) + try: + yield + finally: + os.chdir(cwd) + os.environ.clear() + os.environ.update(old_env) + +def test_waf_basic(): + repo = os.path.join(os.path.dirname(__file__), "../../..") + run(f"conan config install {repo}") + run("conan --help") + run("conan profile detect") + + os.chdir(os.path.dirname(__file__)) + run(f"{sys.executable} ../waf distclean") + + run("conan install . -of=build --build=missing") + run(f"{sys.executable} ../waf configure build -v") + dir_list = os.listdir('build') + if 'app' in dir_list: + run(os.path.join('build', 'app')) + elif 'app.exe' in dir_list: + run(os.path.join('build', 'app.exe')) + else: + assert 0, 'missing output' \ No newline at end of file diff --git a/tests/waf/test_basic/wscript b/tests/waf/test_basic/wscript new file mode 100644 index 0000000..d605466 --- /dev/null +++ b/tests/waf/test_basic/wscript @@ -0,0 +1,14 @@ +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('conan_deps', tooldir='build') + conf.load('compiler_cxx') + +def build(bld): + bld( + features = "cxx cxxprogram conan", + source = "main.cpp", + use = "spdlog", + target = "app" + ) diff --git a/tests/waf/test_flatbuffers/car.fbs b/tests/waf/test_flatbuffers/car.fbs new file mode 100644 index 0000000..5bc78c5 --- /dev/null +++ b/tests/waf/test_flatbuffers/car.fbs @@ -0,0 +1,14 @@ +namespace Test; + +table Manufacturer { + name:string; + coolness:uint8; +} + +table Car { + make:Manufacturer; + model:string; + year:uint16; +} + +root_type Car; \ No newline at end of file diff --git a/tests/waf/test_flatbuffers/car1.bin b/tests/waf/test_flatbuffers/car1.bin new file mode 100644 index 0000000000000000000000000000000000000000..0b3fd9549b87bc60b5f7b701f2697daedac62a56 GIT binary patch literal 76 zcmWe&00Axr0R|2R9tJib3j{v0%K%9hAZ7z%ztZ&d)Di};3JwN#APoj$AbD0G_Dyz9 HECSL1yYU6* literal 0 HcmV?d00001 diff --git a/tests/waf/test_flatbuffers/car2.bin b/tests/waf/test_flatbuffers/car2.bin new file mode 100644 index 0000000000000000000000000000000000000000..ebb195d125c79a151aafe4130041491761a5add3 GIT binary patch literal 80 zcmWe&00Axr0R|2R9tJib3j{u}%K%9hAZ7<*x19Wfg480g3J#zM2T%+Mv_bM9wsT^U Lf_G+KI!F)z>f#4x literal 0 HcmV?d00001 diff --git a/tests/waf/test_flatbuffers/conanfile.py b/tests/waf/test_flatbuffers/conanfile.py new file mode 100644 index 0000000..4403979 --- /dev/null +++ b/tests/waf/test_flatbuffers/conanfile.py @@ -0,0 +1,19 @@ +import os, json +from conan import ConanFile +from conan.tools.files import copy + +class WafConanTestProjectFlatbuffers(ConanFile): + name = "waf_conan_test_flatbuffers" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + + requires = "flatbuffers/23.5.26" + + #ensure 'flatc' is available in build environment + tool_requires = "flatbuffers/23.5.26" + + generators = ['Waf'] + # python_requires = "wafgenerator/0.1" + # def generate(self): + # gen = self.python_requires["wafgenerator"].module.Waf(self) + # gen.generate() \ No newline at end of file diff --git a/tests/waf/test_flatbuffers/main.cpp b/tests/waf/test_flatbuffers/main.cpp new file mode 100644 index 0000000..1191a4b --- /dev/null +++ b/tests/waf/test_flatbuffers/main.cpp @@ -0,0 +1,56 @@ +#include "car_generated.h" +#include +#include +#include +#include + +using namespace Test; + +void read_buf(const char *filename){ + std::ifstream file(filename, std::ios_base::binary); + file.seekg(0, std::ios_base::end); + size_t length = file.tellg(); + void *data = malloc(length); + file.seekg(0); + file.read((char*)data, length); + auto car = GetCar(data); + std::cout << "Model: " << car->model()->c_str() << std::endl; + std::cout << "Year: " << car->year() << std::endl; + std::cout << "Make: " << car->make()->name()->c_str(); + std::cout << " (" << (int)car->make()->coolness() << ")" << std::endl; + file.close(); + free(data); +} + +void write_buf( + const char *filename, + const char *make, + const char *model, + uint16_t year, + uint8_t coolness +){ + flatbuffers::FlatBufferBuilder builder(1024); + auto mfname = builder.CreateString(make); + auto mf = CreateManufacturer(builder, mfname, coolness); + auto m = builder.CreateString(model); + auto car = CreateCar(builder, mf, m, year); + builder.Finish(car); + + uint8_t *buf = builder.GetBufferPointer(); + int size = builder.GetSize(); + std::ofstream file(filename, std::ios_base::binary); + file.write((char*)buf, size); + file.close(); +} + +int main(int argc, char *argv[]){ + //write + write_buf("car1.bin", "McCar", "Nugget", 2033, 22); + write_buf("car2.bin", "Car King", "Flopper", 2032, 43); + + //read + read_buf("car1.bin"); + std::cout << "-----" << std::endl; + read_buf("car2.bin"); + return 0; +} \ No newline at end of file diff --git a/tests/waf/test_flatbuffers/test_flatbuffers.py b/tests/waf/test_flatbuffers/test_flatbuffers.py new file mode 100644 index 0000000..603c58f --- /dev/null +++ b/tests/waf/test_flatbuffers/test_flatbuffers.py @@ -0,0 +1,41 @@ +import shutil +import tempfile +import os, sys + +import pytest + +from tools import run + +@pytest.fixture(autouse=True) +def conan_test(): + old_env = dict(os.environ) + env_vars = {"CONAN_HOME": tempfile.mkdtemp(suffix='conans')} + os.environ.update(env_vars) + current = tempfile.mkdtemp(suffix="conans") + cwd = os.getcwd() + os.chdir(current) + try: + yield + finally: + os.chdir(cwd) + os.environ.clear() + os.environ.update(old_env) + +def test_waf_flatbuffers(): + repo = os.path.join(os.path.dirname(__file__), "../../..") + run(f"conan config install {repo}") + run("conan --help") + run("conan profile detect") + + os.chdir(os.path.dirname(__file__)) + run(f"{sys.executable} ../waf distclean") + + run("conan install . -of=build --build=missing") + run(f"{sys.executable} ../waf configure build -v") + dir_list = os.listdir('build') + if 'app' in dir_list: + run(os.path.join('build', 'app')) + elif 'app.exe' in dir_list: + run(os.path.join('build', 'app.exe')) + else: + assert 0, 'missing output' \ No newline at end of file diff --git a/tests/waf/test_flatbuffers/wscript b/tests/waf/test_flatbuffers/wscript new file mode 100644 index 0000000..4cffb91 --- /dev/null +++ b/tests/waf/test_flatbuffers/wscript @@ -0,0 +1,33 @@ +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('conan_deps', tooldir='build') + conf.load('compiler_cxx') + + #flatc will be found in conan cache + conf.find_program("flatc", var="FLATC") + +def build(bld): + #declare the expected output from flatc + gen_header = bld.path.get_bld().find_or_declare("generated/car_generated.h") + + #generate headers using our schema + bld( + rule = "${FLATC} --cpp -o ${TGT[0].parent} ${SRC}", + source = "car.fbs", + target = gen_header + ) + + #compile application + bld( + features = "cxx cxxprogram conan", + source = "main.cpp", + includes = gen_header.parent.abspath(), + use = "flatbuffers", + target = "app" + ) + + #waf's include scanner will detect that 'main.cpp' is importing a file + #generated by flatc (aka the `target` keyword above), and will ensure + #proper execution order \ No newline at end of file diff --git a/tests/waf/test_ndk_cross/README.md b/tests/waf/test_ndk_cross/README.md new file mode 100644 index 0000000..5daf5ef --- /dev/null +++ b/tests/waf/test_ndk_cross/README.md @@ -0,0 +1,4 @@ +```sh +conan install . -of=build --build=missing -pr:h=./android_arm64.profile -pr:b=default +../waf configure build +``` \ No newline at end of file diff --git a/tests/waf/test_ndk_cross/android_arm64.profile b/tests/waf/test_ndk_cross/android_arm64.profile new file mode 100644 index 0000000..4ac6ea0 --- /dev/null +++ b/tests/waf/test_ndk_cross/android_arm64.profile @@ -0,0 +1,11 @@ +[settings] +arch=armv8 +build_type=Release +compiler=clang +compiler.libcxx=c++_static +compiler.version=17 +os=Android +os.api_level=30 + +[tool_requires] +*: android-ndk/r26b diff --git a/tests/waf/test_ndk_cross/car.fbs b/tests/waf/test_ndk_cross/car.fbs new file mode 100644 index 0000000..5bc78c5 --- /dev/null +++ b/tests/waf/test_ndk_cross/car.fbs @@ -0,0 +1,14 @@ +namespace Test; + +table Manufacturer { + name:string; + coolness:uint8; +} + +table Car { + make:Manufacturer; + model:string; + year:uint16; +} + +root_type Car; \ No newline at end of file diff --git a/tests/waf/test_ndk_cross/conanfile.py b/tests/waf/test_ndk_cross/conanfile.py new file mode 100644 index 0000000..84ece30 --- /dev/null +++ b/tests/waf/test_ndk_cross/conanfile.py @@ -0,0 +1,14 @@ +import os, json +from conan import ConanFile +from conan.tools.files import copy + +class WafConanTestProjectNDK(ConanFile): + settings = "os", "compiler", "build_type", "arch" + requires = ["flatbuffers/23.5.26"] + build_requires = ["flatbuffers/23.5.26"] + + generators = ['Waf'] + # python_requires = "wafgenerator/0.1" + # def generate(self): + # gen = self.python_requires["wafgenerator"].module.Waf(self) + # gen.generate() \ No newline at end of file diff --git a/tests/waf/test_ndk_cross/mylib.cpp b/tests/waf/test_ndk_cross/mylib.cpp new file mode 100644 index 0000000..1299a87 --- /dev/null +++ b/tests/waf/test_ndk_cross/mylib.cpp @@ -0,0 +1,45 @@ +#include "car_generated.h" +#include +#include +#include +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mylib", __VA_ARGS__)) + +using namespace Test; + +void read_car(const char *filename){ + std::ifstream file(filename, std::ios_base::binary); + file.seekg(0, std::ios_base::end); + size_t length = file.tellg(); + void *data = malloc(length); + file.seekg(0); + file.read((char*)data, length); + auto car = GetCar(data); + LOGI("Model: (%s)", car->model()->c_str()); + LOGI("Year: (%d)", (int)car->year()); + LOGI("Make: %s (%d)", car->make()->name()->c_str(), (int)car->make()->coolness()); + file.close(); + free(data); +} + +void write_car( + const char *filename, + const char *make, + const char *model, + uint16_t year, + uint8_t coolness +){ + flatbuffers::FlatBufferBuilder builder(1024); + auto mfname = builder.CreateString(make); + auto mf = CreateManufacturer(builder, mfname, coolness); + auto m = builder.CreateString(model); + auto car = CreateCar(builder, mf, m, year); + builder.Finish(car); + + uint8_t *buf = builder.GetBufferPointer(); + int size = builder.GetSize(); + std::ofstream file(filename, std::ios_base::binary); + file.write((char*)buf, size); + file.close(); +} \ No newline at end of file diff --git a/tests/waf/test_ndk_cross/test_ndk_cross.py b/tests/waf/test_ndk_cross/test_ndk_cross.py new file mode 100644 index 0000000..95ea42a --- /dev/null +++ b/tests/waf/test_ndk_cross/test_ndk_cross.py @@ -0,0 +1,35 @@ +import shutil +import tempfile +import os, sys + +import pytest + +from tools import run + +@pytest.fixture(autouse=True) +def conan_test(): + old_env = dict(os.environ) + env_vars = {"CONAN_HOME": tempfile.mkdtemp(suffix='conans')} + os.environ.update(env_vars) + current = tempfile.mkdtemp(suffix="conans") + cwd = os.getcwd() + os.chdir(current) + try: + yield + finally: + os.chdir(cwd) + os.environ.clear() + os.environ.update(old_env) + +def test_waf_ndk_cross(): + repo = os.path.join(os.path.dirname(__file__), "../../..") + run(f"conan config install {repo}") + run("conan --help") + run("conan profile detect") + + os.chdir(os.path.dirname(__file__)) + run(f"{sys.executable} ../waf distclean") + + run("conan install . -of=build -pr:h=./android_arm64.profile -pr:b=default --build=missing") + run(f"{sys.executable} ../waf configure build -v") + # TODO: check if output lib matches profile arch diff --git a/tests/waf/test_ndk_cross/wscript b/tests/waf/test_ndk_cross/wscript new file mode 100644 index 0000000..02f1c59 --- /dev/null +++ b/tests/waf/test_ndk_cross/wscript @@ -0,0 +1,25 @@ +import os + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('conan_deps', tooldir='build') + conf.find_program("flatc", var="FLATC", mandatory=True) + conf.load('compiler_cxx') + +def build(bld): + gen_header = bld.path.get_bld().find_or_declare("generated/car_generated.h") + bld( + rule = "${FLATC} --cpp -o ${TGT[0].parent} ${SRC}", + source = "car.fbs", + target = gen_header + ) + bld( + features = "cxx cxxshlib conan", + source = "mylib.cpp", + includes = gen_header.parent.abspath(), + use = "flatbuffers", + target = "mylib" + ) + diff --git a/tests/waf/test_ndk_cross_advanced/README.md b/tests/waf/test_ndk_cross_advanced/README.md new file mode 100644 index 0000000..b911449 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/README.md @@ -0,0 +1,56 @@ +This is a more advanced cross compilation sample that allows targeting multiple +android ABIs at the same time. This is accomplished by creating separate +commands for each of the 4 ABIs (arm32, arm64, x86, x86_64) + +## Building + +Example for `arm32_debug`: + +```sh +#install ('-of' must be build/) +conan install . -of=build/arm32_debug -pr:h=android_ndk.profile -pr:h=variants/arm32_debug.profile -pr:b=default --build=missing + +#configure arm32 debug +waf configure_arm32_debug + +#build +waf build_arm32_debug +``` + +Alternatively, you can also have waf invoke `conan install` for you during +configuration: + +```sh +#configure arm32 debug +waf configure_arm32_debug --conan-install + +#build +waf build_arm32_debug +``` + +### Available variants: + +The following variants are available for this demo: + +* `arm32_debug` +* `arm32_release` +* `arm64_debug` +* `arm64_release` +* `x86_debug` +* `x86_release` +* `x86_64_debug` +* `x86_64_release` + +To run it, append the variant to the command, such as `configure_x86_debug`, +`clean_x86_debug`, etc. The available commands are: + +* `configure` +* `build` +* `clean` +* `install` (warning: this will try to install to your system by default, pass + `--prefix` to configure to change where they're installed) +* `uninstall` + +Each command corresponds to a partial Conan profile in the `variants` folder, +which is combined with the base profile `android_ndk.profile` to compose the +final profile used for each target. \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/android_ndk.profile b/tests/waf/test_ndk_cross_advanced/android_ndk.profile new file mode 100644 index 0000000..08f754e --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/android_ndk.profile @@ -0,0 +1,9 @@ +[settings] +compiler=clang +compiler.libcxx=c++_static +compiler.version=17 +os=Android +os.api_level=30 + +[tool_requires] +*: android-ndk/r26b diff --git a/tests/waf/test_ndk_cross_advanced/car.fbs b/tests/waf/test_ndk_cross_advanced/car.fbs new file mode 100644 index 0000000..5bc78c5 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/car.fbs @@ -0,0 +1,14 @@ +namespace Test; + +table Manufacturer { + name:string; + coolness:uint8; +} + +table Car { + make:Manufacturer; + model:string; + year:uint16; +} + +root_type Car; \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/conanfile.py b/tests/waf/test_ndk_cross_advanced/conanfile.py new file mode 100644 index 0000000..84ece30 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/conanfile.py @@ -0,0 +1,14 @@ +import os, json +from conan import ConanFile +from conan.tools.files import copy + +class WafConanTestProjectNDK(ConanFile): + settings = "os", "compiler", "build_type", "arch" + requires = ["flatbuffers/23.5.26"] + build_requires = ["flatbuffers/23.5.26"] + + generators = ['Waf'] + # python_requires = "wafgenerator/0.1" + # def generate(self): + # gen = self.python_requires["wafgenerator"].module.Waf(self) + # gen.generate() \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/mylib.cpp b/tests/waf/test_ndk_cross_advanced/mylib.cpp new file mode 100644 index 0000000..1299a87 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/mylib.cpp @@ -0,0 +1,45 @@ +#include "car_generated.h" +#include +#include +#include +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mylib", __VA_ARGS__)) + +using namespace Test; + +void read_car(const char *filename){ + std::ifstream file(filename, std::ios_base::binary); + file.seekg(0, std::ios_base::end); + size_t length = file.tellg(); + void *data = malloc(length); + file.seekg(0); + file.read((char*)data, length); + auto car = GetCar(data); + LOGI("Model: (%s)", car->model()->c_str()); + LOGI("Year: (%d)", (int)car->year()); + LOGI("Make: %s (%d)", car->make()->name()->c_str(), (int)car->make()->coolness()); + file.close(); + free(data); +} + +void write_car( + const char *filename, + const char *make, + const char *model, + uint16_t year, + uint8_t coolness +){ + flatbuffers::FlatBufferBuilder builder(1024); + auto mfname = builder.CreateString(make); + auto mf = CreateManufacturer(builder, mfname, coolness); + auto m = builder.CreateString(model); + auto car = CreateCar(builder, mf, m, year); + builder.Finish(car); + + uint8_t *buf = builder.GetBufferPointer(); + int size = builder.GetSize(); + std::ofstream file(filename, std::ios_base::binary); + file.write((char*)buf, size); + file.close(); +} \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/test_ndk_cross_advanced.py b/tests/waf/test_ndk_cross_advanced/test_ndk_cross_advanced.py new file mode 100644 index 0000000..4a744ec --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/test_ndk_cross_advanced.py @@ -0,0 +1,38 @@ +import shutil +import tempfile +import os, sys + +import pytest + +from tools import run + +@pytest.fixture(autouse=True) +def conan_test(): + old_env = dict(os.environ) + env_vars = {"CONAN_HOME": tempfile.mkdtemp(suffix='conans')} + os.environ.update(env_vars) + current = tempfile.mkdtemp(suffix="conans") + cwd = os.getcwd() + os.chdir(current) + try: + yield + finally: + os.chdir(cwd) + os.environ.clear() + os.environ.update(old_env) + +def test_waf_ndk_cross_advanced(): + repo = os.path.join(os.path.dirname(__file__), "../../..") + run(f"conan config install {repo}") + run("conan --help") + run("conan profile detect") + + os.chdir(os.path.dirname(__file__)) + run(f"{sys.executable} ../waf distclean") + + for variant in ['arm32', 'arm64', 'x86', 'x86_64']: + for build_type in ['release', 'debug']: + v = f'{variant}_{build_type}' + run(f"conan install . -of=build/{v} -pr:h=./android_ndk.profile -pr:h=variants/{v}.profile -pr:b=default --build=missing") + run(f"{sys.executable} ../waf configure_{v} build_{v} -v") + # TODO: check if output lib matches profile arch diff --git a/tests/waf/test_ndk_cross_advanced/variants/arm32_debug.profile b/tests/waf/test_ndk_cross_advanced/variants/arm32_debug.profile new file mode 100644 index 0000000..b85b141 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/arm32_debug.profile @@ -0,0 +1,3 @@ +[settings] +arch=armv7 +build_type=Debug \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/arm32_release.profile b/tests/waf/test_ndk_cross_advanced/variants/arm32_release.profile new file mode 100644 index 0000000..64f0027 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/arm32_release.profile @@ -0,0 +1,3 @@ +[settings] +arch=armv7 +build_type=Release \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/arm64_debug.profile b/tests/waf/test_ndk_cross_advanced/variants/arm64_debug.profile new file mode 100644 index 0000000..e7eaeaa --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/arm64_debug.profile @@ -0,0 +1,3 @@ +[settings] +arch=armv8 +build_type=Debug \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/arm64_release.profile b/tests/waf/test_ndk_cross_advanced/variants/arm64_release.profile new file mode 100644 index 0000000..5115530 --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/arm64_release.profile @@ -0,0 +1,3 @@ +[settings] +arch=armv8 +build_type=Release \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/x86_64_debug.profile b/tests/waf/test_ndk_cross_advanced/variants/x86_64_debug.profile new file mode 100644 index 0000000..efc996a --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/x86_64_debug.profile @@ -0,0 +1,3 @@ +[settings] +arch=x86_64 +build_type=Debug \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/x86_64_release.profile b/tests/waf/test_ndk_cross_advanced/variants/x86_64_release.profile new file mode 100644 index 0000000..57b31ad --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/x86_64_release.profile @@ -0,0 +1,3 @@ +[settings] +arch=x86_64 +build_type=Release \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/x86_debug.profile b/tests/waf/test_ndk_cross_advanced/variants/x86_debug.profile new file mode 100644 index 0000000..db6244b --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/x86_debug.profile @@ -0,0 +1,3 @@ +[settings] +arch=x86 +build_type=Debug \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/variants/x86_release.profile b/tests/waf/test_ndk_cross_advanced/variants/x86_release.profile new file mode 100644 index 0000000..571871f --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/variants/x86_release.profile @@ -0,0 +1,3 @@ +[settings] +arch=x86 +build_type=Release \ No newline at end of file diff --git a/tests/waf/test_ndk_cross_advanced/wscript b/tests/waf/test_ndk_cross_advanced/wscript new file mode 100644 index 0000000..ce1dafe --- /dev/null +++ b/tests/waf/test_ndk_cross_advanced/wscript @@ -0,0 +1,60 @@ +def options(opt): + opt.load('compiler_cxx') + opt.add_option('--conan-install', action='store_true', help='if true, will call conan install during configuration') + +def configure(conf): + if not conf.__class__.variant: + conf.fatal('must invoke with a variant, such as "configure_arm64_release" instead of "configure"') + conf.load_conan() + conf.load('compiler_cxx') + conf.find_program("flatc", var="FLATC", mandatory=True) + +def build(bld): + if not bld.variant: + bld.fatal(f'must invoke with a variant, such as "{bld.cmd}_arm64_release" instead of "{bld.cmd}"') + + gen_header = bld.path.get_bld().find_or_declare("generated/car_generated.h") + bld( + rule = "${FLATC} --cpp -o ${TGT[0].parent} ${SRC}", + source = "car.fbs", + target = gen_header + ) + bld( + features = "cxx cxxshlib conan", + source = "mylib.cpp", + includes = gen_header.parent.abspath(), + use = "flatbuffers", + target = "mylib" + ) + +#------------------------------------------------------------------------------# +# Command variant implementations +import os +from waflib.Configure import ConfigurationContext +from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext +for x in [p.split('.profile')[0] for p in os.listdir('variants')]: + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): + cmd = name + '_' + x + variant = x + class _tmp(ConfigurationContext): + cmd = 'configure_' + x + variant = x + def load_conan(self): + variant = self.__class__.variant + if self.options.conan_install: + variants = self.srcnode.find_node('variants') + assert variants, 'variants not found in %s' % self.srcnode + cmd = [ + 'conan', + 'install', + str(self.srcnode), + '-of', str(self.bldnode.find_or_declare(variant)), + '-pr:h', str(self.srcnode.find_node('android_ndk.profile')), + '-pr:h', str(variants.find_node(variant + '.profile')), + '-pr:b', 'default', + '--build=missing' + ] + assert not self.exec_command(cmd), f"conan install failed: {cmd}" + self.load('conan_deps', tooldir=f'build/{variant}') \ No newline at end of file diff --git a/tests/waf/test_sdl/conanfile.py b/tests/waf/test_sdl/conanfile.py new file mode 100644 index 0000000..86be172 --- /dev/null +++ b/tests/waf/test_sdl/conanfile.py @@ -0,0 +1,18 @@ +import os, json +from conan import ConanFile +from conan.tools.files import copy + +class WafConanTestProject(ConanFile): + name = "waf_conan_test_sdl" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + requires = "sdl/2.28.3" + + def configure(self): + self.options['sdl'].shared = True + + generators = ['Waf'] + # python_requires = "wafgenerator/0.1" + # def generate(self): + # gen = self.python_requires["wafgenerator"].module.Waf(self) + # gen.generate() \ No newline at end of file diff --git a/tests/waf/test_sdl/main.cpp b/tests/waf/test_sdl/main.cpp new file mode 100644 index 0000000..02bcf9e --- /dev/null +++ b/tests/waf/test_sdl/main.cpp @@ -0,0 +1,40 @@ +//taken from: https://gist.github.com/fschr/92958222e35a823e738bb181fe045274 +// SDL2 Hello, World! +// This should display a white screen for 2 seconds +// compile with: clang++ main.cpp -o hello_sdl2 -lSDL2 +// run with: ./hello_sdl2 +#define SDL_MAIN_HANDLED +#include +#include + +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 + +#ifdef __cplusplus +extern "C" +#endif +int main(int argc, char *argv[]){ + SDL_Window* window = NULL; + SDL_Surface* screenSurface = NULL; + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError()); + return 1; + } + window = SDL_CreateWindow( + "hello_sdl2", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + SCREEN_WIDTH, SCREEN_HEIGHT, + SDL_WINDOW_SHOWN + ); + if (window == NULL) { + fprintf(stderr, "could not create window: %s\n", SDL_GetError()); + return 1; + } + screenSurface = SDL_GetWindowSurface(window); + SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0xFF, 0xFF, 0xFF)); + SDL_UpdateWindowSurface(window); + SDL_Delay(2000); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} \ No newline at end of file diff --git a/tests/waf/test_sdl/test_sdl.py b/tests/waf/test_sdl/test_sdl.py new file mode 100644 index 0000000..20927a4 --- /dev/null +++ b/tests/waf/test_sdl/test_sdl.py @@ -0,0 +1,36 @@ +import shutil +import tempfile +import os, sys + +import pytest + +from tools import run + +@pytest.fixture(autouse=True) +def conan_test(): + old_env = dict(os.environ) + env_vars = {"CONAN_HOME": tempfile.mkdtemp(suffix='conans')} + os.environ.update(env_vars) + current = tempfile.mkdtemp(suffix="conans") + cwd = os.getcwd() + os.chdir(current) + try: + yield + finally: + os.chdir(cwd) + os.environ.clear() + os.environ.update(old_env) + +def test_waf_sdl(): + repo = os.path.join(os.path.dirname(__file__), "../../..") + run(f"conan config install {repo}") + run("conan --help") + run("conan profile detect") + + os.chdir(os.path.dirname(__file__)) + run(f"{sys.executable} ../waf distclean") + + run("conan install . -of=build --build=missing") + run(f"{sys.executable} ../waf configure build -v") + dir_list = os.listdir('build') + assert 'app' in dir_list or 'app.exe' in dir_list \ No newline at end of file diff --git a/tests/waf/test_sdl/wscript b/tests/waf/test_sdl/wscript new file mode 100644 index 0000000..4e900c7 --- /dev/null +++ b/tests/waf/test_sdl/wscript @@ -0,0 +1,22 @@ +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('conan_deps', tooldir='build') + conf.load('compiler_cxx') + +def build(bld): + bld( + features = "cxx cxxprogram conan", + source = "main.cpp", + use = "sdl", + target = "app" + ) + + bld.install_conan_package( + '${PREFIX}', + use='sdl', + recursive = False, + include = None, + bin = None + ) \ No newline at end of file diff --git a/tests/waf/waf b/tests/waf/waf new file mode 100755 index 0000000..5cd645b --- /dev/null +++ b/tests/waf/waf @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# encoding: latin-1 +# Thomas Nagy, 2005-2018 +# +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +import os, sys, inspect + +VERSION="2.0.26" +REVISION="76393535e97b42e706121768c1146b38" +GIT="ad7b733fc60852f77eff200b79e8b6f9562494d2" +INSTALL='' +C1='#1' +C2='#/' +C3='#.' +cwd = os.getcwd() +join = os.path.join + + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir, src): + f = open(src,'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ('Tools', 'extras'): + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + try: t.write(txt) + finally: t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + try: + for x in t: t.extract(x) + finally: + t.close() + + for x in ('Tools', 'extras'): + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.remove(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) + base, name = os.path.split(src) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + for dir in sys.path: + if test(dir): + return dir + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in (INSTALL,'/usr','/usr/local','/opt'): + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir, src) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SYl#. #.Pu((00b{#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.:TU+94ZFu%Rg}zjsPiӅ;t{U)%{<U졯]آ#.a=quZ97y>@:㵾v`;דw\#.#.#.#.籠 H#.#.#.휠4U-4#.IG@#.׽z#.¨+F#. #.QݎBPP#.:Pg7ۤF=իt跩]ْ)$wƻ}׽{(^>{fS;wI}{w{7{wvu/v=0cOb`P3n]gyv<wmݞJ잚nuPn-vz퍅ow\ϭwvvr:6OTs֯z8]g1;1%Nk/{ۘ+Լ>vޮmz{y9+p8/Yrn0מ^7obuw:<{Qm)!9k#1#.t#.4^mխڭxn=Փ{|@9j50#.FNy#/h)g5` V/Fln#/k@(fƺR]6Jv7.Lw:;z}=ymUϝ/7c;mg:G/gfmeuٞ{u} 9îu_M@@&#.Ld<̨OHmC#.h LԂS@ @@@Ȫ~ђMdjzzSGP4z#.#.#.M"DA#.S4AO%<=Sڏҍ2#.d@#.#.OT4#@2&T2S#/&C4h4hzP!#/44#.#.#.$4#.I &Si4'#.#.#.#.IE<ЙSTLSjzTzz#.#.#.h#.#.>]rOիMu~jw`$zu5ZdUkY'mj# 1-d=~~m}I]?\9IթU%O3*ya|*ϸڷV8'H+]qF7*#.N#.uT=+QUQ>1XztxT[<Wo\ &i q `b#.@jŴ`EF֭TjZڷ%U֨UBH>0R)b*0T2Pd PDm]UV63&BFfi"Z&eBAc (Ҋml(LA(jj-FmY4%dM4#.k404ŕ+"dZ)fK5!e6`33ԑZ$i6H(M4,1ѩȖY2b4̓0TB`&mhRQi-*Rd 5MJ()Fȑ"iE&ED6)6aDAR($FdD C"2Hڑ2DfAK+,IdhY61,e!#FҚA6$ȣ(fLH*J4Di$#1Ȋlh,E2E3HfIҙL,F#.`JMDD$I("DC!&PI)5eFa ԑ$"қ HE؀,(YQ6S"Ie 6D2$iJaQ`,iDXd(&E4MHfƓe53H)"hkP,$I2E"h&H3 Jbe E&L# eLYkԌ)+(QPE"$$cc*iňL)Y["(MLfAHc(CfM5*S&h6FHɑM(F2"(TRII4Ѥi$c`LF0LL12M&Qd,ȂSd1J ie&X K$d6* XR$cc1 2RXYJ&FM(ZhII$2ɲ#)E,ѥ14PhM&dDU`BjfF&RLȈ6,&e4b+ e&iBSk6HJۢ 1QX)Jj)PѢZm!6MFDdX!c()$[" -mbRij6HZ2ibŅH)dJh*6Uh-)Y2ȪU,0͌fѢȊ4ȕ2md6Rf5%#j,Sh$JZTĆVEj+"QDL,FыT mc`-&L(iR F24-5J&H-S[YbL6ɢM,[R6K)S6XVD[iT%)jjhk,XTk+*CPPʍ$HE"ņi01$Ch4&LBѪSQC&M&MY,R)4l#.J%3%4 AE$&Fm6cSH,-1$dԅĆA5Q%2)%P524i1f3dZ2ElI(2,؈I16K"QEFC1&e*e%,2V), jE54ECF)6ce0Qd T (M1HdL#/)SId&1QIiHdF32a3AɣT6(Y)X! mTTj(X̶L)hiiFj#16,e$*2cd2"6Ũh( RU&(mb!4!E$fIh*b2m(-&JHhfP(3%T؆lj#mME)4+֓)CD%2 Eƣdd&#JCY&`Vd#/*4̩ZJɶdi#1i&(P%bI,l4`& ږ#EEkF2m&DcRQbM L&4Ԉġ$ͨU%*YVJЛR-DlQ$UE3lڍbk+Z+iQQJj6f*i"4cX*H%lY6LTi-ѴZfb2Q1%4lSI6dhش[F542Y6!mIME26Pl#/ff5@$IeD#c':]?HV|Uu'H}@'N{̏(&I"H5%Y?,Ƴ Vfd?B'9|\Q`D 2%Z?7Ljg?_;#1u>9M)k>MΛ~r,z~}zL+{'296m~9E =P?\<4b!R[\:K#1(Wib7-Q6h¤kye>Y$|;\՝3޲q#1+ R)$H"c6%@)f|ʑsF_H`jm92<_`z:s4F>.r،b^=3}>ס>on¸E_P5H2Dush < rQo/}]eG1'="_Zj9Z ;4sES+t^;llOu^6P[W[LȶAO7z{Bin{;w,*t[U~9d:x鄕d6֠[aP+:Bj&wܣrJ2IG~/+^]_߽\<%'z)dEVcdm=yAoo89Hv3Qn#`]R#1g|eݒrg@C$:]cdTx}#d*$#1GP'MM©f5?m QYY#1֕b#>*zN h$]̃R"m>|?MNgb&cыK3&)|u#f{ߓ__/o܈*^GcJxD[.x_P**(7&xڹ)}Rm{+g<)#/*Ab䆰c)wnW{md('I뗉SWUD zŽz"Uw,Z+Ku~ƨ£=i{1hYȌcPi-4Y_xW~Y8*!Z[85N^#.B?G #'Ch_9E/ ֑"E`&Pԣ=HJHti8~T{𔬢)2Jqds\SOgξe1&n/ͤA.``V*NqgY#1"*YQ;|)g-N3Y#ŇT 뤰(M>\O0ÙU#/\% w KI{ ^{|^jRd("V^A1#q:R~:L_Z׶aĥQȪA$AQ;G0pcy ^!:`,`]ϮinɫmAn&E q#rcԺl Qd&H0cs*r绹S]#1#/[+.$YQBF7ֵ8DÐ&鎕ԾꅙYP:$91u& tC./̗tuXDS79Js#17\箚QzfudO7#1{k!iۿ4g<ڜpy]t荜}O֓;<}g#1VDZNӿ=GmϏ<4(-1} KRToI?DLSǧg >>#J]I&Y>Q h2ᗿI0ߒal#r<΅f[5aVs̷VuoC#1*lϫEzfNCUFGX~ 9>-le]hbT&I-ˁvlD T ^]o". _q%ϣv1<(11N#/xrqL'q5 ˪ouHq]vK}JNn/TW?m7\ʁdRL-'!p>(c QAA#1meX*AQZsɟkEaTV~2`sgZWwd1BATN wdveO#C[䂿z&_\ygdowv_:qW4:l;F}US`7ivje)&ޞ_Jq7): /–'2f>8n=}̒7uIqa,Fep&*H09L~:d)~]d#1X.#.R)#]/JdN<97.o1*NLmwyy N)/)JX?#1O#&~-<2DF/Г,TWߓ{#/U9Q9ͦm2;Is\D#.ӎ䫑y #. #1Գgjnr 6g>ruBLJXx!axt]2ܞ~R|H29bM@n@=9kA3kj1y>=C0#1Huҡ.Te2ow#/P?>=saYsMNs9f~~|aP_ә8As)U}.8/3gkkiTX~WǫY L ~ݱ wIz=fJdG h8tV= <39kåsuMjIӚO-=ruW.8-g#/.3Cl"SG>" Y03񂈒NzJ8GTU*C9Ho;%iP/°M&ٚC ڄcy>`Ð@fz-4āG!EUI 4qp_h4A[r BȲ!b"F?ӿe?hz6=?>VXi}UgOWׇtz'C\eenzEf~VCSVJ%@tcOgrCW I#J9aclyVH`)jRzcUix##/ 3szvt7T#1^ ,"s8O4-)ƚ2PDbbM噘.EXE?~ Y9w=`_ocϵ'`Ŷ'[kZTf="#/z=+:d9(1hu1~{.p"[?M~>Vܕ4.#1Kh~{}9O=+Z9|.36XJ̕2Yg"#1,o-|D^8%X3v JD6[+1#1 VJ)=XUb&-b8S\Ѿo@ʇ'$$(tէ(qkd(}&` ݛqm.i`.P@rݟ}9F#1yf|<-3hNIyqU o!Aڴѧ9<q)FbKMn"h cR=V^aD<0mָv#/jQ˹rTj( hv)"%+go\~o( B4as#ݎI˸}hv_7mMuҭҨ?,meƐ!8:Z@y*!"PDvŒfG2<1$ -4D*x^:4ka[i0]v^hRZiy֌(AI- IuLV?.:I|MbaObb#/ta@w|{$:^OƕAYfG#/7:Fº{t{f79IBS:7"M ɂ$(:P 91MR-2{"McNL,2-K垄ɒYQ.3|te:.^َZOY|Ԓ  ECYM]ٯ6MZ[b7αU׶eJL~1wb˓պ4k9.VE9˲GP+[o׆,TƀΊdgn䦣EI`aW7u_F̈ZU[S(K•?#/F΁1wwrCPC_#19\z(p-ˁ*gNcЁvQ5&i߯?eŚ[ٱvݮfO_}(yךȖu1|Aq\S0`o*sQ#XyA1dmzx) wW-XNźiDbzTgDuպ/ "Ƨ`yj-(l4zd">+Ss45#/-ރƛE睳8p({v3{p3Afn4eJNm _,CȮ-S5ǚ|y_}W"AJ5u.WXD+U GEx#/䣜Oж9 5:#/|~W>x)|?=ãW{xXBN/I`f(d1XHpdnR}mϷv;Mhke /&ߗ, $A%Guض-n1]D5$S\<-*9AG(䟝:@jʒ-ɓt>?OӨl@睱Nz]Y:&ک#15+XeuYq8'r1p! #.A$o!MpY>6۩bcc e[1?V(߃LEIM-vcxoY^O<E S#.ƐSDDu?/ w .c>aA]l1=+[&1#1#5@jE0ii!/~^M& .A}F##/-?ӭ& 0%;#.b1_Eag#1L?Ooܵ_O c1"B1#Ǒ JKj]XctlnX)nC${35#. Vqr#."DlG KW͐sP=H ^#^lSPd(ܝ >[>:~@`t]q-]md_}TU#1/\_{(H ;0 gs@W9PLC)yY[ryKԘ#/Uu3IUAB"!$r +*+A_ΧleLa#1!'6)zvvUʢ.HŁe UQu0+M9!'yΚA@Hk #/#./c|zׇl]%pps{l#. &n$@E#1JБCl,5 S ՛;3G%#.JkUIw}ކ5lB>#.AEiM4j*gg?tk7#ǣ@C(3`D䘧x5)i^-O`e#/$i:F6tj8b,$YZɟ:uXuC:=gh5!$ۃosP̮Éșv%]њx_hO:/,aYVIBw;>psq;] :f їFjsz_"}^L7IP_k8n5ϢIRnL|_n¿{P"1@`޺~{!*u*SzԆdd,N03'bB")#1ly0ܡDY(n5A"%0gs#- L}}#1X"b*?xg3(N?78w nȐJ1׊p#s|#.|䶾 V8I#.+.BVh'05bNǔӏ(tۛB|0asˆaFW4HqF8]}Ql,P~ZI*ۖ;JQPU+jꦪ'*ua=%T2V 'ݦ9MhR}=qI ѳꆄ4#/䆨6mшvd*GNf:20MFb/P0P<шu Spە7*:d|{,ukp$qJW&]{u+fZ9ěHeIӯ^wKӭ3e{*OLYDS1>RaL{pLG#/ps a3Fz}?<$>ҙS;׻G|ii:f ohc8ۯ*]RD@ B2e(EEm,/wZx376@$PblN"o+}f{g{~q#.#1+aL-0ٚaRˈ>zM&XdJkP΅StKYT~>dwkg-#1qC`ʣ%D(qa^˰qTG fhMz`M$.l1\v!-'#/KJC,=`i`#1Yj }".Dq߮С0`úB‘>?O_yK,D"3Hv;UMڧ;{zJZv#/t_>]J^ BbEOэ0P"=ё"s^W#1L.:06$E#/ʥV&rg wT747ed<9f#1vñ b#/fT<ȧ#.%ħDnU2Mzw9z:+tp@j`N'#Zԟ>X$sOqoڎ$H$y#1$#TҬa1гrUP-"DӡGq:je4D&Fhd|\>?lG/]#1F\sx#. 5Z''3I*j=!1]RTba~Ů}!v_d\eNrO;NuBqƟ"tEfL$CDw- J0˰ f0Bͷlݡoe)4J8mʎYZNm) ǯٜ'ayh) dCf5 *|l*|R:jdz)ӐN91,8x0ԬeM8Z$P$*BqNݨ";#a*)䡼mikߚ㾢<4mLV$~˘g(qk^+Ir]QNGw o3>}B>d*-J1C>7P؄4`1u8saZL$ NpȻq!XUzLi&vò>#/h`Ɯ(2>lV˄`ñ ">_#/xy:#K+R$vh<*>/gQ)Dѣ?9LԈ/-F/qdFҟ2UTd$IU1?>Ӣ=أ?L|#1 A"B(})z{|>*|#/g҇۾OUNKM,tQmPbX#1Uj|4[5bM-Zb5r+F6ۦ`; #1T5d̴L-Dmd҆K_jNf֡zi+mח(;gy#.'@J@MǙo[L:#.C*ɷx _ljd>;e Ի!ԡH6ơX36N},m`;K#1)̞h5AëN|F~Ah-5NawI*W4q t K#1*k|?'1r/r[L9(&_DWdBJU Zw"F]p a'o#/8$;3y v8y.q'VǾ麗~ڻoohU@haOqqag/uOת zHg/Ppp{:cBk?+JϒaA* ^BȊně6h/>_ۥ5q37G;ursL3i_#/22]h(q_j!7 pK#/z#.%V5 Xjե #1@J_k+SAQқ9=!y#aP،:LڒɢL@fʖ%q4ثd%,IϏцx_4eJȤ8G@[#1:gg'X"ӵi}=PӟN#1-Jx?[P<|?y(ygA$db#.TNsK##1 cyLWW*DR@D`D]8>l|KJ4`F/N8{gcmk酸p{xȈe!'&kie'{Kwg+BV=Jp5+#.zò+Y\NU@r(PPQP)Ksi2{`pSCp iI'l((P>>>G AŒa7YT"v&z3ӆnmq~.n ڒHMP]e0tDD9l9KJC'ot=ZT~Cy~O쀏K_||S*gc+>c3g7zC.EwK65޶F˹G0gEs7/Kک9c#ĹLDO]G=}\R,i=y?m]Stw5x&]%kv,Jpử+P'@pT> BzХ0cҀy1P#/9j,-c8 S8khe$=;9I7Un;u?](0:.p.QO eC\WBHlX0O eҊ+^P80z|<6$K78Q<>~R}YC()do!_O^~/xIlZeVUAbyUHf &x5JbIIOKe~M#1zczEA% Sσ}sE\wg(}C1Մ L>x\=>(}T1d&*X7Eyoab$Bӡx;OJO[<9O;+~#1%|ĝZbX*1=0z_}+#.J F`9a1#җf_Lw#/C+a?z샥uT.!D@*̻ctjBEDd<J黏kO1UA^֚?~!e=-0W~9YMej㪾zY^ߵ'ouE~~cvtwV!cv­VAk? 5§o M4yt9nU7`vl'WgF^3`;Ɇwދt#1@g74ၫ7( 03z1.N*])8ɶQL~ӟ!sl7}\_34?㌥]ֻw_÷.ֆp9>?l6^̓շD*#ߡCG]_M㥆V|z;}X;~6{!,[n'ֵ^81-ɑS<:p/|g~_Jy^1?v}5ޘ&!®\^vYg*<Ʒ;!^#/K7jw#1(NR֢22|rs Ƨ#zUtr(?5\#.RRtu>| v5y:&.gGV]_pvpKݟ׏#)e}غ}4rڃ[x=`#hmr#/_=@࿶ċv1V~W=>kpnr5-APk&1{6x|~L?gG0}3`*DG(9;OrD(#1=7ig#.@T Ղ"a7McO5]wu/˗d2~AȭINʼsE@#Dÿ|Q}=5{><z>D6i7wJG{Do#.MS±OnaTB4K}%½.#/3$y+`H#/ 4ב F!0qB#Xκ#1`Jcɦ~J}2vuhhC"C#//f$;x}$9ڗx6$\=r2$!%f9ŢV?T=g es{L9#.&f+v~Ng+^RT2qǑ\McXrȈT(#+8w-uYMk%(EjczT_W^tiàO>͆M&ctrI˔[³٨/jIgG[t՚B[je.5ˍa~*羝OAeֺI9Dψ(B5`h4ma g8}ro#/ZXe~#1=~'2NCwCVp'B{*lN{#RY1#p]J_7GWh#/H7z#1I+P/WFw݀u)r#.nj^#/*ݹ~o&O*?hlYrReF ?w!rdV .eV[41ZL!>"h;gcDB!*Ys+0<Yq-Bf[0\#1alR3) cʈyKi;MV 'CE%ӳ)Н U~nF+F\_LqIlbn5M?}7h<ҎLVrx S)(mDT54~'m=tNh&.*3UAkSׄ^#1me0Y/#1S2b4#1ȪW*%6!$[m^՚e8xcjĚ?|y$1pg>]y v+tW.]y~'o%y|jۮݻwxzk>F?8g';䜂w`4WctH<5(oc;̲tY5Ut垸7Do;k($3h>~>J%<~S3Z-p^eoNǃu"}[[GYaG5 #<~}vNMaSwoÇ'񳓣U"qƿ?>3IdȾzBH+G#/_:D9ٙgS0ŒJ$46L&&s0 AkXF ADeCe4V/9GlaFS*R .$PK+ichbapҴ}e9MJv# e#K4T%KD޷t£=|-)0\bR[0R$&ƞ/SEÀhD0#1=!57 n[L|w),NJ6&@CA3uV#1lY#LYC_|QKl Ma҅`YSƓ!P5RaQKњ8'B I$A)d,=%ʪx׬w+X݄p6hOoPP:+]4mTRZ/z^#=a#yPhFpCrt=Y#1m""uq^꫑8ϺϛXz#EF`o#?+u#.9;H /xC!Vjneb?J)ˁҪI%U3:{|\XKRVڞ~_!!qCS/ǟ nu|QnkyI$"PG4qTUQzDddCnHЏggB. &.9&,0Ɔ*IߪEboL6UﺾYabsn|Cp=|>#16#B3 QYض#K**i[&X2tL.xpL#1 eEe Q|//_Ôhm v wD,?90EJa/AMI'g-#1^N,ܨQ Q>LCtB `Y k4!4 o#1e,}LȎɽfogE&M,{ͭa'd1:m=)M[Q[ Z#1K d&%L#.r5O,:"8q̹E\4d&39 AtQAsQXkERQZmC 2t!pH$yg~jz#.B 7aC{'ODӂ;7X"g{2p\"@qAPci\?>q5ߔ7QV[4ˡ:=#. S39aS)%IL&OfBơ?fwfrgm oɤɶd-\{/,հ/0x3M\i(wpܱL ->hpp;g%2#a颋#ݸ$qBn^ƷJE&%Br V?g9w..;Ә=G'0EB &`%[:Pbz #1@PD2j,`#0У%ck]qmzIVXw@WP13k)2p:M#1^!ʘ tOGy/ۜpsMȜ=DcB18*jSI_\dIF6I'V>Y?c#1k})xAQ5|63ίw\0*I.7:Ɉb Tۥ(88 4:뻙w<(:R(jD:}B`0&tQg}m%[ǬɖL&L5(CpI߂e*!t_#1G2x`2I'h-g*>{.ծKsO0nM;t`8R>k14e C%b'Ֆ 'e`"VCY$Ћt$ުa!;ft^'z&zLq۬6.=aȎW0흠cC תXJβ7>uy[m"n<]SBreF#1aK_<7LN$LovoiN޵`3 2b:SSWN}yX{ۍCp:tkGٻ}~ٟ7|_afjFZVۃԮeů=ű.σ-#/;ҩ/L7#.xR@H1H اOI2}ƜS)aU>!]U#1h-2 @`u]ܧ?ur]q2;apO#/,j#.dwY„2XjŦT=J ,mkbXscsf&'X,o>bAlvI$ޛnGnG&LS1ey瑲)IW ]eː%Xi-dŰ$a?fl#1yhC[e,\$afQ#.uJOH_usF}û]wKN=hYdU5ݹ  OGVd)X{o$w}ʥhB8\n}Gvo]'v]Y~Q:+ˆ?h_ epvqiyLjk=QIGdz׸=2RO]W 6QX[AgBLñ3>ۤg#/Muo܎vyE#/543R( ^ɴI됔Щ'7;.Bw1^YZԺIga@bO_/Nh Y0Rfݏ2p~ʡ9dx9!( t?DNʀO\O{αѓOzZLH,$P}eXva{o} L:- @~佇n>CGv룪M,pfeL}JfBu-3J2b5lKuB=> j"н+o"#/PX}i0lc Ռ"bq*D'quGDz/蝌tA$nGAnuhseLuvY|zu8y֙&;#[_)@1|;S O3\rS: .>wrrWo$HxE~{Is#1NuLPuUO6EGیF0TC$#/= N~3_t;ƛL2={I@^e~^'#1ܙR.fNϮA#.Ieb[XM>H`dY`4@儫z-=$DZcgNDMɌk#H%m+~#/ZlCr#/|(x{Kv}vJŠBӔ?!'Y/wߞ^I#1Cռ2M/{;[|:0=#/V&(\S:|zT,E2]N#5zxMSC[)V*$wI= Ւ}{;vȆf`(44&1P~!0K2/{GQ󙓳q[ ķ{955v.W.0߃#kEŘMQ}Z E wcM RvIF6 d\#V#/g-Іq1$ MIc=\ּo9"wƓUnmC:7#qQ.ChvN| $jjm^ `I{7NP7sr>':$sCnul \Œ=yA:A9Ha`aӛ3;\=BMiQʓΨk&J8@ვP;J=#.#ko-bXޢ郅\LCz'hx\ǻXV⌳hl"7t)\ΗK,e`s#1UGgOx3,%TLU*Aq#ey:hʝ*Ŗ],j:/a P,̝v>G!"3+V6Mv:_}oC7^5NWM=\٬[TtչLkr{}i#0kmP':_R61M$׉#bvIUL#/U1RSd(m0݋50jC@LEM8DojZ(. 7o[s˿w*}LYc&GmiK*'d-$kuƅն5o6#/* Z$UGƁ˯ݰf{RV}ڮC1Ơdpbъ#/3lULCG6,Qd7 a[ݿkBր17.Bp-%~6*3 ChvG"#/VwBȼZ;wyPR v0niVF-#/r7(W:Wܽ=87b#5B#/^Q`1:.n6Y8bv۹#.V-pdx;t92`Ěcן[ӯڠB.m? Y)?.pEyf9/Z^Q+yͅ3eU#.Z%gXr]Md7JDp©j B(([{>_~&]UNbo31d2kexZ[#/u=!_u"H!v?#18JIQ-t)sIͧe^!]bU;321[CaMiLhQц+Ti8$VP"XBTB_,S{^:&wKRQm7@41EY=RLe߭&;c+|k\[R C݌#/1/S%#.w.%ћ.xcUa[kQ\Ha@#/u(54F#MJ95`Ӧ̝ #.)Vs2C;#/ђrT^Ġ:p]j6K$#/D/ #/rN8g0)FɊ檛pYƳ`>|jl瓹i2vFjӕ&WۍTעRq] &h^Xˢ'%$i:t׋UE(lU۟jHFccp_LN.o6'73T>4̔'jӓ*']hFEHpcBJ:IY{]bgjz]f7nF(odܣZ85hu2*;Lm}rvlsKnώ%3kVukq64 [ :P8 f7 ]}0xي!#1,9|\\Fo^#1ES`*T Fc\ju= ַoLx@g f5C3̆Z9Ӑ#׈#D!q0|T)-dch.,aeӤJ8rjJ$1LqE}ϗ1CsH:Aj-bV;S9ϳŻ{0y2Q)\D2#/ls"y ݡ٭y@p.FPǙ߶-O$#.MBc;2#1ljA6TM'H}*hBu4unF.y<;-[Ӌvp W;#7b#.$<ʧSf;&j{C~xנ\oej1BoZ򅲽ިjms H낶'?vS'HصV#1f4e,3[8ּuü͝~PQrِ"2moWbp(`܏iGEI^ܼe#.-)SG*%z#RIN5l@d?_jO!-|( g"Fozwyu?pR{x#.0 }*#.i#.w,h=1f•9JC$G/J HG'}]X]1orCuuܶ$WX-c\ܭ]Pew17.>L0śJFkoKvrYŴ70a<۰}I+P;X /}`Q1`D+$G !#.ˠGQm<\sy`FZȪ2ZBBFFT3qowlJl7҈=NT_6oڢ׸3!#. )q!>!)hCfËh޺>PFqOaod>t:)鹹 ӘF lI ҭk8-UD|1緑ߴE5"'MOR>4p&#.gd?s쩡!#/KwkSH1!kR`UoAc_ϫgU-"΁n-6 ;c;Qɲ˟uCHC0g&~y.`9qqgϵ K!G)*,cl#/1S\¢ l?ڠT: h~uPF~_XGN(M58(vN̪{A\~xOmwHPz+< eiBi2sb}o4?~%YUO~*mjKBBs脏8u$V^N$خـT%7bO4pC[grA3ֵ-?a*|I53+ݦXc+(cH%%6GdS8'>:W9T#1 :s#.-uzE]PQ;uujkVpoyGe3ˈVtS(;FـX?DeG-0/߿ߑpat=^9I /)PwN~;O H*Aa"ʜF#1G%A#.? #/#10H@&PCn lg4]]#/q` l7?ӓz'#.<'95\.33M0|36p#1scT#/W\&W߃/j!J5:=W_]]䑐w}T$c7my9Crp}=BT[Ǯ*@esʃv.Va8vj~}{[+i-8}t2s>/+t_5YÕ\b^ZUˍwW\H#/89]Ի/x4j@YX [ CxKuW$;k7g2gx vL1ܴhD4+O2_^7,b"W(ltH@lC8v:_<}#/0⺷9gGV.<탷9x>\rgkp%#9$*i"sLiu< 9&h /':!uĒY>Qs;tU̫PFN/|Ybni)-!/ExrO&ԯg0w9|et1DIO2`E(tztB;Ӥ=5|JhUؔoiW ?xvpjz#/w,O9$~Ҭ27:H4#1-)iUҪGF:5| _Z]>ydYWirQ5aZP)~ q%KԐGW_:T-r 0#NVJP0uU5j2YodU1yv1h)#/Wh8qvOf",$&~-/ybEN]Lj,Vك~^ARkms'#1-EZ a4hi6L彭):W&"Ȏz|+7ItdT,E#/m5f1CY;6N'IPHEQ"u#/<&of3Bo4@[Z;hI0Xua9{qJ>m? PW3.KVPM>>H: qrR#13MBHjUD<}Mm3#|:eXc#1dH79^J1Ԉ;׼&k\TeSRrpXcyaPOMdn̟m(|Sϡ?s#pЌxul 5h*^l]P{JJ/* MGlS/ޟ^?Z.C1i%܍f#bXJW}TF0~sKɍOБI$#!^}^=0USِ N~&'=C0^/H[`ZqC;߸6Ok<N)ox)P -,#.b֔ _/&}^g???|?#.A'4gq$U `!QKyr⸗JbSQpG*PlX|GG(uJ#. ";`r!L&,+iM%R@0)pjER0 &\Q si(ۈJ΢1F%R%ᢔtBy? !͹tB?A?O2] \J,#1q`(DGƞY7>XGf %.zd4!`i:ۥG\vQ-!(ɖw"!A sbHJQO={syΣ:-?圐l oۭt4zom53i^AAJIn,\݀ߩae7ˮO0+]; 5U5xmJI[a 5Eu?#/G/o`u%s!! CՇ%ap}уEB i,%3JkLL~|[kȲ84&xF9s\f0k%a&ùH%a"(ijI3Дlp7M{(v9Ĝz+Mq0$Μwd.'Q&"{?`&#.PX̛J7;7P}gI~ө,QS2,S Ag ;\C`l1Ē;#/(N##./8hO|_NPMk!dE*1A}?i$d >L|\ |z ;MWH^L1(ޚ8j[AIC:gQBi\#1b]"~#15=>fcͨ4>6,Cl?l7< QOg#1xT7AE(#\yGX-A :@"=ahҍavURvקR=l oߨK7"fgOsp3. m?$b${8l96G^=+tns__dc#/#/vBH \ߦ#1N/<ҏ|#/Xӹq#/o9~*?tP 4#/.t#1aG=&}]CZIqvp[$ϳ}ʸK,0'E#.Ϧ_k=fyn[-[Zږ~v_0)Ee>~s̊&@ Kip>Oz#T[ӡ:#1D8 D c(|m^R'}{*},)1i՜XU\hFHxIX3dfMBɔ#(|7^lv(]C(bT`ҮaNxɅ 1{+ {ZSJ4,Y Q5$NnDuq¸Lzl+9<}UUkMź+3׈U>q{.6a7<GY%SPhnБ4w0:|I,wFnڣÉ}Pw~#1$Xrn#//$mk> n*&L:.$1~ᯄ; w 7g#1CDb#/^z9=Ζ&fbC,YdVkzuwFha̱?6Ng']$XDO] =4̵&qP=;ipD1!ڴ ]랍gV"=:)h߈xIuOe zqIqbJ@b#1o~HMÄn x^dBaHz]aFʈ"aTMJ[,cZp& (d`Naض.C4M!MSc-6ƷmyHpwH9pyhΤ$1N#/$;%#/@8=iO>miS0.z.^a:7x&<}#.w-+;H)GJm|> PA @9X9)k.ebq.xbg[.߫gZ>~y 'H#1#.$E_=v5#.If":E:Ψ C30,]5%&FVfB }>!"¨RPgͿ T~}/bH8ku6(lܬ&hI \E#18|Ί @y#/pp%.*i7EBBJd$X tnA.utB]htA6+:*Ǡ}CHZ*AyQe5'#.z2~R(Vgw}#1]#//lUxa#.9p zz/F#vch#.<'9ŧn#.c^˰cݣ@ pdw{Plv]`=8ÝTc! sh|Wgi?Lwo'hfPD!#)(kl_Ѵ8XCYP{\LyvkjwF"#ʇ'|ɛӝfuQfQ~oN͹|F

J#.xt(| ¡>RЉ|IÜfs#/}s۞ r d֨H#.[d,Eu9ُA*(9 wujz%P.#.7-9tHk߀a#.KPJL_BW< pBe9M{URzxyۨ]~~\rJVcOVH2It!3CG|;#V_̓Y!AG4Nrpo\s;ں}2jF^fÞDs?xU*DŽTT:#1vyNh˙_ka`+W);W}CDC^-"̯`'L;{6D)bC,[1fVA5SeN/IpIB;¨6V92sdv=˸xO9?SO{H[szy92XtO?7[tNTX,f'ٗ!W\PcA#1aWJoNûγ1\=s=b=\c/N%ǤQ߮Ω `ý#.Ôɚ`+VlHШAb %.mv$Vځr-%W'l, /eAɱ@Y2-~8jU%*ě*ٔ NO~_ѷ ҙL#'NaaQrN&UuP!ɇ5:pPFOϝ9~-b1-#.ó8;V W*prXLZ4Q]PgsA ZixhGz)I&\I$]6įĢPmܵTeQ QtPݲ9ڜw:6y7~kۻc|KM)pw˙xK֡ǸOA"-?$zM#/hVTTuQ[}{䉂1>۫WlyThn-&ZJ > .ECYٓ~C_3҄L_EU3av?9>rc񸟛0xU:2uZLUg~YcӚ@8dlCFdd Kvۇ,9Bg"`ѕj_}\{|8$0>14pE @-z>ז mjB9t%ݦTl;zkW#10&ȍB̉&pOF|XTe10ִ~Һp"#Du%q2ccr\?*>2t)xt,*P\n(#/FU,~SءA#. MH%g{ğ5M\n>J@K%W:B[D|9-9څsq:w:;>Rؕ2>);@*BKST#/*[떵/+ò[BI9g8GLFݒa؋|CfۈP@TGC .\d{voLS Gl+{  RxA.&;+lSG0>>E: eq7߁H' -!λÏoE[*9%5\TI#.e]A\"C#.@0ɘ+E TL#/*#1|fMÿ0O=|髸Gb=r"FmYt#/J9A@G?@`ӤxE:_G>pZKUۖuN04"{7B& "[9(0Fک |Ô}A$T"~$V]՗$aH+Hs@zq6fc3UJPl&g|kͤH}U~+OyoTbE.Ǭ:s1He#. 弸^9ۧb\L U EvE8_=zġ{u/勊p;I;+]jcxQ>"#SHHMPcʕҷT:A` #.X~lɌK@˖3~[Dg~r^*#2./B}j`|͞#/6 Am!8AЃc$Ԧ,,`bf ]ۙD5K!)#.rN&dpJmN=#/ǰk+/`u3IO 0WQ*Ej$kzW-uT=#1/[p%w.!ţq8#?o#/#Yj~[ik+#1O!e;9;y"QԐtcϗp?(J )ꌯLO:0M#.D#/O.nXjfw'QCO4sxmځR(5e-Bpl~DNPvRR6$Y:α)QG,{; ] *u#/SR*􎧔aPs;`SN7kq6q䬤t_Q3 ="UGK?pjcIK__N&W:,MbB6q`5Odkr:ID9!Aа _%t֧X|^%a0`ndr۠玒vmvt=R0X[ndjÇi)$B82JܒݟoS20Q㺛j ƎMa3tG9S=:]w_E(R$K;yw퇱rRD?RooݟX5GY)3HmgL{tΛiwigvYml$!')\@o|jxf`%܆rnjz)pSmfb֋{.D{l2P-E\z+Ni(s#FNLڋjI 2I%լ5 lO{/.];2nUQxXD ڎ; &'M+ˮ 1PnuI#.]r1!/.K%XItӸ@#&~ڪ^#ɩOa4#.еWr4])^zɇ#^)sm}}j]7)$,.֡"H8jE]¢'v*Kpc9$:0ou;dpo+&s\CxH԰x(U"Gc)JWqhv;nWg+kIb/'5y,>^vyd^f;ß s)8vGL#1uXd6;t=ßԫ3:5 ͐PCkFNܮ)ghhe4lE%&>A_#΋^&I'o'l6u9ViN{ޥv?zH/g:wQ]ۣt$v$^k /qz*TJ_C_}"\KV#1ǬRRPQʡ^dJEO&T{ިRM*(]u:gY¨ͲGCՠk!jЌ#1. i+b@us{5>eXCne%I omBS nm"y H1D$DqUOoXtbs8)VU`G%#1T)>vR>NLaNe S;\}̈́RNX:2M$4oNf$˧##oOf{ͩ\kw5dZegHB CTȎ֍i\+4)fojp6T^ Qs>/g!7# N%?j%H! sUCB5EX>QhD(#/Ǐ;#/,5dY=ӶtYL"G\"› jO&#.7: ps#.Jj_~_ :s CO!GӡQ"#1e|ƧMuSWLXυ4!uDK겈TUC*"!#cŏ$?,O96pp,9X3̧M3~?x:O,_#...?yl ['݄^O3@`u^`R;i/*o@S6Ș֖a\0C?F,4;#/u:u5Qh.!aVGh#.ɊP:HhA<B #!&JB^%'yJvo^[ GO׻jj[p= >O|yuo7xJHeZ pXfGg#15%ӔQ09FB>Fu wkW6odĘ0EdO>)뤥ay=2\;騯#/OzQ}l156U9K+Z3_x!mD8Mk9>M+?xaݳu?-y$9?n(zciU:%22~䫐Iaua7[pO>:KQjtf qs;$xzcqnA?x\vOzj6Ssys|o?#/!(n#/ h.D) 3f*t ]N;H*i1E@!>`#1Q>,,Cv6v ѡ2 l$FbC7O9t,\0Z!gZ.T+Z%ԒJvތz$Hv.në0ιVja.8f9LC_DV`s.mS.p^Z㥶n lf幆bwy̹3WqS\y#/0hnX0gZSSgZmzrkӀ`.^+/gk,ȋeJ4Va&wgrPm݊qG="dj|/>MS%D=/ʫTсDQb0!O@@!O? CpM",7I\?Iuǯ]={]"Jw=MHX, RAI}`ɒt|ɡd/:U@\pkVH 6QF!cAFo^- 'ѨߗVF#/3AaMAh%ո/H]7잠AD`BE2*Ձ@tw$a ̊ (C9eb&"d̥H)574CGf?#1D!QC"H$T1?d1Chz-gȽAng߀ `F`G Mתn(p"8#.@=).{-?$W#.OVOѳi 0TI>K|k#~"["'OmfOC>6!ےi{"! G$v~Lv,cN#.v}tҨ>S*]l`wܟ!Òw#.GS4#W#/ZI`FeR"]^ёlϻۆj =yMT{9D5]8q`t$ d j=HyΊgXT=f#.؜￑ B!rHt!zaP1yohW;b?%? 7dVم3:~/wGn4L)OE5P-H{$|P0>>N_pY+@]8,#.|{~I1+3>#]>G_(ylnK/GBl>hh`pڤ2OVzD+sGh]+>%drs,nU}ѝ&2IR40vpF0)s u~M#/x0a*P]q!.*+Vݠ0'xS><^#.ۺrD@;zf÷Kx& )Qk޿WοaR؅xU#&~sa#.m0w%#1PtkY=7 x#1I!B͑3yFuء:0V_3܉]Vwܑb>b;L!S ( #/={!+` !#$* LBM} 'QJt>ӾN$@9 1,2- G$B=q仴9M!<;]93>|J鸴K%BxU30I\r$a.2**X0!PaU*#.Fʀ#1As=`SdE"9 ndҊ ##.`#/H"bـ*H9&!Rgvg 8AgNо+ؓ$ B;H@ 5)$S. Jyr7)(Y;p!u#Asu#/#/?w~iX$"'\-Lvܓ '#1_>q#1,hDf*5ʯJFtʄמH 0gFOwۓuBp0e)ӴPPn^6sPa IW##/6ω7M ⅐L! a`bB8i0[(kJ"z XޥD:k2z09:q./օ gfkOAfޠĿ#.J:96rW1i;{U=~}_QK@5lP2xi~(<}0~9 IQƮb `~ !'%I(:|zv)>'?ҋ=!iUJBN;Ŀ:7ҡL7̣Y%Y$s38ykRrs@pu)Cω$m{wK&MpHQp cdAiKT!3HDzy(s*Kfע<C"4q?z:HB:$}Tp,4p\a\2rH H6vGCX&4,|N P1u}s@`*A:}5`H&9s=' T?#/N󁯍NI%@#1slª|=#.=~,Ea9!}(,#/Q0lo8UPC#/?$jm#1#1õG{+tUʥeQz[^ԐBU XXJBdEVg5狆:i扗2Z8gחpzU*I:PWdUԱ=h7ӸG,^-a@p#.Ġϸr_w'^kIz{"t'(I6ʣyG½ /<9C{8,%˝ Pr€=n8X7nӅ$ubyEM@vkabBA@F"$D`p6ڹ]Bz!йX;8 UUDE7;z7/VI0k)e"?<>xk#/vZO#.WΟ !Lnp#12P>JO%qV$BCޡ'C-@>d'pBB}H"{N}Z25bc,h/`teEMkí4 4Bௐ;Χnxg٭|Һ8U&%U0@GefhyZp#1$sÃ:>д] xFW>1@!d ifYA\Ce$Nd/HїJVd#{OfUd.ZAN"~,B}:pÁ%`[6Zf*k߾μ}rwPg^F߳w}>z|+ -`#/z#.jda3_>H?=)?\EF>k۽W1D:@?`uOz|) ]BaHZeN`PP̳"KD`b 25An0Obe<{Bz#/ #.B~{'TϺaCiiR4#/'Op$[߃@ m@4T%aP4k$ ^UqƎԸt$Hp{lwvnoVT *RrSLUMW.o*M{.1![o0+P@2*-1(:yI$@aybyUJ}JHx8+%_>Б:Y5@2Ƕb^L0$`v` A-ĬCG굉qury'򥆏s@J>@o8j:~sU*7hC?9 *Il#1%"B̭($ %Qd 1q$‰B炆/X\ٹwCB &R9(iU%[3EWN3n/FFS\u i"]Zf n @K̽ywٓx6cus#3 z`u-iU %u`2A#/;0k@PT?7؝_=N,7#.=sCq[W>Ъ1V( _/:2'C\B_Y,aC_!fľ7_PT)1Laug/{ 4JsA+y -a`9wr<#/N=) 4U#.T[zxU6=2Q1Ue9~Љ!:~=Yet8h-$@w_ycWDž_\4\#. D#/#1+P8z۠g@$I۾gVvW~ZS?f#1'8PJޝb$#1xWLe0]yǞn2v^GFiznWS$?QN ?hћ-TM3}MʏA#1kQ"Zw3HEmdoYl^sSoB>';a5_x)<__հĤ6ʈ㟦,MJ@]#(  TzXuܜ]')J6Q儛Gc1 S1|#.q@؉yNS]{HP(젩uzE!idA(1s ҡ>X`ctWË?.Y$]IFO{uUZ6c^vZ#1cŀ9nsa(|e~.{k-w턋oQd|0 ?;Oo$Dp}zEW66\!#.l`Dߢڴ4CI^Ck b/tUԵ#NNy!2v3_p[0Q{ydOR6'}lR²2χ 0WߞpV"tM rNJHcQ/f11g5eF" hZt5%D>c+gL;}Kqv/жL("~#1t-0ݣc2:&iH@(Cxw]K͎t#1AR9c"f/{Lس~o哻dPb$Nڧ%*Z"UB? 4<#/1r BV+l^%$l掛 e~f\@ FbP, A#Z㱄ȸ¿ۄzzТyJx`vUhcl3fXń!Pm<7^tDߒ'ӧQߗX-{MvrYe;>baupMޑf0^XD "K?u\>;|~^t^@ v:2O/Ӫ'͇@zAА#/|#/ :刽@9(`]˗xjks#/1Gή[!𽐝]FQNIqY#.p(^S*d!k#/dIpN9%m:^3;KMbPK{pCwLqm:ƟJ+t㡸3o4ESCB݅Hڠ<_QkUzqf.CY%Bp]Cwأ 'U)qd#.Ŕ| ^g?}'G6G'/JZ#/#ww>A,CG#.s@uۡ#16gfyh?0Q{{#/ƅ#")ܱf[lsrUSGa>NZ06`Y財z/F=ӧ"d;#.ɹ6\5Ҩys;#.;xx[vPqj!(*YuQaRV^~ɪd_;?6a+2r/'^N<@X%"^XguJ}+o_B#/#.A] a v#.;'Ӹ0{ _7_ygӊ%a&A䤐Qҙv#.5}Uz|!,ɗд0^cqs"@?7fޘ.y݉Ci>f2$bd{8:(w.UTZ\;ʔkgKytm:O9tw7-\!bG+o(g5aEz.jF! 뫟S 5`.#q/!˿A ESi&w\8D98„n-'^!(I6#1!DŪf~p8ރvu7)  2ގnM|XD==2w.nmiwP?L퍟xRSD k6!! u81"(2Ϭ#/Ʋ rjB!̴3ȝR)vW#.Q) >#.@'PM؟Ln#.|~^xUT:MSe}w3swA~0XǸXCt+*&`%n G@PI#0Lq [ocψH.#/D-{᭚PGOCE-5: 9K5>]o+#ŵQC2?t!δi)~xH;u[֧SԻJQl͓(ε~muuxwhk C"`no.JMRd)}PHB?U 1DN#N PPE,w vSD_ǼbجR|u)igH#/ĐJߍIG=}ojR#/@J<^)`9OےZ$5|TNla..W~r݄"#.#16=H! Du>|fJ#1 `:lTL#.;TΐFQ B@|wJΗaKl1syəX;vݯR(]ɬLpj͢.n| (q:HI'}^#1*["ȄH/j$=xK}!&Tr#.@#/k/MwZc'xJQJe]Qcet#.>=gQ}#.w% T61=S<{'Q9,^Q K\vdT-=p9"+3 C#1[ܩSXR`l +bf#/;!x3. e`4R*$yM{}m)p(x-Vr{}aUUՆV/"`|[:ܐh~@#1n;3n,&R'z`Y"J!;4aCG iVH#1,;p7QQs Q<"\h%TRutajn's#1(% Ыzi3<^#@@Ԁ@35d#.b@L%{NQUac>(}O#1Xg22ofm=!77Q߷#/;z#1$YLJD9iJ0QŵmmE`٫I*1VZkV&i&6de@@`Dȉ>ع%(o1ڇ\AQ  #.X#.d=Hݹ/F=Zql/3L#/N#18ِ)m&=`U*c#.*Ոʟpߧ~?j*،>2tp:FBIR9ad} LJ}w#1t5P]u?|Q.6eu‚U* w/xp9ʑTZR1` sD) ōr*͹nS6[3 [X*3n|p@ֻ BH8*`#.f׍ohX/Cf馎r42@1 uxuf) #.$#/L#Ho{k[ar&rNM(Z(i?$4a/:dK$Su7.&3Hv:'CIR+60#.;=2wGîFcѣCB5S{}#/[A%Op#1̣Ćo^!,#/#1QC)0zmy'A9=aa@i 3I̜R&w| #W Ē5{߈p;kۓcsCٸ}Aq:RV|ew[ap4#. n3u~CUp2tX5P}Zx9#1]:z#.b8Ⱥh^!SQĮ%gh;뾈R#1G55L)©T'ϊYqvȔ' X+g~.P]z "  "xw#.oA=#.4Xm! u#1A#/QX?Z;M]ݮ#/bM}!A5D2M"@O}N=Of^0a28@T}Y=pA:Wv NAIp9vGc=`#.ڲ b\1s/Mu˫_Mjׄ4&އCP8a YIF /V9_t*Xo*0=#1̏C2YqPNc¸34ۂ-_S&aM #1E#1T___r>9,BȻ1Wzd􇬷.(伇An۷ux[`t]xϜ0b7~*rD}G#k#f}~3^8k137cv=CUY>;%=}OP x6KraM߀Ze:ã]XCt@8#/`Nx̑7ǤחY/Q$rGnFI@5~t9ӪTSssY1֗u>2_$C- DH۬Ȯ'EwY;xa22]q! nbI&ݾ2e#/qi 磨81V&K/,ŽSî&|8s <5fM씷 ?#Î9_$VB `\n#1o^*yeg~<)քvAiDE(#-eXPȗ),(K[&rSs5syZ*ըZ"X|#/ҡIpx8^u6-s#ZXYB ~8yZb$v!~/=w\#/P!&PYi CCd#/#\ ؊0mSy:s _كIݿ\cI8|<y#1nq>?Y3<%z]PuFȌ|SYX'!Xa|}kjmhCJ2LrdQ&O[z$V|jͼk#1z.i#/uGXr*@%R˾džCD,(bp#1<9l f-8IO~iHdXi*Bh#. 6 }V.Pa,#1{pz1Ӱ ؁Q~m` 22D'9+ύ䆠Lt_Rb'%9Biqj>#.+" @8#.;`MKq0#*H)GCA)Y r!%i)C6o#1C4.0A'ДNa(R4e6V\DB17%ڐ'ηrgNX#1!SBzw򊥞ެj6;6A#1J$wf#1^}>(W&OS#1!5E | V 5gû8?2KYjޥm#C&$| Hlm0KwO.|0lѾ@sْ+v0JḲ}9Ú\} EBQ? F jЁZ\A-rd7'l-U#.}e2 ʦEF"(V#1h+A{<_+yTZp r,PڔV[ LZC9L$'#1p(d*M6OD#.nsrsT#.#xt;Umxl"z˟R?)#.f2䴪3M`ēs3Zipn!Iz*M{UB#/Wcg-Ƹ̜:;N27(}|#/gGtAPaL{`@P9}g9J0#1H>j?316?A*{Agc'baVm#1<FъKa׃0JfYG b}S5)Q#'7}72}!JxfbWw]iyoRlZ Q@VDd7sώِBr{5!: a1 ׭՚%C0]8{}\/7b'LGVC=yrXuuSOÙbˌ,4inH \yۤsk3:lDW#1+dr8}*I" #1]H#1#/ݚGD^dPEډX $Kx)aP53>9lȾ6andIMLd|3ʤ-t x$H`Ma64-B'CRx!"14 r,@EQI@x\#1i8#1ĉKT<蓡,+aHjn>ßH|t#1jdi/PJH2f5lVu&t:47p J(ͨ#/8#L ,59Nf .{Qӣ*%(%mK$OTöJ!jk=z#1ƥ#-J)! Ί5PR9fS|#.]1\Ͷ6&2g7ñHuqNde jO^*%5N+ I:.iլ 5;nlGAPSB,,ڳQl~غ/'Qm/N>OvީajZ4m6"Cgjɂ,B۸&:% #/s0۔iAbN.Y9cu#ᬭ/4t(dQE(2LN@2,ɱWSspR i4(b&UUJl1#.-V#gM:glSe&#.Jzf #15pL#/*2HXBi/D#.G#/667%@,>ŹaTH6c7weǶ#1lvfگDzE|M<GHBdC'`VL%m*f6?P@QS ~|BR~%A`mFQ[cmBTE #.ȠQ#/ n#2'a J)1Twq5[tdze;AYѷ'~fI$JdL2JRiMQi2I1Q4C%4DŠб~ܡZLY%[,̘d$QE JQ4~"6ȡ0 FҢ Fh2VR()Jh4ͤcI)H̐7) #1w*Etłdej%1]Ms]ƻ%bf[L%mH LZb@TF7Rt&$F}TzkpW9Q0YegVx ߀ ]d3HZtƟ੢$\MBu'$UawۆR#1EP֚S%:af{_bF4ւlM%Y@"ûLwϠj.bˆ 6o͝S4TnqB՝=l2Һ࿾qqŞ +߬H `Mar>#/^f=;s8E6ZCi7ogJ͓MPle{P[!C/v"}ܘ+ R$( 0cζN#FuUmGXR0e>>TGH nk{nR` )`g'g3ۙtFؘ!OVye’l)xc}S}RnPӊ#1rqr4L99Đml~ƍWѭ` L}$u|f2b%:p;bO{V66VW/bT~xIJq8\3tFބh1]d!%`/2[bzK;3 ,.׉kC#/( >Յ@?Y#/{z3JHCIX !@8"n-rNi#1߹ړrcz?'^wݧ?vu8?@T6J'g-Mx&`_N˾,n) !0Scwj:SL%|4.;#.](NF8Ԙ3'2:aى#1;+pwaMSkcE#/nT_7ڃF_px6SY UwkBBGoa\LgƳmFngg8*m4L F#|4;jOӝap6ɪ!iLvJ09^ipCJip҂_Sg{ʤPџkhc%n8(9<ƵOK9{O1HuDԔ,y95[\o\D" w)2**^ C mJu6aoOe׷1;{"c\$-z"Vn/F+\W8qrlby}n3|!?tXuДKOqlzb3i/U&iq]SX=ozc0^|z%i>͙Yy,Y>_#/8)kDI̚!PS>d4]a#1<"![#/kQK'9{h'lg2`bS4(ZGfԹRVn"߅[)!L#/MdgYJw{1wn>ܹ7OMnyfLkK0g22&MY&٩'&^ nQ q;fQthrS^nLٸ8e0vhH] A i%?h xL;!SM&ݕND !.PN8szHfnPiI#146IӶ)*$Yhvrח+Om@ݨ8Cf.bJ2E 6zrgrg ԤCC7,)dF;q8B+Y|c82B P}ю*/HŽ<"rJus÷2Jd#1peOVRQ9B&Pe# oӳSqN?F,.dPb&fe Jgsr"5˚τZyrinGz4ESmp95o719/7.њZ/ý汽(tQnq3PR<#1MCWAK3gc+DΓ3c#/9 Eqhw>fcv%-w޸rGRm{i8-M9scs4˺Wk.5GyÛKLҚDZg-_<7XM2NvJXI;yb4ڌ"\UȦ!ٞ-8bީox~\kFS 8q@@;wU@^M;Zx$RLͣC^m&KhTHM<.#$)q#kS'8:%نgY^$=oX*I37`+#.6 d#.!$Ԡ28;dŒo6vP(C07U5;[d*34- !NO#1@(Jt& =Η#/җpqBrdvlCD7LvNaWv́o:#/d9wǶ/^ދ1mE$P7ld11)wV$:@wiP0N-yv aD@'T&2VxH|蒋 ꕞ]AqjDCц0޹g!%H'_+0zzўNٌ^30:I +U~`„},PvhӦPA-,N˹,' Bvw7:#. &E &PΔ8{F%7#/t[D< 7X睷M[rP#CslƟ:[C9'-#.F1D2437G#1r{`V#:uR_40;e\⣪7 4a4! EsJ ˘H&|g;tbxjJ2[ec#.',zٞ32X;z4+LS 8IDO>C!<#1iPT-R#.D2`hLl=Ϩfucqi2oXAV[PNg̹5aW?օD| Avq>[2@#16f rXr NNsbNP)z+ϙ8gNobx<N/pj,#/c1%q5 E&SfDT55[%pbCgGCäWvkP]چS#X^%șNid7Ḧ2yRRM C8,tlb:kd5K^drR9\&\t*w#1 d@P͐QT3K@iNH`#R %#1a@~H""iLmMb#.#J#/n#/>!寧mnV0-MUOYb1I#.3ɲE?zct1G()@!8QN#1Kл=&! % ~X|*0g҃o -z3}NىD({S;p;KmeqmdCIbc#扁H uAo#/V8p2LnS-X䇆EM] #.v6f~H~{&K)$CC-j(CMӹjejIQBUzzXE4;􉠙#/vHI @L qبDGRX|_`1C"4:}U#.I} #.S h1m̹麠͒MS s@pwP?>s`woo#.$1CH' ÙCo"DʎHd>eHg#/eL 6A#/pcм>8sf[|$0J)Evy &fJL$R-8z ù_I٤ޒ;zeTM%BCq~"IߎbA,04z*>HQ"!1э4Z-h1hfDQ[" "(];}MVJB#1WJظ&,c#.MF}BdF`D1M`D$JJ &N1;_cӛD`@EvLW]`ٳAxeFpQrcI#.NDskS(h8ˬ0۸063K]1Cĸphm[* -N$#/d @#.dɪ#.b3N Z2xMJX 1:F#!=*'ffLd,jw[6B`!DA4Qʇ~9O=(pЁ0L."  DKaģ(L06uۑf6"Qh zhvg%“;t#/Gn;\&찈EIstKt#7ii>؀wt $#.$1 p!lPd D.B"8U #/ĖO#.,6©(sXk̯3wI@$IkGð[:rP !&Z*hwwumKAkfWИneBפ4 ZDWͪiRq#/|xu$g!\[_$!M`vhU#1@/fa!9f$@[akӇC46ѡ2^DN]΋#.ݘtnʷ h@ :)idg)2c'r]ݜrob !shV8>U V #1 /e8[r(ʆr&;bXh(y/Qѯ\CbFBkDԾ8lӶ4K$ ԙ HkW|҂XXĵB4+݂C$>$F%laЀ$Pvp" z#.bPAg&{یs6b@(+PZ1MwG&v외qfǣ#.lBV=5|rǃ\#I#.0xaO`c<#.,6(|'%"Z[mKRYZ[oXjmEVPA$HHx%;UQWs\5QKZ)}3\cW:+bvVWѰZI\'N+jeʵS0*T)ּ^u- &JuBMd|ԼO(yfjZ] }NR:MS5ilCjJ !C$"1a7ʥdU #. Zb#18$i~~G"R®09(0&`hC2H"Ȯ#/n`k C†9'jߦϝVR%(Ȥ[hj#]2܉jҖ\pswJ$`RJIFb3#/ă`ȁ"X%lWfhT߿{E"G&T ejEVTRXdS5ԵҬ[K3eϛ_/QALTI!#.V$H@Cqwe6n( R wA@Bq97#.bQ( ,@Dxq|l={FCXt@LC?3Om_[z~>_WFsFf=5@ٳUE4:rYUi d: Tҧ>w:-ҥU(Z` ,#.l@"傸9R7]Iafs^6DtvO,#1d|3}1l^΄9BRީ"@ќ5m|4#2;h/c\˳NY:C/rnOJm,@,\esdfTt`#v!%G{ (](&Lggbm[ ZT"R@{?5h#'iT3FOQoI$4vKQZC3Tȼ]J%̓s[\(KDi,Vk\m6RMljDSLYL1K6>ڳLfҘmkT٢zqEdVw]6/fn*b(2#1emjRړk54cR֫|D߻d"Tmm$٪ۗZZ4I)i*jMIڛX-6o:%*&n%x.W%166ݒ+4)#]< :>'nKsIdܨ 2&ͫͲW$$P]Enl,',8xC|.Od[*im3x=N i4YIŸ*(#$#1QubBJSW6{y,)UV\YU+5ڛYJ)TX-@#.$Alٍ-31-PZRZ֛eLɭJ[0"JZͱ6Qi$(m5&hi1Ab0ЭScdĖRmcmJU6T2U%ƲZdmJ!BPI&Ra)iRʳlF$mE,Lړ$5K5&)&EX0!#.*i,!&͊ B*Z̪zV+m-ڋV5jjmZ:>s?o_.e-,TBVǪs#/n<{/z'^y DҾ"l""Y#TS#1%{XwTHCߗEk+}#/H_s 0w.)-vf͵1hd (8ʡ-jمgl}_V8N̢}9N`0|^imF" wSL >b|#/9C?PsD>՝rHJ#1_ՌHQb?G OtQ"fjpzyu\A/]KҨma, ȡċ)s2Rj?MoLJBL8Đ$$-bOs:N[<,#fpHΘCiT{b {oú˂Ҷ!׌Ni[zYSٙI#b"@X!sa\JH,T!˸#.#.[QmJ!$̤y YeSRFD$Н.u=Pyh]3ݭ޲ʀתvۇD.E`E feBgM"-:T8`X8qT(fwͥitufwO?M#/oOb Xwx=wrn;kqӳ:|oF"~]c< Glo& ᰢ #1n܈sl#/dAeLWsc:fYQX&&`Æɒ0›BĨU@8cMɉ`6Y RL;63̥JF 1;ED3%#/P6 V `XHf5B"ȱ b}R)5Sްwlo#.mtt7j3w3-Nfd8C؋D|VZØ\Fa{BI=cBs2uOVNt668I#.5 F3O<Sy1­J""#.#=Cܭٮr$= Yt 4ѻg|,BcF|aSAVD$KU#/-y97$-dK&(AM!IƤhzIYS*#qkd=֯aD / (c0*scHd!3HFWӯz5ǒ@˕1 oo~-~NIz8~#.q - @pX˅|yaN KV#.W0:}6AehfC7daS:+l]mfAgƍ|.om,6"v,rtv?8 I&Lw>5*z e%ےg>6zx5qXt@EBC|)e%vXy7!@PA #1vc׃GYD55{""ǚpt$ EDmy.6^~υ/C_>u6X^*9(v&#F4ÐlQ)89rXv0: M .u#5ʨ"a͇$@J؜^t.rII'e$.Ȩu4TQ̩tb>Qb)`Uwl𢘓s.HȒŤ@:k5ӒP`1Α@^\I; Me"),hU7@|8aR)V,fR T] bmcZřim,mc_ϓz;@A#.yxTkD-`K2¬{gy TbCVT\.64mIr4#/l26ZVEC49-)C#/ЄwT6TUFb?#1*'op_p_;MH:7]4`:GUBBM5siQ*JZ#.MWv20t#14ɜX`|+FerQ킠@4FwSo2ud "ȸ$"Xq.!}Z#1y;V~#.EXX  /9h#1Q7[~ryNB6I! B!"+p=j Ϳ=)g0_{mDquFe/ 7e7#/#1,<4`?j =%4 BirI Ϙx(xځE @# v‚x}/"o@D (H IU)PU#1"*"%DJHCTy#e1")]Vu}m0 7w?`ߓPN\pBe6Xk]nI!>)#/kZ Ox=IG02}yW#.vI@6F |s1 5ɊCTWrSuNᔻI!b#a-Ca5<IijAZg@;uq"|}H:i3Ώz2<BH"vlj+ߴ'L%/-gIC#Ӧ ZhE9LkJC*` 8>Y#/Vk1#1?PX( ?Ӆڰ*$QCtg=&:ܾIU\o.e$;@&IR큉cdIb2 Q,G#1(ɗpq#1"n%Z^]EFT,%a+ xa|R ,XLO ݡl`&8bۆ56KW{@ĩS1^h:JD3łVb ^].ܫPT5bo9n|釧)ΨTXzݝۚ\ՍwA(T<31z^%X+,Y3|:O1i;Mfad.#z{Ӊ@غd/'P:mP(4gRbØ|t%`%Ý1Zom6}0ƇK|$ԙoO_G{?~?ݲgLjւ)R0e#)6HXVX(x]߿m̜xpS\A>!'D-aD| -ek{~iljTZ4*$xd,V(U]KcV(nnY^鴩ڠP EB@G-9Xh:pH@\v>Y5]nT *JR-ki$6%c% wC)8n0u5{xz 3lOmUTBDI?W@JdFRJ2e@H+#1CG"(K6CmSXBh#1CX/Ӛ(![W׍^n(MjR@2wd@8#.`Yd;#/x9A#1sac| 5 BD> rÐpd f =`zljWca#.>Y7(CxΓ]kqI<$1Dxwh_1L(^4*EDt4sr9#2 jis ;GĆ?3M#/a]#/pf#.9Q`}׶'G#X4 T6習/#.<j_cR@#/)iJj^cMVi]E`Lц!!;F@yuz35^0b|iԙ#1ݨC Gph;$IFZ 5&h1JA&2jS2kbY*-Y-6QM*DTmŰ$`>_0/׆vh-#.=1(smں6-u*n*0mMzm_>H$ބN;zwd P#/tiNE͌Ru/ִ  ]d$CdkRaiP@i7O8?ǡ\O{(s2)>Bv)ܟ HDR#.;2=I#/`}S2#1"n#.)P*!0E@a"*|ź"ųy/MP#.< X 7#/)M[_0 ۡN `Ӽ9O05`laBD+qSq#1FzO  <Q˫>gE#PA@7deUhƛUL2h(!ALW0ya~)fdD) a YL$N\)HĆӹz-τBRWXV>`sPv#/ϸb[qN,IX0#/2i":?q !ƻgNTT*)#./AKi"M r_/Rv5 bʔLB$Y?3 nʱ(f7R$d@F,"l]0O\~wѥ#1Ll7 yܙ#1a!D1}(,T; C#-bEOtÙJ)}4H4! TV9nI`#1m9&:!̃%vza)Ʋ^|D@6H™kCF 5\ ZAL6u#D4?JP= l)Ttij 2R:tj0d YF4f#.}8bTHgP.t*gh_ #.49vnF aĥ1c"4F#.JQ,(`FC0!b@il-@x'5{xP#.A :y:a[E#/!jLs_PS@; SJƅM5-* C8og0?W}Ms;2S#1Kq♑(tRBag밳੐Y3sTwF9g#.$PSĚRpb!aك5M;EY/gb^L% 7'Y^˗-1#1 'Kid[aߞ:hsqٺI;cmX"2#/,0`,ݘfxvg󭳦&hL PB-0&#/i9@8! Q9-3ĒuA'o~Hټ₆P[l`p$"%(0K dŀ'$MEH;LgAߝ2#1y#.#1:>Aײ ?vf^P ⛎'nCo#>##.@BP7(GhBudDX;D @o܆zbLu&E}Cui-xaLOAXZ$dE3(f@ lɭMnkN,؅@XR?O8P^VilDMa~ГU.B)Xn*#1r#/_aç:yq8ͼ7NfY$[dL  hI4-"b&@oxc9o "DjP&$L'< Rfn}N$s-~8;緑-Wa]m #.c2AN֓vCTxiJq]Bz gืT"ŭaaJ"8m2!H.1aB9eF#.4 dDOprElDiH9f{\M38N##a0ULwZ-9h?g'ىȞ2SIRD26^0*$99g#/,0tԜׅ08*2fTvȏ*{Ea"gg WFޤ:Do3P~f%%#/B3~R$F\0V! [ RVRRMUԛ$l  #1LJ[&'YbĦjٵV俑HrQ\Xvz<7۬| =h8 4pǡk׺W 3`:{?y-$&%!8ڍиjdc}\QJaEpdkmg ״_*)Zr5ͤ5bz!|.(Mvua\5ci`Q@Tg{I?OZdTӜ)S6]JGoŨvA%Ӷ,_9:mrAC n 2U"jgh"T ZJ'!,ě s2`ؼlDdܹ}!p<ˡ1JL#13LҐIƳ pa4~EDXeWwL#1w~G&M$i Xm nwhu,o (z卡6%rA';ƍwy8j"`Ar7AvDQ "uT7"x7F#/5΋L#1BUiuWpsr?Vҹ|>?)@:""W/T>v9D^uj実u6V*ֱZQ4[ RkFXƵ5Zjׁ**"%Dѣ q G\+DF\XcbX#1 Cw7<Έq X|):f(Y)5#Lm5u='?] ",rQ SumܘR0ə)SWPF $p((%H"#1ňNB~iߥL :gT9#1;w|{bȏ#ͦ#/8zSwNIsG_a'dr$EitVvv,4 &O'f<>bdwq6&Vl.w2_?|W3~Utۗ kǙ坵LJn]ÊfK.\n;p<-J~#13 $9nffuo:)0mwhA,F0,C J%k}ay{g$+[ S=& auPhTkI}_@Ɓ0N1<$gg^s..ᚄ,W@z2c݅#1GG-A !zS9|CZGn6.i*#/a50ȯz' !"l&WƿFmmхhL 'sjt $ݓ vW jZ Lhȳ  Ud5رk^u˦֧nRj)eHTW8ۻƫ*A(A"* !&!#.1J!::ϵ0GaZ[nSgA&"(Q1E#1Bi@'$0#. #1B䅑!a8#/Q[tm+{5T-Hu*LfiE5Ɗ̴4cLd(fLJEi3c[f;UѾuzި{0o!$# Mi+QXJmy]׏"ÝޡX`60DȿD ȏji-w7Ͻu 4[cMonYWPZD[NκvZۘImr泫ݝZՕfQlbRi(- #/SKt#iQI'P^6#1 Dfš;EHCb2-XCYx]P/J:t7CE >?,5>cu=Q$!f2|L#1XM Eܮ!"?N\Z"c j-D^.#/$bۛz|1Z\OzywBMzxLlSD:1Y:V9U;"Ǒ$]с4)%.a>$vlڔ<4;LByA&'#.j|iիɔdA jN#1('%mFV/+#1+3Dz A9@3~y7n~v@dƭmjE3!DF9Ɩ9$j`2>?@`+K|ڶ̓mm]_'J;˼UDF$vI2OA'9fG1ER0Tfc9 ą̦NiL( [)[i-7PpND-6at p~BQ RKD#/ ^O?eHQ 'h"kV˵c )H^{!~'õX+4{!LvX#l9=xIpc/Kׯ/=#1@wkUmV_MP[" 9W~1/~ g02}y[yZ*S>8?JxXӐc"=Ǟ֮Yvoܦlwv9KHHݽu_#.w47nsCM) i#.za_#1fMm0|cW@!P.#ll4+D5,h#/rYm2˂YA"^aPYEѰа_"]J/o;0}TF0ޭT0RӚSO>o0͑:ںQlWv4C(`V"<(BK{l;E#/%%4P%B0L2y;OEED77-#1X U`ؔ/5f"i$l&ʖ۴%(l` %͹\ۛ^M]v9йVDP1#.Z"L)rk2ƇnU-宛VMRnMdxwF]ٝ5g8rshH%)#DE'#.)`z'c< m>;Dp')q#/hWXvB`j"4'- #1;tQMߚnTp>% #."X+!_t'5|plOPNIPA'r57*RDc$$ 1!qQ"TdQ*L$XC}"_d][hR[M^_#.K>2c`#/Z[APpHb#7 B O); `O,XQ,A$^_7kV:ЄТ^K,(#HśtJP @I׷hsL c^f0 Q!#.Q aD06s#/I]( ,Tf4J)eu1 "9*E&j[aV 16GY݂0B F"?ԈHI"B |73ƞԻ=F`(S!{5$@̣l84כ^_=@ T_eZHɗ#/vX%,[#C)va(r#1&"(!A`* C DCBO γx?#.=Y?O7^i?gI'ڐ!HX*f?jԶ*>}qߌAL7qÜxYBdG yk:.&t&WtџBSy?]^12%AżPC.:4s9i-F&x̬Z5kG.EQjnfT:]/+ԑ4&DG5{l Iΐj#.u Lѡ,jO1@sGQ>[7#.H`2JI Pr٨R'f*js#189i{Ӽc0U>&aݑʪ٘DWa޿}ce!qq#/1.L$ `#5H`jZZ-㨈`#/8Yl5d<-aOS(r]~#1U[XZG̱#$^Yl Rz4δKiٰ /v+=8G>`c?6Njx>) iqWbE*&/;y&!Y9=SCI.f}6Hy]hB }"M!֡?yo/ K"6 #1– !L-e j(n!Ȃ,DIkn s#/VPUy5N*'\yC"v,@?!s]HXV,[`[xiz扊@;E*|XVbeقNCG} P؁Q$D1M%ON&ɑsH CGҩY&>ĝ콌ι~ga*Oq WmM"LI#1&0+~20-VvLgM5EL/UO.EI@2(g[xxD/;yC%pEӲhtNJl:Gy!U-O5j0q+c+3y! #/HM% :;}ٝa,UKJ\ Xy)4϶Ojp@3o8wd+SyFބ"A2z$RI3zwnڋ$D5~*#1)783 O{aϯ >i@Chn~ f H|#.Z b=z=ol>#1V#/#1"H) HHPZ##]^'`3es#.nbG{GB7ǭ:xfnv@\Iπvj d_`ool@ȍffN'ٓ, &BDjZx+ʨ2DH9[fV|#/LyTC˅#.Gɟ tuN' +Y}SPa#.O)""SPu\ݮ Û2>&gnL"$Z#/ۮ9{ce0muJ7%ɀ(yjX ]")s{1hS6VmamtqU9|QI麟p#/C mccBԞڥЙPpT8#.c#.:PRE#1DLo<;8g0 y0ߥw?^m}oOv́ewaBᘙL 1XƤp"#1)v@)T(HTA#1K DHQ˂$4xSLT2+#/JrCR7Bq#/I 6QBC#.amT3B#1Ze^6U˥͝ȭ&7J:%d9(a/Z`E#1Yxet5RմW7S4ixfYc#/1_Cv3búhyjd(ǂd=*MN#/Q@Xs'#1tS,7펆K֥"TR6] 8+eTQD}K @.#1 I)J#1y0#1S`DD(Ҵs3#^ӒfKlA2&AHW2Q,q1\ULqt :BlRJUB`cLLn"#V#1 S `fKtɔd#/ێ痬5]lQwT]Z\CJHǭn]pljD3Ct#1i ꢶA@ذRflb]䱮n+$_@A^.2LjpLbx}Fno}9PJ>?TI#.<_%'QQXH 0%cAڹ5y!g`a4Y~_ѫ{bY]~~cݶ}.#/P@S#1hp,~=%6ؾդDP&*BZX$>CA;#1p>m'y)Wa}E`*#1L( RJkbѪڮݷ,h~C徏 QqI-H#1e?zy/Pח b+t}BO.H9g{OgiR]CBL^%w]f߻:.835Ť;m/@G{~x[3B<`CS l| qU9iNO)tݸ7tB#.LLhxCץ+69EEG=^)u^yM(׏3a)Aڬfus=e8.I;ꎻ]mJJ6b?n(V1$'Slv\p#:UO9Wo))׸+žgZEZ@Sd"gઠNė>ʅO=ՙt8^/[]#/twG{BN"5U\S|< 7;tcw3QPI#/Y|U=:Eǿ<v%g7RK6TʝĨ-c߼玃Pڌ/yys dIoqa7M<mTJ[_8LWR\ 2Nqz1oW=8[PƹYҏh,RsVXtŎ 47;! nG~l'ǟ1R%t'\=#/F)ʫTP(Ҽ_gvu'^-SXv5u_ ~9D&r +Lp][k9M6j7.y;?NU-)>.~;GJETQSSNG5R t,=代{Zt4o. 0"0 S}i/#1!r۷氁ha"K#/[y uRhsEUWh-#X˻yyt)i;hF}%K^k}8XxX m-*j把"S0!'oeE}EE;rѝ#1ǎݹ5_\@#/]8="C;w#/a/ eȻķOc ;,McuGkL9ߛs^Rqymy7(4m yPqq y&G9xۥ#1{rEʉ%;L J.na/vJTCKy,ؽQtGN5\${qzNp|P$D*X+pYQnz2y7OfBs8oXvEQݏ≠j#1x:8!רXۡ5muyBN_vhnJ;-s #.ǂXYl#\H}aj6兆(s|9~a+A Sh2{#1ФT/yLbBIgTùv4<dp N4|f3׺cUc3DJV'-Jx$ޏ#9ht'i6aD;`''ys8yasJs1:Jtpm,/7`"(@`ea׊Zn;*cv$7l1zh#a! <CIlcp_/@E4$0h*$b5Bh!,Hić >j#1UQ'UFwaCq;\u3XrI'#1S}ʢACʂ2H7)Ѭ* h]wϦ0sw@y9;bƪ0usQ0h0„>#.>ެ g@; ׶#/ݕdH A0HEBL@(T*#1{:?e#/`<w}>OzhMP6Œm&1MD(ZUi[%IXXQE6Tmm#1i4%)I *#CmQ)ђSHSFal%(I1VAVr-1X =QhÍu̅ν62״&I C'E;z~]gL`[\v́m#.U~o2#1 _ &%H)#/_G݃ii۝Y @$bEo2)a!-8I q5]w}]qj5 qO6~9ͰS5e`D6LF6ZcR&F-b3ݫVZ[ TX#.?2' 22Hd?pʂn; rNSӫ8^(ŸgYO9 %ėvPѶ3+ xH!`;Rd`ϵ&1hΓ e920X2pCF#10BT73GMBgP a(P *)QyG4=OE{0\reHJGoZm|n“1T#JjglA#/Z85O aB30PY@MjΘe'@P: Ȅ)0~II$E;>Rd62)J髄KՄx7)فB@7}#/c?*'P;CDd1IhXǍ]ށ@s >0gt@jxyǦ#H*ԭS5x8@N\t+#B+7dz?&+>̐QϯxvpS{ +BLNu#.ِʵѻ4}_x3?ϚpعڢWqpyAiCNX/#1nEz sz覕'uF~HQ HH`E9j.5aq;.&G&#/2~//ڒI!#1Hk<ͺ!`ؘ*3q14B A=*ug>eӜCPE_;FGvCs@d 1UM:紧Л37y(ytv0]z~ɣڔXY 9)tqJWQnӝ=ۖ#1}O. #p'i"6"RD=VhyWu3vTmS'#.BE$z7hIH#.#1(_tc`df2ռלwIr$ {'0 0:Ƀ)D|JShxat0;A9ñ&bv!I ᷽1r9RE WMEJJ<4љrY-'FBb:280̦i44H vk){7tR@S9mq%ɝ]=˭!osPauug_;{eǪԸۼHX9sM1T?DC$mF+xwh={oxhi&i+^*fbCˡK N'0N'ݩ:+¬n움!.IBn&#/@L~~9gW%O+v&w{휣Yb(pHL:53ӑZ`iR:ۃ`}רneÍ8%r?TAx5k$#/bpR#ƚP^ѐ4] 0RQ 2vβ Cƚ!trW)8ZMcX[p:-n3 F8#S#/|^ vE0P#/@L9bbmh -koLW0Km;QtS&,zx)fGNNZT 3ɟ 1KjYq#/olitN[/ܦf`&5iS"Ķzѝ)/m1H/D|&$ QC [Pn3 "o[Zq-)d>7mhBԂ+vÞx(1Y`ǎv B%P b7yKdQN;C!@2w!:nR+ؖ3M2ÇK%y*POB-rx #/1 BRwLXˆ0X48Xn x&@ݐ9!S_iRҳ)Y©E<#.0L[BqlXTPG$B2,C"C#/l%g~k<3;#E^ZM7'o|a_@Pf( &E vCsG#.OavzVoEu/mfͬ\v$VϸI rZBH-/Ķ<9xIh%TgSwHCSa9f iUbH0WƗ5*V%U>WQl2B#.&!UzZJ#1)7D8-Eѱm8NɨZhgh,QU0A.xLwwdy~rq,ft?_0倶_ (NB";4&f#"wI>%ߴ?Yф&nڼ@_@=+4} TQO)mZ(#.6 2壧aa'YѶ`Q4K#.yb{;؝d29#/dbԴ&AXDLYm DiD+T.pQ˦AbAcV-ZVQff@}X$Ie뭹oTɛvFCSTw#.Po$4m_vg`#.TN[B>D+73S28VP0p7Nn7 1$΋bA.ccdS9na@x N.]; q7tQ:eٸ[rC?M,ho/ Coqg z1]!maS*E-T13S}pNWnL]17pY#.9@4@z!_Mw˭$ DʺP#/d-oɒH1s 2.Gf~NRFE B#Ui[zP˜@ZEV۵Y) :6KQrEEpY !0#.#1BdpK^P'F*BɁ#.#N+_YtN7W_3hii6*-F!sݾ׼9>JfŶ@J  " ~oݺfQ5M(yrhf@]19iRh2勸(sD 2h[@a(b\7&0H`2ĪčIZوYM)m&*7]-j@V@f9s fTu_ȂRա4-mw>  I}\z:f!b_xVf F^C17G_phcuQV$#.rߦu rŦQgLB,=T%1: 8` @#.-ڠifH:2Ly,`,ةY'h(PMYFIT9ߺ'4]L=S=R{?a(W㼽T=^e#.J, !,uGD1 Cb#chE,lׯ5|QmFM3m~ S` &0XCIHca>#b`ȢZn"j6dnebQlbz[i5SjDQ:)@#/4k)&o_YϨvMZpQ֞ߥKdX|nk}z dRvϷ)9ƼՈ.eN\Uo?0z#1]mucSP#/?3Tz0ks!Ӟ= !MDևW@W>QU#L H,&ȨڤaKFm#/^%IHEAyq;یe$#1B=lV> q*FF& (QM#Me6@xcujsTFիʱmF5(Ub-cmQZߞh5[n/8RIao>@@ف5c3AQLW FSP9PT'Ҹv&킒$).D`߻(zK @1mi54U*[l+B0!@djDn3vfUuQW;PIl6.imwuwui6T2%65R& 2iiC #1+*LfJ+חJilbD#/B&Pae[)k[ݺ,(ˆ¤, =螑#.E* oցt;b1wP{SU䑎@ W“}06u8Ӕ?'C&y}DM#C<^X]?[氡`%CӚ|Wh.Q@?~'xz;u6y\[^:t>%̅NM#/2vt}#n"F) >6| ? Q3WO:nd~؀̄HRЎdoٙՑwX)%Yo5֯풲Ԟ>[$ .$D* cjX~ƭª6xdqJGo}T}F@? 7ve&v$y$OH%kWzFW%#1DGjQ8yM1GA0:L.v Ɯ#/N5Nl":vje-kf|WpIWP!"B-JcE Ez*'#/D > Z:!B\_= 6Îv%=D/)dRS(v >Ak^x0;٘0Ӟvg=$(/&BG̶}\#/B)g籡ZֵW(:޾]ۢ}pOb?M~` BiH?xC}=C <'Gү<ïQf*eUnO!II#/T"e bo1 j PؼDSAD\7L/bV p8j! 5zw?$$ϟ[-U!>zіXS2%Bt޺zubʲC~9L-e-=>p{b 3%w*F IWqQB=T%+WD@ mOKs#1^-#1r=pL?lV #/Ӊb0#.*{P69p/k幹մLjJlU65hDŅDMe(PƄN11~#. "KZ.>)#/`5(+#.;? DN۵uD٩5uZMA{=^n'ʇe3B"蝝ģɝ'Ypi1#.q=Q> A@@u@og{wO~uF}Ut_R?x$#.zHbg ~>i6f+ j?g_yuhMܵduuǕ+)9>>2Ro7#-*Àu~#/xI_#/~|h {E#1! W2nQ d.P6geT_!/a:F#1Y]N*pMj\|!ttYX]2bKrk'/@/ BA6֬y,<0$E,=z6GnB[껁qWXj+`Syvzh03OyI#Ianl#/~3xrE8Pl +#<== From 35d1085e5de94abbbb405e48964845c6353cb23f Mon Sep 17 00:00:00 2001 From: Alejandro Ramallo Date: Fri, 3 Nov 2023 16:51:39 -0400 Subject: [PATCH 2/2] removed test artifacts --- tests/waf/test_flatbuffers/car1.bin | Bin 76 -> 0 bytes tests/waf/test_flatbuffers/car2.bin | Bin 80 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/waf/test_flatbuffers/car1.bin delete mode 100644 tests/waf/test_flatbuffers/car2.bin diff --git a/tests/waf/test_flatbuffers/car1.bin b/tests/waf/test_flatbuffers/car1.bin deleted file mode 100644 index 0b3fd9549b87bc60b5f7b701f2697daedac62a56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 zcmWe&00Axr0R|2R9tJib3j{v0%K%9hAZ7z%ztZ&d)Di};3JwN#APoj$AbD0G_Dyz9 HECSL1yYU6* diff --git a/tests/waf/test_flatbuffers/car2.bin b/tests/waf/test_flatbuffers/car2.bin deleted file mode 100644 index ebb195d125c79a151aafe4130041491761a5add3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80 zcmWe&00Axr0R|2R9tJib3j{u}%K%9hAZ7<*x19Wfg480g3J#zM2T%+Mv_bM9wsT^U Lf_G+KI!F)z>f#4x