From 470a71540b9071a1e103b8d59f23d215bd568f4b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 May 2019 19:19:39 +0300 Subject: [PATCH] waf: initial deploy --- .gitignore | 3 + cl_dll/wscript | 69 +++ dlls/wscript | 75 ++++ scripts/waflib/deps.py | 65 +++ scripts/waflib/force_32bit.py | 56 +++ scripts/waflib/fwgslib.py | 30 ++ scripts/waflib/msdev.py | 774 ++++++++++++++++++++++++++++++++++ scripts/waflib/reconfigure.py | 47 +++ scripts/waflib/xcompile.py | 208 +++++++++ waf | 170 ++++++++ wscript | 149 +++++++ 11 files changed, 1646 insertions(+) create mode 100644 cl_dll/wscript create mode 100644 dlls/wscript create mode 100644 scripts/waflib/deps.py create mode 100644 scripts/waflib/force_32bit.py create mode 100644 scripts/waflib/fwgslib.py create mode 100644 scripts/waflib/msdev.py create mode 100644 scripts/waflib/reconfigure.py create mode 100644 scripts/waflib/xcompile.py create mode 100755 waf create mode 100644 wscript diff --git a/.gitignore b/.gitignore index a0bc5604..adcc4f7b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ cmake_install.cmake *.vsxproj *.vsproj *.sln +.waf-* +.lock* +*.pyc diff --git a/cl_dll/wscript b/cl_dll/wscript new file mode 100644 index 00000000..85de7e55 --- /dev/null +++ b/cl_dll/wscript @@ -0,0 +1,69 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from waflib import Utils +import os + +def options(opt): + # stub + return + +def configure(conf): + if conf.env.GOLDSRC: + if conf.env.DEST_OS != 'win32': + conf.check_cc(lib='dl') + +def build(bld): + source = bld.path.parent.ant_glob([ + 'pm_shared/*.c', + 'dlls/crossbow.cpp', 'dlls/crowbar.cpp', 'dlls/egon.cpp', 'dlls/gauss.cpp', 'dlls/handgrenade.cpp', + 'dlls/hornetgun.cpp', 'dlls/mp5.cpp', 'dlls/python.cpp', 'dlls/rpg.cpp', 'dlls/satchel.cpp', + 'dlls/shotgun.cpp', 'dlls/squeakgrenade.cpp', 'dlls/tripmine.cpp', 'dlls/glock.cpp' + ]) + + source += bld.path.ant_glob(['hl/*.cpp']) + source += [ + 'ev_hldm.cpp', 'ammo.cpp', 'ammo_secondary.cpp', 'ammohistory.cpp', + 'battery.cpp', 'cdll_int.cpp', 'com_weapons.cpp', 'death.cpp', + 'demo.cpp', 'entity.cpp', 'ev_common.cpp', 'events.cpp', + 'flashlight.cpp', 'GameStudioModelRenderer.cpp', 'geiger.cpp', + 'health.cpp', 'hud.cpp', 'hud_msg.cpp', 'hud_redraw.cpp', + 'hud_spectator.cpp', 'hud_update.cpp', 'in_camera.cpp', + 'input.cpp', 'input_goldsource.cpp', 'input_mouse.cpp', + 'input_xash3d.cpp', 'menu.cpp', 'message.cpp', + 'overview.cpp', 'parsemsg.cpp', 'saytext.cpp', + 'status_icons.cpp', 'statusbar.cpp', 'studio_util.cpp', + 'StudioModelRenderer.cpp', 'text_message.cpp', 'train.cpp', + 'tri.cpp', 'util.cpp', 'view.cpp', 'scoreboard.cpp', 'MOTD.cpp' + ] + + includes = Utils.to_list('. hl/ ../dlls ../dlls/wpn_shared ../common ../engine ../pm_shared ../game_shared ../public ../utils/false_vgui/include') + + defines = ['CLIENT_DLL'] + if bld.env.GOLDSRC: + defines += ['GOLDSOURCE_SUPPORT'] + + libs = [] + if bld.env.GOLDSRC: + libs += ['DL'] + + if bld.env.DEST_OS == 'win32': + libname = 'client.dll' + elif bld.env.DEST_OS == 'linux': + libname = 'client.so' + elif bld.env.DEST_OS == 'darwin': + libname = 'client.dylib' + else: libname = '' + + bld.shlib( + source = source, + target = 'client', + features = 'c cxx', + includes = includes, + defines = defines, + use = libs, + install_path = os.path.join(bld.env.GAMEDIR, bld.env.CLIENT_DIR, libname), + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = 1 + ) diff --git a/dlls/wscript b/dlls/wscript new file mode 100644 index 00000000..95581ff7 --- /dev/null +++ b/dlls/wscript @@ -0,0 +1,75 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from waflib import Utils +import os + +def options(opt): + # stub + return + +def configure(conf): + # stub + return + +def build(bld): + defines = [] + source = bld.path.parent.ant_glob([ + 'pm_shared/*.c', + ]) + + source += [ + 'agrunt.cpp', 'airtank.cpp', 'aflock.cpp', 'animating.cpp', 'animation.cpp', 'apache.cpp', + 'barnacle.cpp', 'barney.cpp', 'bigmomma.cpp', 'bloater.cpp', 'bmodels.cpp', 'bullsquid.cpp', 'buttons.cpp', + 'cbase.cpp', 'client.cpp', 'combat.cpp', 'controller.cpp', 'crossbow.cpp', 'crowbar.cpp', + 'defaultai.cpp', 'doors.cpp', + 'effects.cpp', 'egon.cpp', 'explode.cpp', + 'flyingmonster.cpp', 'func_break.cpp', 'func_tank.cpp', + 'game.cpp', 'gamerules.cpp', 'gargantua.cpp', 'gauss.cpp', 'genericmonster.cpp', 'ggrenade.cpp', 'globals.cpp', 'glock.cpp', 'gman.cpp', + 'h_ai.cpp', 'h_battery.cpp', 'h_cine.cpp', 'h_cycler.cpp', 'h_export.cpp', 'handgrenade.cpp', 'hassassin.cpp', 'headcrab.cpp', + 'healthkit.cpp', 'hgrunt.cpp', 'hornet.cpp', 'hornetgun.cpp', 'houndeye.cpp', + 'ichthyosaur.cpp', 'islave.cpp', 'items.cpp', + 'leech.cpp', 'lights.cpp', + 'maprules.cpp', 'monstermaker.cpp', 'monsters.cpp', 'monsterstate.cpp', 'mortar.cpp', 'mp5.cpp', 'multiplay_gamerules.cpp', + 'nihilanth.cpp', 'nodes.cpp', + 'observer.cpp', 'osprey.cpp', + 'pathcorner.cpp', 'plane.cpp', 'plats.cpp', 'player.cpp', 'playermonster.cpp', 'python.cpp', + 'rat.cpp', 'roach.cpp', 'rpg.cpp', + 'satchel.cpp', 'schedule.cpp', 'scientist.cpp', 'scripted.cpp', 'shotgun.cpp', 'singleplay_gamerules.cpp', 'skill.cpp', + 'sound.cpp', 'soundent.cpp', 'spectator.cpp', 'squadmonster.cpp', 'squeakgrenade.cpp', 'subs.cpp', + 'talkmonster.cpp', 'teamplay_gamerules.cpp', 'tempmonster.cpp', 'tentacle.cpp', + 'triggers.cpp', 'tripmine.cpp', 'turret.cpp', + 'util.cpp', + 'weapons.cpp', 'world.cpp', 'xen.cpp', 'zombie.cpp'] + + if bld.env.VOICEMGR: + source += bld.path.parent.ant_glob([ + 'game_shared/voice_gamemgr.cpp', + ]) + else: + defines += ['NO_VOICEGAMEMGR'] + + includes = Utils.to_list('. wpn_shared ../common ../engine ../pm_shared ../game_shared ../public') + + libs = [] + + if bld.env.DEST_OS == 'win32': + libname = bld.env.SERVER_NAME + '.dll' + elif bld.env.DEST_OS == 'linux': + libname = bld.env.SERVER_NAME + '.so' + elif bld.env.DEST_OS == 'darwin': + libname = bld.env.SERVER_NAME + '.dylib' + else: libname = '' + + bld.shlib( + source = source, + target = 'server', + features = 'c cxx', + includes = includes, + defines = defines, + use = libs, + install_path = os.path.join(bld.env.GAMEDIR, bld.env.SERVER_DIR, libname), + subsystem = bld.env.MSVC_SUBSYSTEM, + idx = 2 + ) diff --git a/scripts/waflib/deps.py b/scripts/waflib/deps.py new file mode 100644 index 00000000..f216515b --- /dev/null +++ b/scripts/waflib/deps.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# Michel Mooij, michel.mooij7@gmail.com + +from waflib import Utils +from waflib import Errors + + +def get_deps(bld, target): + '''Returns a list of (nested) targets on which this target depends. + + :param bld: a *waf* build instance from the top level *wscript* + :type bld: waflib.Build.BuildContext + :param target: task name for which the dependencies should be returned + :type target: str + :returns: a list of task names on which the given target depends + ''' + try: + tgen = bld.get_tgen_by_name(target) + except Errors.WafError: + return [] + else: + uses = Utils.to_list(getattr(tgen, 'use', [])) + deps = uses[:] + for use in uses: + deps += get_deps(bld, use) + return list(set(deps)) + + +def get_tgens(bld, names): + '''Returns a list of task generators based on the given list of task + generator names. + + :param bld: a *waf* build instance from the top level *wscript* + :type bld: waflib.Build.BuildContext + :param names: list of task generator names + :type names: list of str + :returns: list of task generators + ''' + tgens=[] + for name in names: + try: + tgen = bld.get_tgen_by_name(name) + except Errors.WafError: + pass + else: + tgens.append(tgen) + return list(set(tgens)) + + +def get_targets(bld): + '''Returns a list of user specified build targets or None if no specific + build targets has been selected using the *--targets=* command line option. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + :returns: a list of user specified target names (using --targets=x,y,z) or None + ''' + if bld.targets == '': + return None + targets = bld.targets.split(',') + for target in targets: + targets += get_deps(bld, target) + return targets + diff --git a/scripts/waflib/force_32bit.py b/scripts/waflib/force_32bit.py new file mode 100644 index 00000000..efec6b11 --- /dev/null +++ b/scripts/waflib/force_32bit.py @@ -0,0 +1,56 @@ +# encoding: utf-8 +# force_32bit.py -- force compiler to create 32-bit code +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from fwgslib import get_flags_by_compiler + +# Input: +# BIT32_MANDATORY(optional) -- fail if 32bit mode not available +# BIT32_ALLOW64(optional) -- ignore all checks, just set DEST_SIZEOF_VOID_P to 8 +# Output: +# DEST_SIZEOF_VOID_P -- an integer, equals sizeof(void*) on target + +def check_32bit(ctx, msg): + try: + ctx.check_cc( + fragment='''int main( void ) + { + int check[sizeof(void*) == 4 ? 1: -1]; + return 0; + }''', + msg = msg) + except ctx.errors.ConfigurationError: + return False + return True + +def configure(conf): + if getattr(conf.env, 'BIT32_ALLOW64'): + conf.env.DEST_SIZEOF_VOID_P = 8 + else: + if check_32bit(conf, 'Checking if \'{0}\' can target 32-bit'.format(conf.env.COMPILER_CC)): + conf.env.DEST_SIZEOF_VOID_P = 4 + else: + flags = ['-m32'] + # Think different. + if(conf.env.DEST_OS == 'darwin'): + flags = ['-arch', 'i386'] + env_stash = conf.env + conf.env.append_value('LINKFLAGS', flags) + conf.env.append_value('CFLAGS', flags) + conf.env.append_value('CXXFLAGS', flags) + if check_32bit(conf, '...trying with additional flags'): + conf.env.DEST_SIZEOF_VOID_P = 4 + else: + conf.env.DEST_SIZEOF_VOID_P = 8 + conf.env = env_stash + if getattr(conf.env, 'BIT32_MANDATORY') and conf.env.DEST_SIZEOF_VOID_P == 8: + conf.fatal('Compiler can\'t create 32-bit code!') diff --git a/scripts/waflib/fwgslib.py b/scripts/waflib/fwgslib.py new file mode 100644 index 00000000..e7cdee0d --- /dev/null +++ b/scripts/waflib/fwgslib.py @@ -0,0 +1,30 @@ +# encoding: utf-8 +# fwgslib.py -- utils for Waf build system by FWGS +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import os + +def get_flags_by_compiler(flags, compiler): + out = [] + if compiler in flags: + out += flags[compiler] + elif 'default' in flags: + out += flags['default'] + return out + +def get_flags_by_type(flags, type, compiler): + out = [] + if 'common' in flags: + out += get_flags_by_compiler(flags['common'], compiler) + if type in flags: + out += get_flags_by_compiler(flags[type], compiler) + return out diff --git a/scripts/waflib/msdev.py b/scripts/waflib/msdev.py new file mode 100644 index 00000000..df6b2ca9 --- /dev/null +++ b/scripts/waflib/msdev.py @@ -0,0 +1,774 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# Michel Mooij, michel.mooij7@gmail.com +# modified: Alibek Omarov, a1ba.omarov@gmail.com + +''' +Summary +------- +Exports and converts *waf* project data, for C/C++ programs, static- and shared +libraries, into **Microsoft Visual Studio**, also known as **msdev**, +project files (.vcproj) and solution (.sln) files. + +**Microsoft Visual Studio** is a mature and stable integrated development +environment for, amongst others, the C and C++ programming language. A free +version of this IDE, known as the *express* version can be obtained from Microsoft +at http://wwww.visualstudio.com. + +Description +----------- +When exporting *waf* project data, a single **Visual Studio** solution will be +exported in the top level directory of your *WAF* build environment. This +solution file will contain references to all exported **Visual Studio** +projects and will include dependencies between those projects and will have the +same name as APPNAME variable from the top level *wscript* file. + +For each single task generator (*waflib.TaskGenerator*), for instance a +*bld.program(...)* which has been defined within a *wscript* file somewhere in +the build environment, a single **Visual Studio** project file will be generated +in the same directory as where the task generator has been defined. +The name of this task generator will be used as name for the exported **Visual +Studio** project file. If for instance the name of the task generator is +*hello*, then a **Visual Studio** project file named *hello.vcproj* will be +exported. + +Example below presents an overview of an environment in which **Visual Studio** +files already have been exported:: + + . + ├── components + │ └── clib + │ ├── program + │ │ ├── cprogram.vcproj + │ │ └── wscript + │ ├── shared + │ │ ├── cshlib.vcproj + │ │ └── wscript + │ └── static + │ ├── cstlib.vcproj + │ └── wscript + │ + ├── waf.vcproj + ├── appname.sln + └── wscript + + +Projects will be exported such that they will use the same settings and +structure as has been defined for that build task within the *waf* build +environment as much as possible. Note that since cross compilation is not +really supported in this IDE, only the first environment encountered that +is targeted for **MS Windows** will be exported; i.e. an environment in +which:: + + bld.env.DEST_OS == 'win32' + +is true. + + +Please note that in contrast to a *normal* IDE setup the exported projects +will contain either a *debug* **or** a *release* build target but not both at +the same time. By doing so exported projects will always use the same settings +(e.g. compiler options, installation paths) as when building the same task in +the *waf* build environment from command line. + + +Usage +----- +**Visual Studio** project and workspace files can be exported using the *msdev* +command, as shown in the example below:: + + $ waf msdev + +When needed, exported **Visual Studio** project- and solution files can be +removed using the *clean* command, as shown in the example below:: + + $ waf msdev --clean + +Once exported simply open the *appname.sln* using **Visual Studio** +this will automatically open all exported projects as well. + +Tasks generators to be excluded can be marked with the *skipme* option +as shown below:: + + def build(bld): + bld.program(name='foo', src='foobar.c', msdev_skip=True) + +''' + +import os +import sys +import copy +import uuid +import shutil +import xml.etree.ElementTree as ElementTree +from xml.dom import minidom +from waflib import Utils, Logs, Errors, Context +from waflib.Build import BuildContext +# import waftools +# from waftools import deps +from deps import get_targets +from subproject import get_subproject_env + + +def options(opt): + '''Adds command line options to the *waf* build environment + + :param opt: Options context from the *waf* build environment. + :type opt: waflib.Options.OptionsContext + ''' + opt.add_option('--msdev', dest='msdev', default=False, action='store_true', help='select msdev for export/import actions') + opt.add_option('--clean', dest='clean', default=False, action='store_true', help='delete exported files') + + +def configure(conf): + '''Method that will be invoked by *waf* when configuring the build + environment. + + :param conf: Configuration context from the *waf* build environment. + :type conf: waflib.Configure.ConfigurationContext + ''' + pass + + +class MsDevContext(BuildContext): + '''export C/C++ tasks to MS Visual Studio projects and solutions.''' + cmd = 'msdev' + + def execute(self): + '''Will be invoked when issuing the *msdev* command.''' + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + self.pre_build() + + for group in self.groups: + for tgen in group: + try: + f = tgen.post + except AttributeError: + pass + else: + f() + try: + self.get_tgen_by_name('') + except Exception: + pass + + self.msdev = True + if self.options.clean: + cleanup(self) + else: + export(self) + self.timer = Utils.Timer() + +def export(bld): + '''Exports all C and C++ task generators as **Visual Studio** projects + and creates a **Visual Studio** solution containing references to + those project. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + ''' + if not bld.options.msdev and not hasattr(bld, 'msdev'): + return + + Logs.pprint('RED', '''This tool is intended only to ease development for Windows-fags. +Don't use it for release builds, as it doesn't enables WinXP compatibility for now!''') + + solution = MsDevSolution(bld) + targets = get_targets(bld) + + saveenv = bld.env # root env + for tgen in bld.task_gen_cache_names.values(): + if targets and tgen.get_name() not in targets: + continue + if getattr(tgen, 'msdev_skipme', False): + continue + try: + bld.env = get_subproject_env(bld, tgen.path, True) + except IndexError: + bld.env = saveenv + if set(('c', 'cxx')) & set(getattr(tgen, 'features', [])): + project = MsDevProject(bld, tgen) + project.export() + + (name, fname, deps, pid) = project.get_metadata() + solution.add_project(name, fname, deps, pid) + + solution.export() + + +def cleanup(bld): + '''Removes all **Visual Studio** projects and workspaces from the + *waf* build environment. + + :param bld: a *waf* build instance from the top level *wscript*. + :type bld: waflib.Build.BuildContext + ''' + if not bld.options.msdev and not hasattr(bld, 'msdev'): + return + + targets = get_targets(bld) + saveenv = bld.env + + for tgen in bld.task_gen_cache_names.values(): + if targets and tgen.get_name() not in targets: + continue + if getattr(tgen, 'msdev_skipme', False): + continue + try: + bld.env = get_subproject_env(bld, tgen.path) + except IndexError: + bld.env = saveenv + if set(('c', 'cxx')) & set(getattr(tgen, 'features', [])): + project = MsDevProject(bld, tgen) + project.cleanup() + + solution = MsDevSolution(bld) + solution.cleanup() + + +class MsDev(object): + '''Abstract base class used for exporting *waf* project data to + **Visual Studio** projects and solutions. + + REMARK: + bld.objects() taks generators are treated as static libraries. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + ''' + + PROGRAM = '1' + '''Identifier for projects containing an executable''' + + SHLIB = '2' + '''Identifier for projects containing a shared library''' + + STLIB = '4' + '''Identifier for projects containing a static library''' + + C = 'c' + '''Identifier for projects using C language''' + + CXX = 'cxx' + '''Identifier for projects using C++ language''' + + def __init__(self, bld): + self.bld = bld + + def export(self): + '''Exports a **Visual Studio** solution or project.''' + content = self.get_content() + if not content: + return + if self.xml_clean: + content = self.xml_clean(content) + + node = self.make_node() + if not node: + return + node.write(content) + Logs.pprint('YELLOW', 'exported: %s' % node.abspath()) + + def cleanup(self): + '''Deletes a **Visual Studio** solution or project file including + associated files (e.g. *.ncb*). + ''' + cwd = self.get_cwd() + for node in cwd.ant_glob('*.user'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.ncb'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.suo'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + for node in cwd.ant_glob('*.sln'): + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + node = self.find_node() + if node: + node.delete() + Logs.pprint('YELLOW', 'removed: %s' % node.abspath()) + + def get_cwd(self): + cwd = os.path.dirname(self.get_fname()) + if cwd == "": + cwd = "." + return self.bld.srcnode.find_node(cwd) + + def find_node(self): + name = self.get_fname() + if not name: + return None + return self.bld.srcnode.find_node(name) + + def make_node(self): + name = self.get_fname() + if not name: + return None + return self.bld.srcnode.make_node(name.lower()) + + def get_fname(self): + ''' Returns file name.''' + return None + + def get_content(self): + ''' Returns file content.''' + return None + + def xml_clean(self, content): + s = minidom.parseString(content).toprettyxml(indent="\t") + lines = [l for l in s.splitlines() if not l.isspace() and len(l)] + lines[0] = '' + return '\n'.join(lines) + + +class MsDevSolution(MsDev): + '''Class used for exporting *waf* project data to a **Visual Studio** + solution located in the lop level directory of the *waf* build + environment. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + ''' + + def __init__(self, bld): + super(MsDevSolution, self).__init__(bld) + self.projects = {} + self.xml_clean = None + + def get_fname(self): + '''Returns the workspace's file name.''' + return '%s.sln' % getattr(Context.g_module, Context.APPNAME) + + def export(self): + '''Exports a **Visual Studio** solution.''' + dst = self.get_fname() + + s = MSDEV_SOLUTION + + with open(dst, 'w') as f: + for line in s[0:3]: + f.write(line) + for name, (fname, deps, pid) in self.projects.items(): + sid = str(uuid.uuid4()).upper() + f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' % (sid, name, fname, pid)) + if len(deps): + f.write('\tProjectSection(ProjectDependencies) = postProject\n') + for d in deps: + try: + (_, _, pid) = self.projects[d] + except KeyError: + pass + else: + f.write('\t\t{%s} = {%s}\n' % (pid, pid)) + f.write('\tEndProjectSection\n') + f.write('EndProject\n') + for line in s[3:8]: + f.write(line) + for _, (_, _, pid) in self.projects.items(): + f.write('\t\t{%s}.Debug|Win32.ActiveCfg = Debug|Win32\n' % (pid)) + f.write('\t\t{%s}.Debug|Win32.Build.0 = Debug|Win32\n' % (pid)) + for line in s[8:]: + f.write(line) + Logs.pprint('YELLOW', 'exported: %s' % os.path.abspath(dst)) + + def add_project(self, name, fname, deps, pid): + '''Adds a project to the workspace. + + :param name: Name of the project. + :type name: str + :param fname: Complete path to the project file + :type fname: str + :param deps: List of names on which this project depends + :type deps: list of str + ''' + self.projects[name] = (fname, deps, pid) + + +class MsDevProject(MsDev): + '''Class used for exporting *waf* project data to **Visual Studio** + projects. + + :param bld: Build context as used in *wscript* files of your *waf* build + environment. + :type bld: waflib.Build.BuildContext + + :param gen: Task generator that contains all information of the task to be + converted and exported to the **Visual Studio** project. + :type gen: waflib.Task.TaskGen + ''' + + def __init__(self, bld, gen): + super(MsDevProject, self).__init__(bld) + self.gen = gen + self.id = str(uuid.uuid4()).upper() + self.type = self.get_type(gen) + self.language = self.get_language(gen) + self.buildpath = self.get_buildpath(bld, gen) + + def get_type(self, gen): + if set(('cprogram', 'cxxprogram')) & set(gen.features): + return MsDev.PROGRAM + elif set(('cshlib', 'cxxshlib')) & set(gen.features): + return MsDev.SHLIB + else: + return MsDev.STLIB + + def get_language(self, gen): + return MsDev.CXX if 'cxx' in gen.features else MsDev.C + + def get_buildpath(self, bld, gen): + pth = '%s/%s' % (bld.path.get_bld().path_from(gen.path), gen.path.relpath()) + return pth.replace('/', '\\') + + def get_fname(self): + '''Returns the project's file name.''' + return '%s/%s.vcproj' % (self.gen.path.relpath().replace('\\', '/'), self.gen.get_name()) + + def get_root(self): + '''Returns a document root, either from an existing file, or from template.''' + fname = self.get_fname() + if os.path.exists(fname): + tree = ElementTree.parse(fname) + root = tree.getroot() + else: + root = ElementTree.fromstring(MSDEV_PROJECT) + return root + + def get_content(self): + '''Returns the content of a project file.''' + root = self.get_root() + root.set('Name', self.gen.get_name()) + root.set('ProjectGUID', '{%s}' % self.id) + configurations = root.find('Configurations') + for configuration in configurations.iter('Configuration'): + configuration.set('ConfigurationType', '%s' % self.type) + configuration.set('OutputDirectory', '%s\\msdev' % self.buildpath) + configuration.set('IntermediateDirectory', '%s\\msdev' % self.buildpath) + for tool in configuration.iter('Tool'): + name = tool.get('Name') + if name == 'VCCLCompilerTool': + tool.set('PreprocessorDefinitions', '%s' % self.get_compiler_defines(self.gen)) + includes = [] + for include in self.get_compiler_includes(self.bld, self.gen): + includes.append('%s' % include) + tool.set('AdditionalIncludeDirectories', ';'.join(includes)) + if name == 'VCLinkerTool': + if self.type == MsDev.PROGRAM: + # Force Windows Subsystem + # TODO: this isn't enables Windows XP compatibility! + tool.set('SubSystem', '2') + self.update_link_deps(tool) + self.update_link_paths(tool) + files = root.find('Files') + self.update_includes(files) + self.update_sources(files) + return ElementTree.tostring(root) + + def update_includes(self, files): + '''Add include files.''' + includes = [] + for filtr in files.iter('Filter'): + if filtr.get('Name') == 'Header Files': + for include in filtr.iter('File'): + includes.append(include.get('RelativePath')) + break + if len(includes) == 0: + filtr = ElementTree.SubElement(files, 'Filter', attrib={'Name':'Header Files', 'Filter':'h;hpp;hxx;hm;inl;inc;xsd'}) + filtr.set('UniqueIdentifier', '{%s}' % str(uuid.uuid4()).upper()) + for include in self.get_include_files(self.bld, self.gen): + if include not in includes: + ElementTree.SubElement(filtr, 'File', attrib={'RelativePath':'%s' % include}) + + def update_sources(self, files): + '''Add source files.''' + sources = [] + for filtr in files.iter('Filter'): + if filtr.get('Name') == 'Source Files': + for source in filtr.iter('File'): + sources.append(source.get('RelativePath')) + break + if len(sources) == 0: + filtr = ElementTree.SubElement(files, 'Filter', attrib={'Name':'Source Files', 'Filter':'cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx'}) + filtr.set('UniqueIdentifier', '{%s}' % str(uuid.uuid4()).upper()) + for source in self.get_genlist(self.gen, 'source'): + if source not in sources: + ElementTree.SubElement(filtr, 'File', attrib={'RelativePath':'%s' % source}) + + def update_link_deps(self, tool): + '''Add libraries on which this project depends.''' + deps = tool.get('AdditionalDependencies') + + deps = [] # clean out deps everytime + + libs = self.get_link_libs(self.bld, self.gen) + for lib in libs: + dep = '%s.lib' % lib + if dep not in deps: + deps.append(dep) + if len(deps): + add_deps = " ".join(deps) # work around when converting to vcxproj by inserting spaces + tool.set('AdditionalDependencies', add_deps) + + def update_link_paths(self, tool): + deps = tool.get('AdditionalLibraryDirectories', '') + deps = [] + dirs = self.get_link_paths(self.bld, self.gen) + for dep in dirs: + if dep not in deps: + deps.append(dep) + if len(deps): + tool.set('AdditionalLibraryDirectories', ';'.join(deps)) + + def get_metadata(self): + '''Returns a tuple containing project information (name, file name and + dependencies). + ''' + name = self.gen.get_name() + fname = self.get_fname().replace('/', '\\') + deps = Utils.to_list(getattr(self.gen, 'use', [])) + return (name, fname, deps, self.id) + + def get_genlist(self, gen, name): + lst = Utils.to_list(getattr(gen, name, [])) + lst = [str(l.path_from(gen.path)) if hasattr(l, 'path_from') else l for l in lst] + return [l.replace('/', '\\') for l in lst] + + def get_compiler_options(self, bld, gen): + if self.language == MsDev.CXX: + flags = getattr(gen, 'cxxflags', []) + bld.env.CXXFLAGS + else: + flags = getattr(gen, 'cflags', []) + bld.env.CFLAGS + if self.type == MsDev.SHLIB: + if self.language == MsDev.CXX: + flags.extend(bld.env.CXXFLAGS_cxxshlib) + else: + flags.extend(bld.env.CFLAGS_cshlib) + return list(set(flags)) + + def get_compiler_includes(self, bld, gen): + includes = self.get_genlist(gen, 'includes') + for include in bld.env['INCLUDES']: + root = bld.path.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, include]) + if pref == root: + node = bld.root.find_dir(include) + if node: + includes.append(node.path_from(gen.path).replace('/', '\\')) + + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + uselib_incs = bld.env['INCLUDES_%s' % dep] + for uselib_inc in uselib_incs: + root = bld.root.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, uselib_inc]) + if pref == root: + node = bld.root.find_dir(uselib_inc) + if node: + inc = node.path_from(gen.path).replace('/', '\\') + includes.append(inc) + Logs.pprint('YELLOW', 'Added relative include: %s' % inc) + includes.append(uselib_inc) + return includes + + def get_compiler_defines(self, gen): + defines = self.get_genlist(gen, 'defines') + gen.bld.env.DEFINES + if 'win32' in sys.platform: + defines = [d.replace('"', '\\"') for d in defines] + defines = ';'.join(defines) + return defines + + def get_link_options(self, bld, gen): + flags = getattr(gen, 'linkflags', []) + bld.env.LINKFLAGS + if self.language == MsDev.CXX: + if self.type == MsDev.SHLIB: + flags.extend(bld.env.LINKFLAGS_cxxshlib) + else: + flags.extend(bld.env.LINKFLAGS_cshlib) + return list(set(flags)) + + def get_link_libs(self, bld, gen): + libs = Utils.to_list(getattr(gen, 'lib', [])) + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + try: + tgen = bld.get_tgen_by_name(dep) + except Errors.WafError: + uselib_libs = bld.env['LIB_%s' % dep] + for uselib_lib in uselib_libs: + libs.append(uselib_lib) + pass + else: + if self.type == MsDev.STLIB: + libs.append(dep) + return libs + + def get_link_paths(self, bld, gen): + dirs = [] + deps = Utils.to_list(getattr(gen, 'use', [])) + for dep in deps: + try: + tgen = bld.get_tgen_by_name(dep) + except Errors.WafError: + uselib_paths = bld.env['LIBPATH_%s' % dep] + for uselib_path in uselib_paths: + root = bld.root.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, uselib_path]) + if pref == root: + node = bld.root.find_dir(uselib_path) + if node: + libpath = node.path_from(gen.path).replace('/', '\\') + dirs.append(libpath) + Logs.pprint('YELLOW', 'Added relative library path: %s' % libpath) + dirs.append(uselib_path) + pass + else: + if self.type in (MsDev.STLIB, MsDev.SHLIB): + directory = '%s\\msdev' % tgen.path.get_bld().path_from(gen.path) + if directory not in dirs: + dirs.append(directory.replace('/', '\\')) + elif self.type in (MsDev.PROGRAM): + for directory in tgen.lib_paths: + if directory not in dirs: + dirs.append(directory.replace('/', '\\')) + return dirs + + def get_include_files(self, bld, gen): + includes = [] + for include in self.get_genlist(gen, 'includes'): + node = gen.path.find_dir(include) + if node: + for header in node.ant_glob('*.h*'): + includes.append(header.path_from(gen.path).replace('/', '\\')) + + for include in bld.env['INCLUDES']: + root = bld.path.abspath().replace('\\', '/') + pref = os.path.commonprefix([root, include]) + if pref == root: + node = bld.root.find_dir(include) + if node: + for header in node.ant_glob('*.h*'): + includes.append(header.path_from(gen.path).replace('/', '\\')) + + return includes + + +MSDEV_PROJECT = \ +''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' + +MSDEV_SOLUTION = [ +'Microsoft Visual Studio Solution File, Format Version 8.00\n', +'# Visual Studio 2005\n', +'Global\n', + 'GlobalSection(SolutionConfigurationPlatforms) = preSolution\n', + 'Debug|Win32 = Debug|Win32\n', + 'EndGlobalSection\n', + 'GlobalSection(ProjectConfigurationPlatforms) = postSolution\n', + 'EndGlobalSection\n', + 'GlobalSection(SolutionProperties) = preSolution\n', + 'HideSolutionNode = FALSE\n', + 'EndGlobalSection\n', +'EndGlobal\n', +'\n'] diff --git a/scripts/waflib/reconfigure.py b/scripts/waflib/reconfigure.py new file mode 100644 index 00000000..333e64ac --- /dev/null +++ b/scripts/waflib/reconfigure.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2019 mittorn + +''' +Reconfigure + +Store/load configuration user input + +Usage: + def options(opt): + opt.load('reconfigure') + + def configure(conf): + conf.load('reconfigure') + + ./waf configure --reconfigure +''' + +from waflib import Configure, Logs, Options, Utils, ConfigSet +import os + +import optparse +STORE_PATH = 'build/configuration.py' + +def options(opt): + opt.add_option('--rebuild-cache', dest='rebuild_cache', default=False, action='store_true', help='load previous configuration') + opt.add_option('--reconfigure', dest='reconfigure', default=False, action='store_true', help='load and update configuration') + +def configure(conf): + store_data = ConfigSet.ConfigSet() + options = vars(conf.options) + environ = conf.environ + if conf.options.reconfigure or conf.options.rebuild_cache: + store_data.load(STORE_PATH) + if conf.options.reconfigure: + for o in options: + if options[o]: store_data['OPTIONS'][o] = options[o] + store_data['ENVIRON'].update(environ) + store_data.store(STORE_PATH) + conf.environ = store_data['ENVIRON'] + conf.options = optparse.Values(store_data['OPTIONS']) + else: + store_data['OPTIONS'] = vars(conf.options) + store_data['ENVIRON'] = conf.environ + store_data.store(STORE_PATH) + \ No newline at end of file diff --git a/scripts/waflib/xcompile.py b/scripts/waflib/xcompile.py new file mode 100644 index 00000000..71d05ca6 --- /dev/null +++ b/scripts/waflib/xcompile.py @@ -0,0 +1,208 @@ +# encoding: utf-8 +# xcompile.py -- crosscompiling utils +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from fwgslib import get_flags_by_compiler +import os +import sys + +# Output: +# CROSSCOMPILING -- set to true, if crosscompiling is enabled +# DEST_OS2 -- as some operating systems is built on top of another, it's better to not change DEST_OS, +# instead of this DEST_OS2 is defined with target value +# For example: android is built on top of linux and have many things in common, +# but it can't be considered as default GNU/Linux. +# Possible values: +# DEST_OS2 DEST_OS +# 'android' 'linux' + +class Android: + arch = None + toolchain = None + api = None + toolchain_path = None + ndk_home = None + + # TODO: New Android NDK support? + # TODO: Crystax support? + # TODO: Support for everything else than linux-x86_64? + # TODO: Determine if I actually need to implement listed above + + def is_arm(self): + ''' + Checks if selected architecture is **32-bit** ARM + ''' + return self.arch.startswith('armeabi') + + def is_x86(self): + ''' + Checks if selected architecture is **32-bit** or **64-bit** x86 + ''' + return self.arch.startswith('x86') + + def is_arm64(self): + ''' + Checks if selected architecture is AArch64 + ''' + return self.arch == 'aarch64' + + def is_clang(self): + ''' + Checks if selected toolchain is Clang (TODO) + ''' + return self.toolchain.startswith('clang') + + def is_hardfp(self): + return self.arch.endswith('-hard') + + def gen_toolchain_path(self): + path = 'toolchains' + if self.is_clang(): + raise Exception('Clang is not supported yet') + else: + if self.is_x86(): + toolchain_folder = self.arch + '-' + self.toolchain + elif self.is_arm(): + toolchain_folder = 'arm-linux-androideabi-' + self.toolchain + else: + toolchain_folder = self.arch + '-linux-android-' + self.toolchain + + if sys.platform.startswith('linux'): + toolchain_host = 'linux' + elif sys.platform.startswith('darwin'): + toolchain_host = 'darwin' + elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): + toolchain_host = 'windows' + else: raise Exception('Unsupported by NDK host platform') + + toolchain_host += '-' + + # Assuming we are building on x86 + if sys.maxsize > 2**32: + toolchain_host += 'x86_64' + else: toolchain_host += 'x86' + + if self.arch == 'x86': + triplet = 'i686-linux-android-' + elif self.is_arm(): + triplet = 'arm-linux-androideabi-' + else: + triplet = self.arch + '-linux-android-' + + return os.path.join(path, toolchain_folder, 'prebuilt', toolchain_host, 'bin', triplet) + + def cc(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + 'gcc')) + + def cxx(self): + return os.path.abspath(os.path.join(self.ndk_home, self.toolchain_path + 'g++')) + + def system_stl(self): + # TODO: proper STL support + return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) + + def sysroot(self): + arch = self.arch + if self.is_arm(): + arch = 'arm' + elif self.is_arm64(): + arch = 'arm64' + path = 'platforms/android-{0}/arch-{1}'.format(self.api, arch) + + return os.path.abspath(os.path.join(self.ndk_home, path)) + + def cflags(self): + cflags = ['--sysroot={0}'.format(self.sysroot()), '-DANDROID', '-D__ANDROID__'] + cflags += ['-I{0}'.format(self.system_stl())] + if self.is_arm(): + if self.arch.startswith('armeabi-v7a'): + # ARMv7 support + cflags += ['-mthumb', '-mfpu=neon', '-mcpu=cortex-a9', '-mvectorize-with-neon-quad', '-DHAVE_EFFICIENT_UNALIGNED_ACCESS', '-DVECTORIZE_SINCOS'] + if self.arch == 'armeabi-v7a-hard': + cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mhard-float', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK'] + else: + cflags += ['-mfloat-abi=softfp'] # Tegra 2 sucks + else: + # ARMv5 support + cflags += ['-march=armv5te', '-mtune=xscale', '-msoft-float'] + elif self.is_x86(): + cflags += ['-mtune=atom', '-march=atom', '-mssse3', '-mfpmath=sse', '-DVECTORIZE_SINCOS', '-DHAVE_EFFICIENT_UNALIGNED_ACCESS'] + return cflags + + def ldflags(self): + ldflags = ['--sysroot={0}'.format(self.sysroot())] + if self.is_arm(): + if self.arch.startswith('armeabi-v7a'): + ldflags += ['-march=armv7-a', '-Wl,--fix-cortex-a8'] + if self.arch == 'armeabi-v7a-hard': + ldflags += ['-Wl,--no-warn-mismatch', '-lm_hard'] + else: + ldflags += ['-march=armv5te'] + return ldflags + + def __init__(self, ndk_home, arch, toolchain, api): + self.ndk_home = ndk_home + self.arch = arch + self.toolchain = toolchain + self.api = api + self.toolchain_path = self.gen_toolchain_path() + +def options(opt): + android = opt.add_option_group('Android options') + android.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, + help='enable building for android, format: --android=,,, example: --android=armeabi-v7a-hard,4.9,9') + +def configure(conf): + if conf.options.ANDROID_OPTS: + for i in ['ANDROID_NDK_HOME', 'ANDROID_NDK']: + android_ndk_path = os.getenv(i) + if android_ndk_path != None: + break + + if not android_ndk_path: + conf.fatal('Set ANDROID_NDK_HOME environment variable pointing to the root of Android NDK!') + + values = conf.options.ANDROID_OPTS.split(',') + if len(values) != 3: + conf.fatal('Invalid --android paramater value!') + + valid_archs = ['x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'armeabi-v7a-hard', 'aarch64', 'mipsel', 'mips64el'] + + if values[0] not in valid_archs: + conf.fatal('Unknown arch: {0}. Supported: {1}'.format(values[0], ', '.join(valid_archs))) + + android = Android(android_ndk_path, values[0], values[1], values[2]) + conf.options.ALLOW64 = True # skip pointer length check + conf.options.NO_VGUI = True # skip vgui + conf.options.NANOGL = True + conf.options.GLWES = True + conf.options.GL = False + conf.environ['CC'] = android.cc() + conf.environ['CXX'] = android.cxx() + conf.env.CFLAGS += android.cflags() + conf.env.CXXFLAGS += android.cflags() + conf.env.LINKFLAGS += android.ldflags() + + conf.env.HAVE_M = True + if android.is_hardfp(): + conf.env.LIB_M = ['m_hard'] + else: conf.env.LIB_M = ['m'] + + conf.msg('Selected Android NDK', android_ndk_path) + # no need to print C/C++ compiler, as it would be printed by compiler_c/cxx + conf.msg('... C/C++ flags', ' '.join(android.cflags()).replace(android_ndk_path, '$NDK')) + conf.msg('... linker flags', ' '.join(android.ldflags()).replace(android_ndk_path, '$NDK')) + + # conf.env.ANDROID_OPTS = android + conf.env.DEST_OS2 = 'android' +# else: +# conf.load('compiler_c compiler_cxx') # Use host compiler :) diff --git a/waf b/waf new file mode 100755 index 00000000..7066d9f3 --- /dev/null +++ b/waf @@ -0,0 +1,170 @@ +#!/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.15" +REVISION="ff6573b86ad5ff5d449c8852ad58b8bc" +GIT="02c9f814da0d3f3cd4c1a4f9c5e0c7ed7129bb19" +INSTALL='' +C1='#/' +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 + 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&SYRTu 000(bw#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$}:S}pr7b4CvӵԺ67RD׵]dXϫ wsv䡥%ނ-b1R|VO]EYxr{ wi=Uvi@F:}:#$t'`((m(:0#/*vEJQ$#$=@*^{=op%Ɍ{n)JuJmWshw}Z=ZqRvm4}fӓ;)Ͻv}U66ARB`zr{̺7w{]\$w9ĥZwyø4#$J*OCPO%4unI>wzn@dm.u61lQx#$^P;x3w{}u_2`>hQkOxjkGD^Zn{mz;&8٧[e>Eh/kx2L…=K=o)zΰOfmc4ٺ>r5VO^rw.սޯn/`\^w]}\%Rг0d6ldٻtz.36P81+ؼkfGB۶#$ jڠ#$.󲋎{e۽Μlwa9H΂y̻e+}nTfDAuv<۾|w{{ۮ2Zܰ5լ@'=w书ݽݼw;Hm{[ŧǹN7k砅j[[(z/wexr0V6Uq:^kfﶧ>}#$##,USNӻn\T2ӫ|Ͱvf/{}ϻsnlR({yzoZ:0,}}o @hhR{3p#$UǕy$l4#/Kݸۅݜ n%Um7vwPU0ػiɪN#/>oL^;=MvNBczޞzrw,u3BM^,2ٍ.e) IlK] cH .2,M#,0 k{,?nᯛۙ4Yt\ɯ&vPR(b>[;LVG؊)',9,Ɠl hlO5Rg?rcG G H!08q%F[1< HuMWhc ,AZdcnsES+tO;cb}w_)Ѣ,}ʯط"_"!v7K=?kiĦݸn#$(5=VFm#1pJ.kؼ޽^D Y*rƢT^ǭd#/FZT-Ől|Ϻ14AC x޺/)%EQ:ԖP̠#l՟IV~˳nɱ_Wu_ZI\$ZA`նKi(bCiZ‰GE2R]U"wլUjxiKe99eeQن81> (ݢ4VԥD_څi͖䁥Ҋ©T{8:n89lX ˽2qPgzrN5C0MT,3( bfF߳}}pRi#v/P}#DžǖtÊ~#,-6L֓:^KsY.F֏=QFvGczU2>[҄p"G>}n+jqJ]\t荎Gɧ֓DezQLP53J:tFuvC#)'qE;y&?,31O}aNz<'k9H#>)faRwlN@)WtWFnMB88hשۺ1gY mt4kyk֛1d$DX|yO^T=.bC1J4/5OTG/Ho߬eyiU(/.NP:cc%xr@#,w9V;kz&׮,cȶ_/4;íjvײϳ!*n[7Љ*U!VTօJ+]zez{#,~- UKTW&|_i{RWWKb"V#AET|'ۛJ V=,5ٕ[F(/rHͭ}poU5F,UMD#,q=3yyZֳKY12HiB|p데`75>YslD%/Scmdvp3$s=d.|d[Κwt5`r`QHL ߳7:)x1H0W~#,ZTҩьtuO~y$_抠s߾4?-:GBF/_A#,f#,X)?)q6NfAu]!P 2 TSe1987ðii5,hzڤx3[=;6䗶,/Z-%x^DES#/J m쪤<|uN+E3RJ{4}'k2C<"Tm쪦Hh5Vm g|K5x2#,ҡ@cFH@R-9rlfx#/h6DaKR7gFTaes^o#,b):2ɻ#T/\5~$oElC㉾=UD"gPo2*mg}薉Cwγk@RɬE['zw\w2;UإcO8>UpCؼD#/s >~x5 #,ILjW. VYPzqcm\p"Z[TpCNJAQ8dr*?WdB;b8[v>wNۭb6ǰk`a163z.9IO;#y#B U#${~ m#,qm|3duA}D]疹ͻK(csد*ʝOys.J1jf^E"+m33zYb&"l*v@l7XhkSq]sVTB8@uޯ-6h]d?&iu|Lh|FPFGy$˞L'Aie6GTTgJɒ[(S-.6-s0.0.( HHDyHM.^|nkەeȿ#,n|6Y9t!Le!tPvlt5/V%'D["4`yl_=4suV,anʐgOBu@n(#{7)ѹL x6,680[ש"L|Cbc?- +]Zc~~Pd@(GW+*ti?6s! 4Q:y7F$ݧNcnA0 hh;;xVpX#/} oGMc*9i}%|9٥K>?Ȍ\tTE$Sd<]y|"IӬD^hn9Q{:}D?#/!WK=:o_e{&GMD3y29&UXHNGnk`㋥z$6#,w@e\C1DD?$܍bc}w33!Do%$3\"ML\"F#$p<LhW@yPi.naE4P[k kJ?)Ѻbq L#/XD*^QD3A|˝3T - Y٠1ysʰXހ\#Ku{cJ2~yAN`L| oƛ5a3a{|/X: Pհ"EW@\#^E]5Hs9Ӭ}/!`mSAQٻAã?)Cf\ݲj8|w[}A߿pޝ$5AM".#$怳sܡȏyfQk_Ux8/+"` {G_jbPLI$#,#,c;B*ogS}2ՋV 8)?޼/@*Aݩ- `c,J!z:]WG~ϟ3/3i.\6 3dr>׵M7 )#)W4:,P]ۖ93O~6Fp|83#$bT(#,KIA0&JlP^ڲLMmhc?| f]>.>ѥKfxQ&'TQAٜؔt퀱ZJYOiĞi6,##,>]>z崎P@۩9X aah%zo4ܺƪ(NKoa\'ם\"#,3V?y%HcHM?miR:7{rk#,q!d>i#/k៦vxGfiX#$s ^'ߧC6S?47L@jacllfH-LwwZx"4stRA@X-kO6֫WrP F2E *0iDklq210b#$:N rHeޡbIM) 7OFNOI:,v;iv| VvOZ#,l  \m_mnnԼMQ!.ioǞh`PPE8U&B)"d`P #/'xy#,#/S  ;,)|/)eC&u䏇Hc[}$9ڨްkKf;O#$w t>>ac2fjcVf<l斍6Œ,h!xQK4&r\ff)hnilCZ6mԱ4 cQ#,D4:h4 0-Gd@#$奉c6P@ "l^~wS/1mtr-[sF*OW-_q^{ېcL1bh &Q4d "XTMPÄVǨꥍ 'XUd""ck:{9m pjg)~lDӡG524DSu#M;2@ 6Aҷ}llAD=8+_#/vIWLDz#$9;'JI%]-~V1N#,Ͽg/!kTAEHY-;$m%۱1,oط;0!ưuyQZ(b>6(=b-z@ysI|Ѻ"RDTȍT?erJFV蜑s[0J6Ȇ(q7"#,tjbaT0Y% 2h:6R(H y*I;MH+Jv#/)$դo.؅)ڊ"0ѥbQѸ&b̴:ۃC@F5j#&Ć!L .7{?O0BH6OKD#,c!14LϷ::V1w.#/W<ٞ#ɶnqXP6k_KERiDxrӑL4Dx2Oc;Gz ;Yu!`XἻ`>xs p#f z܈ע##,]Ckޣz)ac#$TyEc Xw$R@9aC{ŧBL O2HLّvB6l%?v~+g@9)J(m#$(#/g%9RqT邑"uQh4#/spcd\PZ1/Gؼe#$c!HaC?ɕi"1E#/̈A}xUo.va!{eQ:' ֧C#/7r߀TD$/,W?øUOzx8vXCӭoef'hK:(6bZJU1&t3XAdXb%2Mh鶚bZ,B02;Kd;*t;yHM(dfmj$jÍMa룧VS#/9Poo¸{*Ķ7 s ϑsoG<00RJ5th,KnߧGGZ 6r=ڏYݾA/<9oix:c>~jյecI#,AubGnaK$3?--[+)=u3g!'.lw{6"a*l hҺ%Ts*`Af tq}_;gC0iɍGsyU)uۯ%m b?g#$C0j5&&j$#/IGmЮ9>Txa'~ay)ȎOTXT(#=Nւ)RY4I RĢN3F~vݨX5RhT ITKg_umׇgC^<"޿>)@P,'Ph1(p.elE) )%*A#,#,=$]VGq_KQ#/<_>(T(` \oXdQPկ8d]@K`ߋ|2~oSczKBm·}M&s*JOh!0V()#$7hV)9thq{;_~'^!WԾnI=hc+`?MK[;:n6Xoin η,wk|~fHݠv#,5tH^1jIU@4};.H-Q;`uzJ_ۉVB56w_@/ E12vۉw>?} E=.6q>iwׅ!U`1"to"G#Qbr#>)3X 54(4}?&R<ϟ9#,5lo߀5'k,HZS)BES9;륾'Ѐyfn:aΔ&STTze0m$(pRqSi_ǵD{*#,y! 4W,W'aOaߡDű\NԝzE74P~Oׄ4M܏ y_'yԒb~i4 CcʼBUW%3)c< R`G_C;_ 0g皙7CUR(St C 7P HQUGN|Ooӯol~34mJPq0XPB5,ވsR\!HG"y}ydZ(!z]]W}fpUAt{]2ԛ>+Ǐ~X+n1~w.Zwe/fYWf߻Xݳ-Z"Z9|{|(f~CۢiMZ:ݫnџ/e͞8eE2wgתzy-E<NOg~Dr $~^ǚezo9)BZd9ǣCue;7Ŵcs'U',mW;٫]ç$?5UzNmt_A;&x#wF.wKWZ|chZ=7mkuۂ)HkKͧ6i5#$ۤѯOx?b.T1Tx'qqm^y;tAzJW_I١PàVN=+#,ii]:$Hq%b$h_BOa[9)>Qw e^G0gN#É)gdFcoQUW~6W}:_-a%oj%}Wd0?w wiGo_q@ehƘ}rpunC彽äG*}inQJN|W[O Yv'!,K?-:8BKMݘv45I/|8~ph? ^8(t٦K[Gѯa7LJ,]3\$'8*,>UB8aS@#0<us~nݣend)uBӮT|_N^vɥog,?p%{P5G|:^R[739 i_7d?/@(#$LRuw3_٫AzO_[-}>`۱;Qp"TNZVͪA B'/Խ;#,=v8#ϯa}>uFo(pmgZDAkSfzbg>~)nin`N7Gۛ٪=pqҟ8څ|(5|z9n~}jw'#$UhY_+SHlg!PDDz&iP$7Bqa4=]]Xn.=G3|d~坴M}78YBA?#,gaǤQ=<ۥln)W/#/]a'JNdəl7}\CdB>I'y'yGL4w+7x;RI۵>;F/ulu| ˣzN7h x#$#,8Mz*I+ϐi{*?U)Ϙȏ/TA쒞Q}ced#$?4v^ U3QG,?Z̹[r9G?7F50Ҩ6ةA EJ$ݱ!n,$Ud.?ͥY #/]ܦ3"{pcv)lv5Ec#,2J`B#,YCCt!IՊ"ca=j0XIFOaFf#$fUI1KBzZ8dueD&s΂PP}?~+S<% fZ9+r c F6,# Do?~F##E4D dҁBoPc ذJLAbm6-$HPYt~} o/͍2)M vˤ{#N4 -glLj]uu]t_ٰK=*~c:9'v_۝V) lw~I\ʶUdvV{#\3 hp ܂$:ˮg?%u{wKPp#,@&ߗӫ) 8o8/ "NWs=9 - <6aSZwz5;uwi"n?O|ŲY0/-!Gv<3knBmZ$V҆AX)%<#, cT8(2[IDdE+kaRCtTQj#,@RBdQZa++)YlF#UZڌZʅ؄`$Mx3#/F5ېMȠ`B#,F&9t)lcQ#,dL00TZCӸ貖3HZ7KkA#$ek.X#,<D:7YhlbbeCN"GDtd֖#/T"Pgj*®ʌp#R1lb%W<!U$IsJCCI4&A/^a/β߼Ԋ#/#/[h S'? t"_Z¿x#/a٭)ozӣ}ڟ)30X~rtB&Ȉӣ̽sN-]~[~=.k,9}No#*@W s #/wA#$_${ۯjGCv*8W.eҠIfUUk{T~h$whM 9,Hbzxw G?W\@t/7Tnurڈ$P(?u5!AhcŘd/2p#/ #/?:9~qBߎtzxGCPx>#,<ڽWH'@pZ3=@>XDeAbXҪ4YԎqqJ1J>i`.H)FD8XoIT4IwjGzt(qPGcE{/F4RhD%j64(F6UBn3D:TґIcDCտ޻|GO|zEj ~?oEJ|A|fϺM[1@fҠϊ{XW˓i9БZa A ouQ]VAR7x26<6ӈ!#/^ BL8PVg&CŔ93ĻG#/kL3H/! #$PDJ)(s.`v[ȿc!%M\G#,P|;Y$CFP L4' dDMXsTuZWɚ2GZԌdmw:Cȷج#,B#Ȥ$cd%Rj~g~nzebBt"2 ^&AӋ$'wġ/4}Myi4e1lsn,-.$C,ru6#ԋ#,Kq띾h.܅9"$nCj qzZ$-Ld"mZGp.gh#i^x{5lfHK҃9i6!b>UA5W[m&}ƜسVbǴś/m2BaIv7:^V1ifgd"3](؝He0V $D?qCIgڈV8mbpӝ0#,)܎4om!& &HHc,|k\-wj]CKpGYfIiH\;3cw]'u om0Cn*6-d۶ҹQ[#,<#/:26#,^W\m;QuvwDyd*78(6]qq5#,k[r+V;B'F)9;6TZf0LjӦNqpȬ2օM#7';d%P#,]H'mjM00y_gcbkˠՁo:٦ K4]HzޱII*JCK$x;M9`kz:  rqB17מm[^J׸ȓȌlI'V>Y?S#/kn뚞x]x٨[&'&uuRM|an!ԥAlK;Ɩ)FP6,-e4:R(#,TVD `Mߛ?mӣJϥIhioXcOtXkw ~a7ia\}:Ki!>rlqD>g<FAM,s A5IYG8A1&rn$M]p4ίEy Ͳ1i+~K[sE>iP܈x]#/aF.Li@Ν-2%&`;]SBtb0TXuwWn!7_c|a{= θ"^yyXVM`3ʄ댘Ntgn0Q0$-FtT?צv=).֔3E3HdQ* ķgW鉆Ԟ_/&!EU_](·ZW(݋3ɰ>cٯ#$σ-x 1$I2އ&wTR.ʃ0FB뙌012ϧK2/X,d ̒8O-0NײAA``#$,;#!z[x0nƂĘRVi@9`%æH+; ×#qI@Oo9yos&Bq^L}Tus/K?L,rm8=InmOlRPlg[%^pI;?X!2f*)ٔ?#,<#,JJdr iU%6L[2;BZ#/9b]83cK>&I,\ț|jg}'H[*cwjR!S&'DG\'caOnt8,^9dVy2XnmtGIRN4ef6nŚ(>BޚNќ(rtq/v2A"H92I0.:"7}ӼN t>Hhp2vBT)@ԙɣ>:ݩn9/FޤnB|L_APSO.5)ϲlV-^:v$N;1(2#, hriA1F3[|Lc+YQ_#`#,9YPEl͍<|C;Y⺞ ʐmj致I,-E+#/SNԧ~P;]VK蝳P;!VhuZI[ogFiY#/䎋_+ktC.y3e:/Y]#,tXɈp)>^fļޅ3t}vOow=D0 ȃ՚5]彑ǎj߶)yoυ nDt2un6c+j/ M}9>y[PoQ"4#,{K7ahJܫ,h\-!4#/;/繟ͦsTfW#//9=jѴSeEB#$X9hch(GGc~N2 \lzQjJ͡ >zÛ ʶ]`y?%ad7X]~p6 Lqi1d2r&yGEVfcⒻ8V85לfQVKeq {u{ÜfՄWRn%mvW`neB{ =BKʽs.\LrN ^`h1K&:r5bhZ=#/g#/%2,iq'M~^'`⩓O8x2, AU`370`s6V粎,qtsXKAed9)ZjXlݑ([Xnܟg\6x6@N8M0ժ"I,vdcZ%U^7NL#*K%+Y9},} ax«az6D7m-TPHLUC巷+]ij=nusXAH- ߫pQnsY̱ NdGNr:N1})W38z%~rwĢ3יL?/,H]lZKMeϔlD4S\g?g˙eSֳ{R:bm!4}oV}uM{UǔjAiz*uEo#/l78N$\4_BQSbKTs5˙(H\7Q1QWާws}2os u<1\ o12qf3u-IDl7ѠrAISCh\XKSRa%.&<'k+0?OcVq1CͥtXB"ICgwjrBWzm^8D#b->ZL_=c /P(P9]ʠ2$@h9"1L)~ @+b^@^2].\}%cS\i%qw]Z]Y!#/U qCtkQ,7J 5g>6((tvB'잌Lr:oylM\M_BMʊ%,J8tNX#/Sz3fYR4V|oy ]xfM9%\U{GC-9D:"-pbC-,psx0l5fKew^bn6}3SnQm w[c<ʘt:kg9m.Ow7ڎh*fEKQ=h.w6to~14=6I8}'snn1Lg*t|cW#/f%-9+y;;􋕟4^JMgݻ\=[/ոFq5Z:ad*uۿE<36$tcQ\\?򔟾*QU#bR-IMVѶ3 N$KDB^tyLfɯʇGc8v:Y#"X(1FMS2]:r:L'L?N|[9vZ?jy20Gn.F!QQx][3ku_7pFrOaod?X:)걸R&ėwJSbiK88#$?o70-?~ve,-=$>xɁđ?WTUM ,OL#,rt,;A)[KN@ *2(Fi$ตhAlK:ts~e}( c;P9=F&^.`ꈇ2P7hC%9<4%؀j#/#/dY#,~ʜ膨SB{)LW@@U@t2j 'U(сgdS,ҰŔu$ɄA5'1(,ΔvK.c#$yjbsa?|?͝}cn~V:B{ A&~Jw=aGZ$p4#cKҚ㓚=(Wn:V,VUv#,TY@J, y)\TۿoL|,輾7,::GO[?Dp;F-P΀|6%2(/Ic잸Zwyy#o2P!D7C=в߮լ>I=eJP(ZU5/wrj)k/wBò#$`1$ s۽X1I5=iS1kRA*u&l]ӭQudv4b@8E.;brˍٝ ט:`(#/ ;{#$.u;!]tnȿV6hj"ߍ(exLLqc nDDl#/PIpwLm{~"(v3e!2@T:ik~ oL.N7%LO~/Mvf0Rdmځ|7yOG3}&t|L\՜AESPI4J^m)@3P"ӊWW@U=Eϧ4Н셥^[?^]TqcÓyGwAy*q'#/A#?&Lݒ HM#\B{X~~qj0$Ϥ<%8M+U玧Z@YCrŅц`eR1)sMSgGw,-F~><@ynrC(ky!c7 )5c1bOsWCj) #$b @0 ~EW.ǬoM$D͍Mȷm<!98>Lfi#wGn=/MD\[[m~f'$7٢ɟ6nX }<Р scɆؖEH5  E*ᅯ~4?.e:1}مȒZk1螟7ʇN/|dRRZ"B_ЊrO&W?Xs\wvє1#WgHA^(̥~Um싇  `(Pu!<ܑv_XVuܥV5*^Erۛ|ܢHi7u'g f{7 ~G#$X'ᒙDҖ VB~**Oxq"y`QqNNHw*`&yV ;s!!QU{gelJ[=oam{ڐElԔ bēXoR^vpO_w);ES9z1hLd~wo(ZgzKn.]\F.PwJBpc={qq%~~Dby8H2 Qw=8EvA,RvDs=|OgdF$ǭ> 0$ݾTdP[Ef4`pqCiL-#$Sv FT(TxM4ޭbݶ/h!2;oto)Wц!iO #N*_֙4L r&xX,u=uxArc_AgW>c^HM%Tա*В?P)).#A zxZ: ťTZ5*݅(VW~;jgPˮ/UQd#+OX }U=ـ'd{z !E/P7@??@ p_7$O/R SBAW崤z@g*Ɨ\)Ib#/!x/^ go*CL.g߿S7!b|kDM-M0{saE@RBBUSD;N=mulJ"0o"c's~#, @Aǀ E.7 ԹN#,%#/bc@#$$9nPҀ?l*h βœʼnN蚾F$ ~@w7v=o#/(kzY#$=,p70$\6Cp!%0["DZ"; h9,DuDqoCI`#$ q-#,/0B&X8+`)$rF"0AȡK* a~.6JP#d(`EEa,a"XT#Q%HBl}Z"#/_!A,SЇU?_)fn 4N[b&x}g#$v*l%b'+e\Gp18OKI!+؆P 4Hq9:@[/kRxN)ܞY{;Q#$%665V|eY-lU[W6ɵ\m^&걈QHÕ A,U gE& dMW+:G9&B8T(=)`n^7x9HT3ߓxltHhWqQ B#QF "T&[Vu,mk@!T#Kmibo*(u㯿dNTE*^UɵO%!7\31=t:)E%B*͙ AL ӯt4˦9Ř%xZP!Pn&a#$ >~rDӟ #$iw1$hmy$#$[LՊu}`9T@EȤ B"P0s*Ι4KRE LYk!vf;ykz}Ujab6\v\etoč -A5@>`͎ve#/ Xaݳ]\Hw9w}UxѺcSK)PBӶ1#/_*ň/><8'zuTSC<<#/R?OͲ5N:9.#,ҊkwPSKF--% 6q1whObCGiA )!,mڢ~ P0@Mul*'|<ʄԬ\(:ÆzDG(#/\R#/RH u5׌ikU|y|[Ai! u[A`}WU /ӜO_ؽkVxLϤ0,16#$= cU:ٛB(Ҹ##,TomvxkK>U o*&L;.$3y~wbu 61 9Hg CA39J=U#/K(bCNJ#/,YpΤ[(`pHA${{Jng@*o"3ܔ #,cLBGƈP#/h@=ݯv _Do\c\GF&l.o6linD CD Kda3-UX#,ӯf] tJM/BC]郯i^=B#8q$ŷZez"<&i`4Dv5ʌ.5S!1C@w28ya b뉶sxKuyZVk[U(!RJ/zuYЋ-H&IݶM&L-zm}:lTU)big}ϡе!f NRh N9cXl{SFC\Q=[ ҕvIpe}Np {uΤ]"QzwJ"@#/wú#,?×dH F 7Y2bJpZRl ?0߫"B̡؀!*"Gd`t|QP>gJРVC&K[n>Yŕ^Y_a>{}<@@ceQK#/-n> ׆&0)x#4#6a}%C? p}=G/2~81q4=8Zbo#錖L6w^& >ʻ?-z0ǬAW18&R#/R"兦tw7✼wڲ۝Nt<)#,0t~o u^ǛoC!KG᳦U8XAW_'ML`RZXZɜEE; ܮ:.g=CvC~p?@)r->-#$Lb.65x ܙɉ)ZM2_>,)>>{v@POM#ELHLߟϟo~h֙5yғnmotX2y$t*Bv}-9>a=/nX#/IT~9#,<0]5'0zr2kp>La{';3-)H;F=ib Jn=xg?OGO]ҿ6)!uDߢnaj 0>dD \ #,{%KhC[i$-̐2t@ِxR^zqDY1W{۠w>OwF#,=Q!s=fAH:UFƎ/^eB}Aq).WQƺ]@>lG>|4`?]r.銻JF/*Dȋ\t R122x{1ہU]%EhI8 NZV@Ol}CwC>>mzsFxu|VVL0g:i">gBml}<-_^|y9y#,Ƣ>:YŶc R#$#$t@^D+wX_-~o"aOʂĄV7f|涿%]aW8+~Sd*?}#,Nd*TH9rCQP^дģR!,OG:n7WU7\( .#$2&_\|~91Z#/BAE7)ưeqYhl*BIpĄ~g_+oէ#$0 KZi]}=fbS0ҘKUȻ}(UHVEB'{@ߣ;8!d>tjɞ4t2K՚uUJ)½Nx.ߏϬtpq3 ̙4;o#VV#?vfh=߯yc}`aa`#hz'2.}Z=yǯw_T?P^0g#$i#,#eb۞ƃB3{a>韲Ԝ%2?˷̚5ÐG8u k/3Ee٘>i G`Ȉt5cUh^ݲ#$01#$7mw+a=02 eQ[@36] n:,dp_dMZP(0#$b[jCGS#`A}H\P GJH o|l3Ҋz/l:C]vx̱xn%8 sz$CoU7siT"tK~p}0'Rڬ*xKuXIN>MKt:SMUFAW*oأB>б C0̠7w菙\3ヰy+0MNJ| #,<;*ZqA]l~S#/Szvu<Ϭ靴VrsYQ~q.-"5zλ\Ꞡǔ3N=} 2qvt03M9d#$Hȷ(xbq!(%Uw{_Saϖϲ`Zo!V#$a;}u#/VOaTX'?QLG}<0!=ިEI:)ƭS9Q> ῴÛӯ4pZE9BK<ڝGIFܽIlg;O[Kq6UN_D9/Tӳl{lĢ.fMI?S2"أ1maVm)luXp:GI'w]BF2qM|"]Ӻ-JOY%bc(iDRѨs:s bq]\:h(r'3yz唵t|h~EMIڱq8\{S-v|&J @\Z&=QxޫM#Hq&6KOUj+1("67Pʆd.Up$ r-2_"~72K'o_v<urNrr?y/Y4Nq+}V F=E|"#,UGҬ۟Edݏ0EfCegufm}}pDe7tq~@96n?&*%Bh?Xvxd~-pUFEs8.)S={pHd?Et3]S|$|ߩ+g}cioY; 9+ǯ}l @Zp@@ɺ}dE~(^_g#,i?='p{`AmMG,Ap KcY{y`PsR+s8t%St;~eOM&xvVtbMRv̉RMT}q_!3HvOԈ5hd!^ zr;8m#܆ cqt)3c#,aU#/%;Kxs?`ٍoL0k=#,,cc~r/waigTN ÒsMJ督o?Ra~o9/b IAٮbZiwy1ɤ[=hfUwݍN{TXPD]*DCE4IIܞvOzcpUʏU@,#$ub9_ԎL3q~X}!ٽJNi0e(b<f"'g)!i=^(,$ bKR.&kwC)Ey(TdXXy~v.#zTS ]"@9h\m#$`OtEX)+#/C!/c!nGVt/#$Iʁ/-VK sN#/{#noM 'ПM]k6b ="Gp]CgbѠvGGH`# (0@$AΞ  uڶLU̼:G릂=OႷcrvxPw#,J^!ʀzrvKGRhBD"Bj+;uzCroO8,=lJ^,. #,JCϻ&<}aÃ2?#$Jǰe^'Z|~z>6;du!5ă^iu.7Dfk|z3sD5K'S#$q7&-#/SmLzP>:Q8w76NWU(sZO*,]TqX.SQ%x]}v|lסT5"²pHI97ü7"7F~(4g7nmW^JNgɘe;gTLq>#,㇄0?J 򇁸NEx1w25Uj .%`wD<QE;FbIB8nX޵@r]6#$3TJ#|U#,>IbYuc;rEPmS_v?L_"'(PwVR6$ZgWG^AEҿ}J#LzԯҨgCřpwwвj%9E ѭRn#,;7 ޱֺkX~O98XR[Tyd' էq־ˬdF&{q>z^Yx9m~m[ΧݦŕuYƏ"9gHX_LԟgZ$&'윇Ǖ#\յ+ӷiJtRYƀepy#U$F:`FU\x7=Z:p'I\fAh3E0\nKǒq tKD,ϕ>r ng ۝ߺM֡xMoLՏLɷl[<8Geq2@A4IXb74\#/Ru3ug\C~__0π(/tsu6E#,KrVLZ#,)Eg*m=g^ i[a\ӈ4Y\Z#$(N#,E8oPJb3;:ZTUt%&%Pb~d󄫢o:Y}LA#,|e;$QTD.IN$8|}gGF.#`MÒytۙzP0wNCXwᓨupd5a_#,##,$̔Y䦞\,*Y6]#,^X፞0iɱWI 7JGگK[q@ x.i4{W[dzuN"Ū,v+|XJbZj\{+i(s#n4=V*1 NaLQ:QA%9H30vy 5j^$2}u)-Gye0 4-b`N+bv^;Q/,vn[is$PT.}*Aȷ#^R% %kÝՄtUG?$y`lAd:#/_ Ӿ&;,h?F1]av).̀f,ύ閇jc5LNa"Nk32Y,)Pc!g(*$T Ȝz]o:/ʜ#/El\`!$\l#$"pfب#$<*&A@n~~~<8,[ l#/ ȷ"{vʊSǯ޼.ZJjY _$#/Ժ2ߟW5.Iz)xw#$^@?6y9rD4Q( Ŗvt;#/%lU4<$b|PDtݔ(8'Ej_a:?TZk; ?[S8f#$>Ϸrk 04;Vv8Q6~޽^K:Xk "~ѡp 1gc?ׁUU}Q|t#/YTJ0o_p|?\̓P P`fFFpۙj2Oa.P;R.lh 섃 #,&H'ųV~9j{{,o>]ۙaMKp]@ G!1֌/qTA5#$`N^ͩ=(#,7z HaӚ7uE@`K]U!!S |=N`ny{Ca?R>EA Ob;"X;`]'Xͅ\5#$?[mas>PQCP!9Zۙ'}u 6(nʺ]M߄ nuOUUqӞ Ś-As_S=Җ#OP)Wn86:VWw_rkmd#,T$h a~V{s.! LRRrAtK_"tO42 x+wO\ڨf!f K$H,#$?P3rk#/'ފq!b`ӘZbHbl@*CؾA05Z'@~nZu໎}A6-IukFPil} yX["K dc', aq. ޴Y zed E9;y$sV5ظpMIaW8C>Q~#$>]3o;޲GT%X{~wdM#, HccIJU#/j+0|HB`,)$"GO$8G3ShKFZA{Ɠmpg~Ҫ#$RN⠒J^7=f:Jg0:ԙ͵.Y8yELajvzL^֚֜tgx-$RUURVHN9^:K\?zw<B2 m:`,nw<fΙSn4[VwOe>~'Nfx#$3%,I#S8% Z`f#/1zU6(T<bB‡ 3# 8lqCOKMNC(KDf. kL[1@.rvE~yϫڱJ"H^3sC>Qik*,@#$h᭠w&&'qG1LȆb:!?䆿A CLSa?7 zG:Ct%#/Au(#,h#$SE8ΗeA05,>"ɐK#",#/jP#SpHM)@Lip`v$+1@TQ#/D[W v rCJMtk<J1. BaDp(.D?3}?Qun.`UqkW=xk.k]RoFJM3"1l,W#s%ޣpL+Ѭ֙!>ӒStnl+CEH$LF&*e֦Yk5 #u u:ߘ)߼: o-.B^CeK&怌QHKy-q-a]d>a؈ZjH M)QlЪVY$ rQI|ɒtn5вV $lC ÖRSYۢe{eQ>%M+՞|yj6#/|H߳GHE`}rc{S`067$r780;6)$"Fߎq)DQBH1U@w]}'iύ>:#,]qJҊ,,lb .}BۑL:"yE=ݯ5OȏB%#!51Cf<挷Dq d#/b{RYPP %(*&'kni]#,&~G8OA!h(q=b-?)@(4Pn#$Ǘ^xN6#$hv]/!̈YSʇ#@#$ɵ;J[vm 'T^^B)g@D#,V8XC4׃"! sCh;Scn-8>($#${z7r#/vR$jcؠQ~E {q gm5gP;_gaR֟nP {{BdB,Ce6>W/+'}OJu/UP@,#/ТM!w{{@ć)#,dOXqTS !V+\#$~Iqo2׏Q95~*4F+d=cs:5R'~bwWչaInxm`D]YU dh`]Ӑy@< '#$aZ.C.F%%  6^x<>%YUң%M5OC*g2Gׯ' hJeTN;;(q IdEQ*B:yvhp-7-%\kM|L*<4a"H,G@$Z̫F:!bR%?.'2>qbJgNgIfVOPuٻ|+l5vF[7dNUJ/s<}<j%fgf)_o#$h?*((WPjҠi鴁|1rF6J!*ZF<;'#/:r"y4B'[>%膶S#,bevGk=Prq?#ЮdSEQX% ũS8`Cu;)N81Aȣ:KJOM%]z%Ow|Ǹ`b0Pp%hm#/ϗ bݟC gas>ܹ^[ւ1.?B1*g2#$I,M¼082$ 4@RّMc& )1rAH1$#J*"9. #/H9!RugBdV2I ;$+#, e$t5O@~)۴LAGӨ,3 `#-7ڜ-kq#,#8T$5pMEL#7˼(5^6#, +-מeˮx׆_/?R#$7YO{OX%f dK^E,HS#fiY!ASW׷v3XM]U|%$h6lZwD9"Y ]wu8IG'3}^Tx@6ڟI8V,8sY! Q![GIː:ܳ'v[v6⋰7L-*c+egفuYY缒'7KK((~OS_xy{;ϪGTHV7~=u* M!hUf7L0ԅaB7Y%Y$t38yvkR 4jbA̩hx9I$!E!&UHQ 2 @5Rʄ7 X>WB{5#:1B#IW.q)cQGhIGZOHC/Wj!#\iO#$t~+@d(),g %ULJXE459y|'#,UA݅#$oPC"?gG3 ,1eAVs L)ʏT!_Ğڠy++.}l)2_Rpq'P&1\AAUR"}.>!]ݯLzi!qp0>vFEqsAc}Pqk܏"#$nW>ǐb*A#$u+%Ŋe*t _V-C֧PuXoz@ƧI%h?MpT#,N#$eIოt`x"H,X4ɣΦ\|*&' eU B&K7 .hMvb*VHxy@YH^` HB2~j3"x@Db!nEz7fm1qٺn]oA͸Htӧ7lE6=^AO4G$#,k.תR@`uͶl؁#$PXLE{Zeyy!!#D"vaCDY(}s!Ab_Y N>y@RzwGypLAy.d?ŽgE)]N<<bHڇxIM"vbcHXV;;K 0?Rtyo0]wKv2Ct s$/XvrPvSDGߑ"i†>Tsɫp @79NPŃYypq>%B#$| %#,{b@: JՏe\>v &OsԹ2~H=;{w8K١J!EC*#$=j*{zhV>ˏ66"_!Q3-vOv): V1@u~kht65H7?XP5_??|S=[ 4AX%"!HF#,3+H2.#$6dߺ%O.]@?uK2zmQXW/ݒ0%!L;u2<;6ݰNJaKd?"#/&Йhq9"T"#/+Aᩅq?fjB#,!S[1׈0~PZb@URcK`v\XS\fP#,DRH2m컕#$vF`z04:sx@hT?қoٛ $AfWXl^~)_jvQFH+"z12'CTR_ #}ܿӟoW: T%4S5ْhWWwO(s`O΃϶uSKP\VBZߛY XCf!(]<3ߤ1:wqhi"m$,Th($0y#2;䂾%QP(c02`xSfBJAX)x9$WOg-3ˡd% pNTӀW&Rf0x4z:(BlM,#/J#$h1@S{=NQG?7삏zn&FXݽ6hq#$!*9W#,99p+5;wBEGYgi@%?L-$(r ܢNJq}߭}57% ɮz 5 {}/\\_GHc4E56oQrKl\P EGrn8&9;nBn@'^D.~elQMU|woWyJJbh0Cj#/#hy'-zjn+HguG.,7$Q[)>BjǕo򏨻՗tNGt5憥pkX3b#MƇ@$v>W9=fꃬDe &0G+a/w9OTg٘zd}=|ysgD_N>xD@Z06Q-9by?Mѥn2 y#/@JvÄnj}Z̶M(?j-Qrw~M6;?@~-TwdE^vm;|Dh2޽ B!zoMA1"Z㙠Иt6Jp2O^iNqaHB8|q9"oZe$TA()~!ǝoq+#,IiZ~Ra>52}:E`/xs;hLN%"]b@6B ~ZCo;0Vðu͸.efB]vfkg޹:f"Q_gꃝ6c9vjhv18IՖ4u3#/5"瞽i9qQhHTܘ]VH/^&r6-ib:CO#,A : _5?gBY;(Oa,_YM ,,#$APWr8'ZaQ9ޞ~J_^v¥}u8DLAIk}?/~n |9Q[llQqL1#Fk]S@V5q?pbj#/vf]Qi )1B#:w^scЩTj1Ē_#[&unWx>S2f#$ĴvM㲷EW6bNá;8 ;֩R iU5fCS+fYp`/HAҩZU5fC{p#X7aݰ'gϝk!sC@,,he;.>V!9m)11;nM]MФ4ؒ\\MPsp*MTgW8I 84@ư B1AgM@;H'0|34ln>!b+QaiMzԽ۝TH#X3աXH 8myI5negkxGV,?Bys5r}W[0qcQfz,#,8 B@l!cJVDV4H65h.1LJ$#,D25?0lʺ[6mɑy_?Y#,~򱵟CMDuwO5?*I܌160Ȗ?J uw8Ij#/)5ޙkr5QCg`|23f^{ӊyw+#/Ocߎ?#,11ֳ^uu!D$ )~xHk~?ju=KP$1a2?|Uvvyp%P@U9ӝ>$&m2U)upZHŁD"S^!(("<;'!%-z+Q`>?$K@(k OxX`Nb9^I!5:EbUCP@;?PDwFą\WUQs=$$"9)>LԤCjb CaDD5, Ԭz}l-hF/<ՋL>.TN#/hRH k.m`èkdEM񕉍}r5Y|\9;ͅIY9du# 2eOy#/#$ ״7p_kr=蕃ͼ@mN:ôE@]2C#$V H{:L^J ȡPI#$ BG;HA"") H4@1"`0qI=VmKQJ>,(FZd#$em^)؝j0i1 IBŅy%2k"EJI1oWv7ģwvJ#EVFrF7 F2VWvI|u^r{QEGl: 2B,$QXPI}n0cD$OX+A/7F}TAdeg^Ӑw<4Ō*Ȉs u 5$`Xwh.Ѧygt>6LIo$YD;om#$q`Q'e##$`H?6aKv#$'hUHjey诬#g7D)80!lyW8&̆Mup%$4DRMf4 . bR_#,nXo7k)RGBY}.zD yqm(:ǫ' #,A#,X;]׶It li!,maSz0Dž/BwK?H9:$|GJA y)FX-D'@~W(%TRvu!ro<(u I/g)@$PW! ,_5NH1J[:愝 j$(/S҉辰&m՞Ř̑r`#/;wcGĨKDt I"bVd6jƒlJV++-MZIkV2MZZUfkV$>3Rq#,b' F@^`{`oq`Jtc7d[&nd(`<46:#,C#@cZ*"슡rb2|+ˡ$ -& M:Wʦ憒@47  X2W)jw*6P Z(xGߨN{uqUC&ݨ$[j]8lJU޻(ƃL`*F"RulL@T Hh3tBE P'Hcj͹W+rL5%lTZj"^g<csL#$#$@TE]xVǘ}gi8\"m#$DA^Gx]H# #$7{=g(>S~'0Igw T)1#$ݕhhD"#UP/{;|tSJAq!5)_[K(ɝ#"Z 7$V- 0jCKr?&V~T"`dCc"(ew  ;v>jtmB^P/ PX l#/̀8GIkl(2h:#4)9|`sz&(ǐLy*_]_5 abh׽}!ؿwfw[#/=l?FS48|^+*` >D=ҝFWxч>|lND-0Mu$*Bz 9 훻5gl N .[O9/LV8cb¹s {"HD{JJ|CƎ}.y!<ǴI%OU%rJ5j8Mr%5'wQPL b`H&P P"Xϝv^H#,ښܣ#$d|9<]B%=Ac+#%KM.~\ Dvu!XۡX:&+&MՄilcCMr{\t@skKn:v˧~:ms8v՘Y{HۭT&Gnl=k_Mhz:0l6K.\팩l7< z M[g^MW@g#,+ű|F&hջ(t؏˿g_Z#/A r"+D)_Dz]B|CCG͇^ϯv&Cuv`FzwH[TSPKThVB(s݇V{s!Mg\b/:e#$5\c #$nEcFH<$"C>plءS'+4ADDT oJ-gj$|0k1P3@ch1Ecn3s}_27un6{;Ĝ&K-y^%߯}zwirsD鿈#,f$M粀-|B: g#/4pׁKDC}|84O׊Ȍ4VK*X1{@R^ܙȂ! HjxOuzۧHlm]j&:#/:}B#$eH;o^Pz{~5#,̒0.Žs˲&y#,kװv|T ٢dʇ)ZRI֦tԍdG]IqB'}_ |yN&lyoNS5@1FE4׵zeQ ANf-jڠh@yʱ0K`jIiU%Nl\6ڋH#P+#0+#/*R`B.[w#6QnBe#$HB+ z/khjk%Y *Hv)8l)D:ןg#s[_ORRY@6t2k6߱uOeg#$#$C#,l ,uz?t] /2Q[W: 4IF/cah1PS Uj oE=?mv Oj+M#P0#/yiR5+'#/::fyaNqB{;;#*mck*,R"TdP!r7H[,hZT"XRvfC(q9ȒHE(!sabEx4W9.y$&;:қ> ?KuUy}>#/`"^%Jn~M);AJRM,m&mUUO0{ IUÀVgVQ}*z m C!Dgr0< Q.Gc|R 0#$DRhj)o]DоOUBHp׿L(:#,G{x*%(?]lhX,Psp-#/H5AH|: @VF#,okQ;>]mtMq:v,1gbN&RI$7MW@Ȑ#,&(x!׎[l~b@dpIDhN@x7 fj2B+N ( Hgó9Uhoz 6FZKY()ѠE]M0N6Ĉs`2B5hi"a =G ن3ܱ̐xشV|<ީni5i0K "R#, Fяǖuv^^DpJiLwHSɸ#,a`M| *1{9d_QtFͧÕ:Jl;02x2w&us[O(I,7m=MĒ䵒~ɹlpkڝjemxv~-)b{|4}B#,#,[׽ 4ؕ!rgN;8#/0ZM(RNOL@Tk=_^u&.ϑk2cyOW|DZ'i.9Y41#$#/i'}-Qc@%Q,D(Q#,"ҒcqsF0nRdƕhH&4!BL iٚ,#$!3#$,(wSm5FrS㋍w4xz8:TF/p8hEϊO*4hM2jk?d!$F|4EoVmA%*UĹۈ,#/kQmxkQ5mmWKK>ϴ_ru-ޫ[ ,oSfbtl$yֵ$$$3)VfIJM1I-&jL%!MJ)%#/+K2kI$S+f2bf34ĥC#i,LRJK(a QiQ&S4c&J6BeATf Am1%4Aiw$lk-o<(y#,a;S.y6KO}HRGxZ붖5R!M wK%:L;eVqs\i%x4mǎHw nkK gÿ6pbV]RKm7#,8~Q-wd*t*. nsP;;H;[a:y#Xc~M;t>ɇ#K';I!RC,7RoGC\CePnx{K(;^{ў @JTg;܉l} w83x#,vفWfe2j$%jLg~'|J=ԇn]$ PVm:ruHDɯ3%5)DO:25+jF8Ve2jP̦ .posYqFXn&sP24c4FвCѣQEMJ5)ֱ,K,6[|f02!n6#,ΩсĐLqǎ !6te*V#/烨i72;!67LLtџkhF9p+i5zY̳~s[icXt؂kG#$,֚iW6RMhţZMDx== Y0gʈ42ˍ%flg&qz1]:޷t:Ӻ6=V_ٲomcXqs['FtQ :I{KAj=1$59:Low4(q:/qnWd/gJn2Na663#,G#,cvCpTQDI@(L1c!1[#,m&3fZpidcRNLܢ7L:T䱺׬U58&#"B\7$iu%HjTHʧd\:vfk.C-,72#,)FyMaڝL -THliv3ss4tӕӶWCBiuM)C&Ŧyp93jR~4=#/YE&)+afC\ĆTcHvLspњnU|IZ`pX܌vqʤ! e"M4.*OfL"Z-,.$P8g}CNwk_#$,a\k(pGSb)(n#owY=hYGzLTLTl68'-#$ьQ 6FcC우z|JɁ[8#,9|`5Y0}Xę+ 9I9gVZM kYZl Vx#,Nz#/#,2NL$13PiI+]gw{=ng|2Y "4@ACb Vf@juLr+oą%ꚡL"RP #/eIu! Ձ%3I89;#/#GH#$8,cmͥk[.U3BDvJlRQ]+y"9a48yr%6(j"#,.!"Bp5əkcp>%p%R0Di+ fd(p8; ` pUC<;& Fz0F nhg4g.3ho$#,DN% C0 F94SldωPBI#PaL#/fiu"j3^"btKFL舅 \1t&]"aY2 ]u8|^p T:C=#/GNf5mϦw#,@DL̯M)`5\~]bUV0-Jk_M1 8##$"ɲE'5oW[`0i#$-=Yk82[@ P酂INUI>w.g{ΐRZ\Jfo7~؋:_mP4ͺX#/>Grubt6T2K͎&̻CF1bzNu]\䈙#,#${Hc4rp?ևtB)$F;!uU#$(iƏm~4mޥ%3Q#/"PP9[LL) LD5=Y!&tk!kjÊZ\axLP_qn_W#$oӵe\ySWwUٍn.nn-sd5<{wdH}A, (|UGLK&J-v6#,|=q%Y0 @;(96ضp̮E3Pa -GJ ZLic$YƄB&&#$%/BfR@l@E`+KD#0Lq]ʄV!o|"`GFhxrF0#/K{oRCf)w߄<̴kdE4B4ӿn.d#u.!66%[GIhHCZ0 4a#/^pr)#$`Db+Fi<-vܢ*BUX.żb]p.Z!QY/}C $@39$G#$3^^UVam73F]m6~RkLp7.Fjkd\}G\)M9"H_Sm#,eDwφ%5tDiޜ4LR#/ZS[Gjjhql"ppCfPf"Nlp^F:t#,Th9w`wA23L[#9I1ؒݜ}=. pBjzSrjxWF(E!Z´ڇ.o. 6۳#,k\f`xkDiYYڷ4$ 4(p2Th"uu2;HB,&l`2&%#,&xՄ *BLP16qle%3$0 @A]^]{{g#/VdϒM8Qb{(=G+t@}c >8KIvO;.?hz KBbUs#/w]ּܻk[*j[Ij6H>f0'wz>(r|򇯩 4ӊk|tu+W@n.rr$E#,{:iY%leʵS0:2KYk2k/ylcR׭F((fE)xpx6GŹy& I-.)Te]%J4D\5&艹pDt7ۇvJ4c:ڝo:hOyU6oI&1" #,JwOX-6QLR1ISv̓3#]7jg=5fgIU#,7 oSik_3OR>;c d*MhÃ#,TC Kʊ{ҩ|&5i,#,( bņcVJd@)5.+d=vF.62*I#/:4,h`БKhPT4I$YY.*RDUai"X/,S*v4,#,B@T*l]QRc,2FK 퍃276UWi5t)7]L zBq9QA}$ fX M"h7LxҐ#$RCGg`2TԱUN9*GvtvF<&#/TcX:*UR YZ2"S FV$שޞx8%hcU}9,mj#$AMiK٘ˆF.mx*P~]('~>7 B8Ͼ5'#/$S1Rm;5}]c{&)t8.X9†mS#+/n(4~.Bq1*LPT_|eI=7ޥ||Üuق Y#,܅$j(*n(ugK}φlqGF ]aPȵ|u#evX!Q#,۴"H<#$b0mxQ@R+rNd#/t(ݓ#$"2! $K5ZszVbN}4,.y V*JNjGM9:T5[W^i%?Z{@<wغlyWVS=4d8u8 w`*͐YE#/e"V.?)& m ,Jk+-1VR@TbќD1h@mZ#,xjIZQIoMhyݣC#,chݶg6&Kq,Te!4t2`jH¦?HkFݴ@I[ ǝ.qJ<BGhQhXv -v=F:qHѪJ.2;p>0d'lzCR={LR}Qw#/BH 7sRpӷz u=šd0-J0U F$\/Rף\刐@'3snn}a>Y5&q?Ni->PcGs$L¼,ET48ܒvP-ҍd3)32mdB5r͐TF#/߯z97ƇzDŽ!j*Xc01,8g:160bX#/t_ `;P R+sg8Kxu4ʹmXLh 4ԤhEd47  F2e4{&pp}lqS=;+RΝP" 6dl#;]ymyzbz̝:%hԐ8f^!,j"jֽ+%ZM:e û(TYB @YqZﵾ3 x  d-V)Q481D=h @dPA ȅgH}q%@WS^#,d4+R!x"TL$za\ McsIr3"1wSsDMN"I ~^;/k9!)584ҋ#LF;"eݴ66$L9pK4#$1wXROaz#$=;No+fy*wЙF|"TXBR1,!E@oE1&$,!I#N\/#$] : G[R#$H4 PEw`X2#/H*\^ᗮg8HƛVBI6;CgEB WŎ^-hSJ]I0"#/tJyt2v+,u#I^'~-F EHvjD|2ڽ=#,2.`:l QQ^#/[jXY!bq!g~e>@J8&Ytzo9z<âWs)PN;1zDl(-& `RA2*ZZIL0U`rw0K:|}Q#/F,0%'`{.؋Z^˗xo"fbBw+֮֫Q]"تמ3ۼv*RJ^dCV(iH@M6;D9;bm5כd>^p#$F !rOweJև31}}Rgs%_^5Y͚-u||J~|:jɵVFa`*23a$*+{ihEjFU4i]t\ۡɉ,ŴjmbVƔU^mkYf߿ɶ+V*rtt7ĸe h 'kг)Ɩ.YHe"JJPB7&r2-o!%#$8w,H-ሪ0 !7Dۗ#Wg8hjnI`l3s=QPE8GBҔN#$mU*VzPT*dWfTp9 z :8}ފ1CvT*5HE,2*4EQ[n_Oӛ^ldi8แ{~][TyP"i1oEq8`ZA#,ơ@ (,]P3EP\ m 9{>0Ph#/Qa\MW>nlpTe(#$"BDG4I#,`>~]EcK>[^En&߭=ŇwuyU?G0P"$<ˋ#,u1XސxsĄ# [MQM> r;=EspyTI$@S)*UdaA#$dP!;f ."+Lfa'v5Cq2WF#/ł d/=bp#,=4||޵?`֬}c ؞iJ3:4GJA8$ $ % v_lq}얩-%"iyHv}.JϗKA\:?1Ԁg>'u{jHf)@PvAKfT5uԉE@Đs|yO$ u7ih5P**Q3n) ^J2#$v#h#/ L h^uI#/muX".Uw}r$M:g:F0 0#B &Dfo*QMQoR\!$Z*SS [Uʍ#,161Y+B)6]Z`im{UڹZ1mj#/y1&&e5kHƃ{ʹ[pƽɵxB1+=!`3VMF`L%Yloe5tp`ThZCd-\=eXwY mC7U66'wNb6+خ[bLkwƇIaH/Y5Mhިtc Ei4&FBBԛ:bn@ 8ZԬ;MS+Ccl$($Q<7և}l'DȌ?y0jO#,>oP33)Y;uPč h`'SI)İDЊ"HAq}_"h~*D<%;^ m=cqC3Ѡa#$RAV(FW"XėB&1P#/(7]u|BSO'6ǐ&@8oUQ E_V4x/K&KyS*YUs[ůye[%k#,Ŋ껪-zj(THS#$݄. .UL)%&Ilm#,4#$#/DdyEX!" 2(j)\+Hʢ>s0"He`ɖ@29$"ב *4D l?zaL-~/}mXp[,'}@A%ǻ#Ǿ#,08!DWbUTUϟ\6F;*4\T-Yn9 .A.Xr,sO/H)QP ,j##$CgQu؈xh: ()j (Җ3iy]4SԮh#0P"(TB:'3 r<=Fdom@YȈ^(@:4B  kx`Jͧ};#a$9ӪLM&MPo#,g8bCְ :qj5,bcC&[E pc#,v Ãܘ}#$(r7Tж/-OCXDI"BLP'Q 8ᛕ>OԡHT {=i_0$P0ZiC`l$s@f{Σg4~#/(jct3$Ǵk#$C1Jtff#:I0(W2g!T" (@!).TΖ*B5M >ɮ绉pC'F-ϪɝͿ҈?ˋ4YI2r*Ɉss/iL=Xʤ'N}IؚfߨCw u@X)"lԔ,1Mm-IkM*erڙȴP+$U1,[eU+M-f[e-b҈mL٪MSlVF6P4?;J?a>m\"f"#)5$n5F]M\#,£_kj奧թwuox#$(J5 =H$ⱁ LP<q*2ETwĤ,0}Aԓ(lH.{K^$(Ϙ#/z<8D (TUD ZqCX~+a!#$/H#/h" {R }*zО!{9^űxʥm#,^Ќ"#/o=Efwtq\/K 2MN(izI$^#/Np#,+UkHE%l0w֡r 5wyc(;0MRx$kAB#/PPv+"!А$pI;j8J,X;rl(~%>ce KF2S^F4}[Q{pw:_q8t^0]鮐_tba {xq?Ri!!#$56,Lr`O@RMR#N#/))e% h0@P0|~Luڵ/̾U++ʌ$=L&7`{ӈvބ'&ѬHUB0G%ViLm p&XUȷlj@YT0X#$I#$ l@&(@hdEiR@Y,"m0#/҃Jd׻:Jw6vǡׇM;G6Cw3NQq !sz) =~'!eV[Z#$#,@jCM&A:&"wT*z%0<$@U dKiyۚyFY#/(d0B*LBHT*F2 >QAl G(]kRAAPGpa<I -"##$K"m aF"h}3ZaMi Ʊ4 #02 p,PTUi(#,ie5YB-eSh- AKI{6&6pQ5bP#Xed9qB[aȔf#,DHF̗#$L&D#$鄂J ;[E7rl!0u%ɲو##,%#/3ھ ;#/ehd #/X@$G1%<9z;g`Pl}>Z\[޿P j#$JX U|XC)DɔĮNh(C-ƭ]Vi,Jؙh-&RUbT#$}lX[$)#/`"gjw$10W,%6tmS6">Y!PJpq3cW 5AG[GP y`CzOGq#/Wm*%m>k}LidTA7#,Ǥ{׏x}h`° j7 #0뚆q|L=$F*1mONAb,Q&3k&72^A#$4ߵC0[E2,)')|eVط>f c}7;QrZ'Hykh1&z>{OkDC߯tf(5;}H*p5=}e#/N =a2r1)lfo|ƙ׏j]N^y#eX3oabm>$2/xsF /}OrSP6dj؎S̓#/TOҐ #,:>KшF1"#$H WvŠILFB3TKEXT",!/R@0QD#,[A\uAb ,Q-d(QbH;(Vy۹uުjۤ#/D!#'v#,_ҏz"N=zE<^I&1$ե[v$|vٛysS!>m#,:4 نYa!p tRyj(= :jC4ti7qrV,%el]dlΗM@AY Э#+F43@4ja;Vu1"BB30-W;1mّF&mgV.,P=#,nWaP#$ARy6#o!cw}Z:|9.``}j.z:棰|8mk]b0Va)S}#ծ61VL#kcë8:SQ0~}?jB,NuF/}3t\n6b>tEGtLIbUؘ^2#/#,ehնfa}C֡py#o&`#,B#/b|\@8z 'xmo#/tC|׉Ciְ} 7P.Yf@@=Rn)ke <$t"L"b(keݜj#,rDL\&23uA0"zBV#$Ъ@Uo4l^CxLFxܽ:BM!p<ˡ1JL#/3($rX1ߜ]DI"x;%?X,)]1AdfA1c3[5hJXh>i ,#rL Rz} A0 TJ!\C#ÊATNA9J\Պ[k-ƍcTѬ6[W\EMl142];tQb 4Ȑ#, Fd*Vo:6v3H7,w>7NbMJ=} 6eѯuSA]5t<CQd"a4d0&3b(؞C9~`E hy+ #,!"!w v0a;}Aɇ_@&vlp`v㋜@;$8߮_2CFCC[|C0a#,ڳͪBfC:ȥ5MRiEߵ뮘% gRHBoy(0 e:#/ X#,W9*J >2GǾ^Lw"ZzdIht@=l@b1DHB(R#/QfHk38  p7$d2A4Uj@qٟe>{}O뱎k&BT6Pbd&=vqb!&C]2K#}6aܭVǤ(uUEBm=eY #,5aAUG<eem:W^ Xx!DѺZ˩VS?Rz%]P4T#,uFB#/db#$ޛ%'pHhLmoٸ!d&~l5ڿwυyvIZŰ6JsnI!J-+\x4{*8ɴP n܍ka` 1iK0K(HVmt|;x4mjB[sˑE^̳#׵!fBh$Vex(LCFXf6ZIYZA%F")"T&0FV\F o C+vHݍ ̃MN qT7v:V&&9kckR"O`il(Aģ&ҙ#$*M\2h}@q-SJ^Py#,!M %(iD@*#,iR*#, H0c(BbcI\H$G=NQj#,b޺>K2):'uZ\]@+UT9>X2?)(Hgo6yOF>~o6}w<]Sl&OyCɽbъFg׻Ftt{4zs"UkCCmc4f33nNf3nawhAf#,*&cv hU6dI#/껖Ciyng~CoQtPh]a&,h#/?rtDɏ.Q7gg^iȻtڃSKMjMi#$cB_h&wcNewo!b#.jkR#,CS)B0|;6\|>`io Y!#$ :FE%l#$H6Ƒ@ؘY&TԪQ4;U\#,6#,j:,U^u˦ۻs!˫S,-"1R\㺮ՋRm*Ʒtvym*KhIB#,/ɋhXiBVY]Z(D!B4V k*9"0 I2MPVL!P̅`H@](k H%I(X@QQj)X4*E6+5IYLɉTDmLɲlklj3#/"*@}㡠}b궼j*DI,o෫=w.g6M+(r/Q$ ,VjiV9hGoZ-Ů3Dmwg]kIK[smImr泫kJWݚE)H fFH@4& $AT Cp&q̐!dFXCax[D(6a y$50^5+)K˃^,La>5E0.`*DTEf#$xٰ$O͸fc114&1 ښn 42G;oq)mwyڴ! 6xLl) 6|U90NP s'WIv6_7{hSK͆dė+ h%ʗVMq#/EF#/6!/($X$)#$y#,q\w;QqM H#/ D _>: &/q!@$Duiŋ[Uj%NRX3#(a(Iƕ""ʪ b`'H+l1mjUu\D]"65E=SIDNV'pv:#'\I˗3&I< j6lۻ-s;9`S@$xLDz~^k{`xg|@CF'~a!Ěq8PskZ#,d,d?FM8C#$͡qΆp|Q< CuH]#,2˗ TY#/lxWMgKf@_(#,@7"Ihe'hA6#,+1QͪcBb[k8(DR%+Hh#$)`;Y@"☮-%uAȯFgd_$-'>&6#mգ#$3U:sNEO>lM sUtUݬES#1 gJq;S #, C\VHF)O`Y־ɱ^iwYrJ:y#,NneiȚI 6ɢ%& snW6to.ul:+H^-ɶ9My-˹TQ#kj[F,m:ܦ̲l;7NZHNrYh堐JB+P$F+GlEvK"OTp:"#/:L<`8#$v} BRD OE7'<Ýߩm#/nY)#,PQ#/f tnl>#,`< H U:#$EQP*B)uWf׫N٫rYdj TNp,bF"0w\#,y'7 $d=EOD[a3FT5e&ڗI,4J\C}C^.v51,&8(BIa]f<dTw±mZ*Nw|5P/*½a_?q^.ZODh9T뎴n~UAuG",Iꡦ#/\j"ЁD뙬\Λ[@-#/o g ޿~^P?A0#i Lѧֿ>I$#$t> *hBxϖ=%XV)uA_dEQ{xKrYopV]Lw0(hgEW p PEG2Ög4ģRFŶȄ8inDhX&6.+͑ϋ #4ON'8"#,$xAd6Hǽ;Cikbۇ(ެb۲JCD۰Y"P 4pQ̮^'b=ois EQa}ݑªvsFٓeՓLc?t[k4ip,ڶ+hB㜏cCԿ4}L9tE5&1++s>Y !#V1@x`؃z'Z3xm-1I4ddY9&=!Fv"GD!A&ќQl  Zvb|)J6P'srF˛6ұTeB'KۼtN*Lm t#/Ɍ֑*-5Uw~޸ׁ*h@6RM#,(Bf ݗ8k`s9S(n#,.~+x#$I ܧzCP_o[ZZ&4(v/WA֩zkLFdm"6uǐ{Y>g;%<ͬ4 om9p,{+lxf#,qe&C&YMDW@#,<&=Ts^TI ${¸ro8Be_*<T^Tzzyq ZQvNGk/Ya>ðQL #$@1#/F#w4u 㨓Q :~#/^!` cW8fVRE)LR}5*4U 9Ȑ@,tiP.xG%lo#/t3?jKTkf5" ld]P6мY-/wL3/H_\| 01>UڵZA+_z^ ;}ۥtkmEVVw#/P52Cu`+qe] ^Bk#/A/؆hۻ0oBBF#/U1ȹI#%1Wy\.\i N<<Ä[h $-˹gtɏӽwhHD"$ʆ8c݀xHBԭ#, lH6~$g#,}"\Do~3HMmi9\]IXmfD`@:밻^5I mCM=6O#tQ=C֥Z#$EWz'263dј 4PM6DE&!&4J1*FMHId,b?N\_vx!l*΂>&Û}nfݳ(716#,)5,k7yPʀ&W1?1oBG^#,4`m@@{jE#R5sQ!1QLBV̆L_#,i5U9|QI>c&A8bjMwd҄@"飐2^Ʊ`O׬3tg-#K c_{!5H6|)x$&_[;Q2|#$'D E%S)JM8Ҏ#$`RZbd-7QU;JnVݮsuv16M F#,V1r!m؛h@1AH/+lJT!v%Le2ś+QWHH B#2p*[mˌ[s̠e"]@-f@p ɢ.x*U,S+.&L+?iz,G.c*dC<'5R_uR WE nT:)ŚPy`-ppln8$FEh$#yW!fj)WMSEEm%#EZDHe=TlFahiB0hUT#,Ymۉc")65IMX#,nJE*:R\+ PBunA#/piZl#,-ao#/T#,#,#$\ĽS0%H%*p]U\j @ @e1xfdǝE2V#,07Cs`Nh2Ga%cQf=,$]:5AЍ5U!ǝ%^]3cXE-64x5H1#,]`ѰEQ$DؚgS#/hFlPD0#/6 TPE& lLP{IfE$C4HPv%RŅ[TҲ+cTd޺[q=\ܕ_ |IAbXbau+U,xJ໼1im0 b *(!"e#$]ێ/U}RiD_TY>2EB@!T4ڑBg{)"{*RTC^Av̱#,* p2}JsQ\aH%7VCדz,]ݱ鐖0m4 gHQ6Lm֠1̥E1 #/},(i40jADGpu#/.<_#aa6Ļ;c\%ޜٮ[섋:)0|^{-k ;J%lJX!@t%C˂PTPX.dp&:={J*ȔA־)<}SSe#:#,uy8gK? N[}G:HX0";̃`Յ&QT*:7u`wbnCqؖ ګU&PAJ?@܈~(P2M@{%L"b &]ƈƐhb$m% $B)(;{x)iuHbރ`15" #$D@D۪Э`}ы*vxlH((,IcWo}* @B?#$#$HeՌ4 4l"OEiAսCBR#$s#$Řj Ĉ)lJźXTʇ2`9@FDLbSU:Z$0lbhlY%)II)hХ[DZڊږjLXբMl cnAFHm l#H888cx܃IP#v#L GA#/Ҁ"QatpM1 "c$(UL4"%鵝nrE@ ]FB*#/@9X.vҖZ (h7wNõ?"^ݛkD"HAlaV:t\E(D#>?s'eO>L+KZ(QJ RF@.TH#/L@p*(6 0*bc}UP헁Qc ܢXhD)YwYci f4!#$HN#,ޝw*ra HKTJ"pۥuTe .ZbUjsm[ nڨTb4{6̤Big0]!>WZ @CͺC.&o|zYF"``΃ՁRՠբ4/!(KšET5d5\"C#{`!U94#/d{.+';<6D!JCN'cGdq[r56SIt,D'ހX> *5ўdW\n%fzETR!#$[#$]qc6:btu0bY|uƶ݀ ἻFzZ'\ptS>88-,bIN7.SOl:g=yw4D d)ULHIvzZjM.~2 #,L>f0iڻD֖Y!bSTl p,5қ=x~ٚÎ#huyUE3u=a~ja67{}b;Dei<~:]zc_=M)K8;vXqXi1ЙA0̙\KO3fCj0#iGQ1l:nvyS˔(4+] owd<㝴c ގ>yTlώ1'q^)@mBelJH:a19"wx]1yf;KB+$gBJֺ9#/) 'Kءp9`s&0="0Ad8)ΐ׬.7(fG%P@7V߭rv ch9#,ֺ.(#LC*#$@=*X*Lha-Hob徺uYؤvfr(ZnUbl*s+Ԧb,#$l3H#/"{i6D%f\Ⱥx`~Vh&p9f7Z0Cߒ֋N21D![شiUWGbg%#uYD}[tq[b0V5vf Dv:e;F#/ǗMXu鉾b~#2.9m`5/$uRdN-(ku ǸyFY_hL0xNq]3Ͷ #/4vK#/zpB^$w۾ <P2yQgp`rХK@I5/5D4ś gQ%5\${qy@vjhYQnz2wo.O ̗zG Ba#/7Vm艬5<XC{H缹a! Y˝`P—&cuXg2NƷ^򅅗4E!N岩T45ȶ&tL=Inyr%!)BnLZPjb\S#$P}XeR*1K+ud/io #,hHif-+W SJ݁:kN4bJ:`@,׆cɠ(2tHqbePDiOR;]xa%dH̡LZLZcsd0nlcoMۼ^"ɮJ墣ܧy8܍<#mv4޸5+Z3x#7T`,ip Qlnfn07#$mױ4׎vFjhMUzmkZALk.:?F!UJPmNzOs}M#/5w e> #/#$($Fh %Q5ZcR&F+b3B#$`,P*U#K d]Hza,)H0C$(}mF+6  B;#/-CLaˣU8nWUx#ӈc=v#,"t> MuN)%y`c6=E9,:ۼػ/y0΄"|$0ez¢T*r=I0l8)б w͞A#/pUxYw#$ȏlgDH%KL+"VGNhӍ8_R3Bmݽb2F[BH#/yv+ôkXgśv"~L424(B #"#,wKޖ#/X _bER.̂e"ꋳRU#,LDI#e#e؉8%qs#,B3eh@ e /Ha\v4Z#,(X3W5kyt\2)KLSWːj>bo3da[&&7, VJMjfLX|rX[[#50CL+7K#/ɽۣ%QXY4¢8T($3@̷gnՠ PJP KqĆglxgAH#/L#,D4(4(эz@ۇs rmSM\ koLW0dQ;r9Mgd#$a5)2:rg'+vuh(?hӵޥvA=38LS.<9ݓM7%89-tыήѮ*߂w|9S 2 TsBA 9#g:0*7#3I4"mIEHy!U4"#,$\U(jf8:є`#/%Xp,6aXd2B"$ЗsQvSBkeɀ440|ȹ)9fDV1#,-SWHhM%6Y* n3,UD7u5z2ҋ]muqvًЩ\tC;$j2 *#,},@"F6,)odADàbPn2B5ai[*lN!@0N#$\a;H4WEIj`6%"&#Q3#$SmɦLb(3t!ʼnE@78E3*L"[!agE"b >yyQkkU@(צp~Xj;D!#/,"B@M@*#$P@=|Cr#$pF]#/qQ#,#/Gަ%1AhSpŀәe7z-b9t+vpw"f j }#$:`i~6)Wҡ$P)N ) Շ@{Ǯhmޚd<Upw%D.TÕ8Uj4tْUlEHdߓsZhvgssȌ8"s!#,TV(NASej+mμa4}o{VvXوȹF̡3Dw~ҧ3;Lk#=U, b!GI<}R1[ '|.^I#I1H5$ԲX՘jaT֘jMqqCFf[r7¿V^`s)p&M#$7ƛhmNCMԥь;;&iL 57M'w G˹,:"lœqx4km EF#,&F[-]\g{q!:#/M )kR9F!)A C"S܀#$PYl!V\92Cc󊛞C2!~-V VEsv*5ޭh`f-V_q`33[&2Eгc1#,") wY+gw4 )@v* tԁ"&R:X*zz/~q0cxTNAO#$###$@ U6[oVF]ϺС}-(+Qo{>⍞w{8VD݊/x JKk#$ ͚CGGc*#$hSk :A'0p!,#/ Y ̏QI.$6F_Gy\7z-4VQMQF%t#/6MrCݼ[+)PV%FBj'H~gp ?}FT!0O#?0ˬ:t @IbTQ6RcАbZld**R66!*6F_H( TmiJR$<#,ADA2Ka `##$ |)kY\]O#aMGK}UMsdScXZʱF#T- O^wZ6D@F/n|L]>_G:$Vb?;K!?7\`$ia<=ċv G,"jI^Ņ)Rti1B fL *`,F)K'rkp!x#,#,OEMJ?DjA#/RF"ThDړJMRvW昐6լ#$?#,(+и^TYY^ zz| F)Vxpbܵ*#2Pvfm>t' 6 ;m^--lA:=ӤyF?8} 0eXHX"?W#$]5#L")%I51R`Mo}^&Ehi4a#ow D)#/FLʺ?#$9T(LXQ袚&f KP>Qr/mߖ[[*EkWF-ڔ5F6XXcB}D᎞é͝JR!x3`~c )p4Œjxݤ| cK${dUiGlmbJ8,,"rɀQDyboofSE*[l+kkkmMoJ*-7feW\ݵsȳjƚ&wWw[I)hV@U#,V$Jt7-M֘EyqM-ԙe^W^yƣYFS-e]ۢ[.+s28xbBKa#,!4A?h׍Ufjbɸ}!a0RlT~ #,}4Kh$ AT.^I7brن8c~ ^M-r{u亁pBKr#=X'la%#/۟#E3ȗR`Tkq.$-ݯ|2 C2M#,UJ]/αo#,1}&f#/v}yD"8QH%poRcxkMN$aT&>9ڬr_#4#ZOuҤ1$׀H0l17!dI<}M@d)&3/S=MTY#IC"J݁',=ȮN0FW`)%0'ܠ#Z V%TR73rd5&G:0@٩@ǂhvf}=+zCbUQJ-%ՙ8k:9v'WwNsQ0 % 8w8')XH=nY)a1Ο 궦qBhR\ ;[y(dB7EN@ĮDqxjtNT#,"@bp1ɳyQQ).@A lޖE%2D x%\E #$Pl~&Nx?̪@HHد#z B+ݘ \5lDX-M(Px!h@A"e &x[@( 4#/7Mzz<5/2#,̴t x!)3 ɨn8ߺ[ -icn3[3?``ڕ5$C[#^in8i0c 6D"U;7W֚D˸v0&#$ѺhWR KeTK,>#,<ٔިvxqsL( ۍlr3QQBi4H]Ra&Otad L֡);Έ!6vuaᝲoES|;6UNg1'"a]’ !W.m khZyJ/_u^[mڽ>OH8/-oCuj.bagϼ~f7*QPxHワ$t%忲 OzB39>Iފ@זfT9P^]*zg_f%iSGo f0UOG\U[el)Jot6_ $[۲ƼmIocbmomFez$,#$0 Rn݂uB$9bCf᷅8I#,.DGeÁv⅘P%"(O`C?Fxňb;NJPDa%P)`EB%H B@I E;G>hiFBt}}wN2Yi ^!GF ,A;?>P}'A'ԐȤFO@P_sX[T< zbe Vt<.Z~WO|!aTrCΗuǙuЏw@{9aw8#=y#,_+?B[2D?_Ԝ~~Ë#iS+u#K7run^#$ʠ=vb:'RRZJqTE3oU,#,L;qӧm3,M/-80ay$fu^;WzvdKMqAu|31co##,~#ħ#,bI(P5֮D*{" .p!z +#<== +#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAlyFMaIACgkQSbTGfAUn\neqoHkA/9H11S84e1r7tI8QGS15XwN1bV3KR3guTtnIAt+TaILfTne69BNMZi5iA1\nkRzKt+QcCfjI3HsV5pLJ1X4y4FZEPx4kRtVkFBZYEskl0MNwRLQsKyVEB9T9/dhb\nGP36OFXirG0iGg0qu8IaUsSLfAk/dW++dYqfB1T704FC3LyN05ijMsu5m0hTrTQE\ncUbNbj5cm9gifkbECuaIJQEhvM5TCMR1r8yoWINtD9gLKwrbNAv9f4qdFpqooLRL\noLSPWUSzcq4jkGM4jHosNh3kza5CNoGPsL470tL4u54BgIJuDO8aWAO7pDKWJgn6\nswuVol2v7QvlEb4ejErhP65yYwHc+GbDcY4FDTzL24yt4p9QZils7kAtojIsOIv/\njnE4BpJA44ymB0SKIJbK8VJfT4O71U2EayXGvSpdbGRQt0wD96wDsexLXNPoT+m7\nLiQABIJrYYMZPd6TR6czolS1PjsIb7UTIw3sF16G17xuPM0zDxlULCB2D/eMCU+G\n3xoYj0EtNY/A+xpWQO9lOQ7LqJVTjSChDFDajguslY2vxD735xbGDbGCLtAPQRjm\nVFC88WTnGy/cZ3fFlfLdaCRhpu24y9WVlTYgchSBV83gkD9gnJTvJ4q17PKUvxis\nBMysLiz9gVqT8Ic6xbCThRQ5K+pqvn3isYxz/iEm2zGL1/V7PuU=\n=wpUd\n-----END PGP SIGNATURE-----\n diff --git a/wscript b/wscript new file mode 100644 index 00000000..ba7542af --- /dev/null +++ b/wscript @@ -0,0 +1,149 @@ +#! /usr/bin/env python +# encoding: utf-8 +# a1batross, mittorn, 2018 + +from __future__ import print_function +from waflib import Logs +import sys +import os +sys.path.append(os.path.realpath('scripts/waflib')) +import fwgslib + +VERSION = '2.4' +APPNAME = 'hlsdk-xash3d' +top = '.' + +def options(opt): + grp = opt.add_option_group('Common options') + + grp.add_option('-T', '--build-type', action='store', dest='BUILD_TYPE', default = None, + help = 'build type: debug, release or none(custom flags)') + + grp.add_option('-8', '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, + help = 'allow targetting 64-bit game dlls') + + grp.add_option('--enable-voicemgr', action = 'store_true', dest = 'VOICEMGR', default = False, + help = 'enable voice manager') + + grp.add_option('--enable-goldsrc-support', action = 'store_true', dest = 'GOLDSRC', default = False, + help = 'enable GoldSource engine support') + + opt.recurse('cl_dll dlls') + + opt.load('xcompile compiler_cxx compiler_c') + if sys.platform == 'win32': + opt.load('msvc msdev') + opt.load('reconfigure') + + +def configure(conf): + # Configuration + conf.env.GAMEDIR = 'valve' + conf.env.CLIENT_DIR = 'cl_dlls' + conf.env.SERVER_DIR = 'dlls' + conf.env.SERVER_NAME = 'hl' + conf.env.PREFIX = '' + + conf.load('reconfigure') + + conf.start_msg('Build type') + if conf.options.BUILD_TYPE == None: + conf.end_msg('not set', color='RED') + conf.fatal('Please set a build type, for example "-T release"') + elif not conf.options.BUILD_TYPE in ['fast', 'release', 'debug', 'nooptimize', 'sanitize', 'none']: + conf.end_msg(conf.options.BUILD_TYPE, color='RED') + conf.fatal('Invalid build type. Valid are "debug", "release" or "none"') + conf.end_msg(conf.options.BUILD_TYPE) + + # -march=native should not be used + if conf.options.BUILD_TYPE == 'fast': + Logs.warn('WARNING: \'fast\' build type should not be used in release builds') + + conf.env.VOICEMGR = conf.options.VOICEMGR + conf.env.GOLDSRC = conf.options.GOLDSRC + + # Force XP compability, all build targets should add + # subsystem=bld.env.MSVC_SUBSYSTEM + # TODO: wrapper around bld.stlib, bld.shlib and so on? + conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' + conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC + if sys.platform == 'win32': + conf.load('msvc msdev') + conf.load('xcompile compiler_c compiler_cxx') + + # print(conf.options.ALLOW64) + + conf.env.BIT32_MANDATORY = not conf.options.ALLOW64 + conf.env.BIT32_ALLOW64 = conf.options.ALLOW64 + conf.load('force_32bit') + + if conf.env.DEST_SIZEOF_VOID_P == 4: + Logs.info('NOTE: will build game dlls for 32-bit target') + else: + Logs.warn('WARNING: 64-bit game dlls may be unstable') + + linker_flags = { + 'common': { + 'msvc': ['/DEBUG'], # always create PDB, doesn't affect result binaries + 'gcc': ['-Wl,--no-undefined'] + }, + 'sanitize': { + 'gcc': ['-fsanitize=undefined', '-fsanitize=address'], + } + } + + compiler_c_cxx_flags = { + 'common': { + 'msvc': ['/D_USING_V110_SDK71_', '/Zi', '/FS'], + 'clang': ['-g', '-gdwarf-2'], + 'gcc': ['-g', '-Werror=implicit-function-declaration', '-fdiagnostics-color=always'] + }, + 'fast': { + 'msvc': ['/O2', '/Oy'], #todo: check /GL /LTCG + 'gcc': ['-Ofast', '-march=native', '-funsafe-math-optimizations', '-funsafe-loop-optimizations', '-fomit-frame-pointer'], + 'default': ['-O3'] + }, + 'release': { + 'msvc': ['/O2'], + 'default': ['-O3'] + }, + 'debug': { + 'msvc': ['/O1'], + 'gcc': ['-Og'], + 'default': ['-O1'] + }, + 'sanitize': { + 'msvc': ['/Od', '/RTC1'], + 'gcc': ['-Og', '-fsanitize=undefined', '-fsanitize=address'], + 'default': ['-O1'] + }, + 'nooptimize': { + 'msvc': ['/Od'], + 'default': ['-O0'] + } + } + + conf.env.append_unique('CFLAGS', fwgslib.get_flags_by_type( + compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + conf.env.append_unique('CXXFLAGS', fwgslib.get_flags_by_type( + compiler_c_cxx_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + conf.env.append_unique('LINKFLAGS', fwgslib.get_flags_by_type( + linker_flags, conf.options.BUILD_TYPE, conf.env.COMPILER_CC)) + + if conf.env.COMPILER_CC == 'msvc': + conf.env.append_unique('DEFINES', ['_CRT_SECURE_NO_WARNINGS','_CRT_NONSTDC_NO_DEPRECATE']) + else: + conf.env.append_unique('DEFINES', ['stricmp=strcasecmp','strnicmp=strncasecmp','_LINUX','LINUX','_snprintf=snprintf','_vsnprintf=vsnprintf']) + cflags = ['-fvisibility=hidden','-Wno-write-strings','-fno-exceptions'] + conf.env.append_unique('CFLAGS', cflags) + conf.env.append_unique('CXXFLAGS', cflags + ['-Wno-invalid-offsetof']) + + conf.env.append_unique('DEFINES', 'CLIENT_WEAPONS') + + conf.recurse('cl_dll dlls') + +def build(bld): + bld.recurse('cl_dll dlls') + + +