From 27f8b6b89e431ba7c1c98231ff297022d928e2ef Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Sat, 17 Nov 2018 23:13:44 +0100 Subject: [PATCH] Replace titaniumenv with new implementation --- .../mobile/titaniumenv/build-app.nix | 319 ++++++++---------- .../mobile/titaniumenv/default.nix | 24 +- .../mobile/titaniumenv/examples/default.nix | 91 ----- .../examples/emulate-kitchensink/default.nix | 10 - .../examples/kitchensink/default.nix | 44 --- .../examples/kitchensink/generatekeystore.sh | 10 - .../titaniumenv/examples/kitchensink/keystore | Bin 1315 -> 0 bytes .../examples/simulate-kitchensink/default.nix | 7 - .../mobile/titaniumenv/titaniumsdk-6.3.nix | 43 --- .../mobile/titaniumenv/titaniumsdk-7.1.nix | 12 +- pkgs/top-level/all-packages.nix | 4 +- 11 files changed, 167 insertions(+), 397 deletions(-) delete mode 100644 pkgs/development/mobile/titaniumenv/examples/default.nix delete mode 100644 pkgs/development/mobile/titaniumenv/examples/emulate-kitchensink/default.nix delete mode 100644 pkgs/development/mobile/titaniumenv/examples/kitchensink/default.nix delete mode 100755 pkgs/development/mobile/titaniumenv/examples/kitchensink/generatekeystore.sh delete mode 100644 pkgs/development/mobile/titaniumenv/examples/kitchensink/keystore delete mode 100644 pkgs/development/mobile/titaniumenv/examples/simulate-kitchensink/default.nix delete mode 100644 pkgs/development/mobile/titaniumenv/titaniumsdk-6.3.nix diff --git a/pkgs/development/mobile/titaniumenv/build-app.nix b/pkgs/development/mobile/titaniumenv/build-app.nix index 0250e5bf2e2..472f85fbc45 100644 --- a/pkgs/development/mobile/titaniumenv/build-app.nix +++ b/pkgs/development/mobile/titaniumenv/build-app.nix @@ -1,200 +1,181 @@ -{stdenv, androidsdk, titaniumsdk, titanium, alloy, xcodewrapper, jdk, python, nodejs, which, file, xcodeBaseDir}: -{ name, src, preBuild ? "", target, androidPlatformVersions ? [ "25" ], androidAbiVersions ? [ "armeabi" "armeabi-v7a" ], tiVersion ? null +{stdenv, composeAndroidPackages, composeXcodeWrapper, titaniumsdk, titanium, alloy, jdk, python, nodejs, which, file}: +{ name, src, preBuild ? "", target, tiVersion ? null , release ? false, androidKeyStore ? null, androidKeyAlias ? null, androidKeyStorePassword ? null -, iosMobileProvisioningProfile ? null, iosCertificateName ? null, iosCertificate ? null, iosCertificatePassword ? null, iosVersion ? "11.2" -, enableWirelessDistribution ? false, iosBuildStore ? false, installURL ? null -}: +, iosMobileProvisioningProfile ? null, iosCertificateName ? null, iosCertificate ? null, iosCertificatePassword ? null, iosVersion ? "11.3", iosBuildStore ? false +, enableWirelessDistribution ? false, installURL ? null +, xcodeBaseDir ? "/Applications/Xcode.app" +, androidsdkArgs ? {} +, xcodewrapperArgs ? {} +, ... +}@args: assert (release && target == "android") -> androidKeyStore != null && androidKeyAlias != null && androidKeyStorePassword != null; assert (release && target == "iphone") -> iosMobileProvisioningProfile != null && iosCertificateName != null && iosCertificate != null && iosCertificatePassword != null; assert enableWirelessDistribution -> installURL != null; let - androidsdkComposition = androidsdk { - platformVersions = androidPlatformVersions; - abiVersions = androidAbiVersions; - useGoogleAPIs = true; - }; + realAndroidsdkArgs = { + platformVersions = [ "26" ]; + } // androidsdkArgs; + + androidsdk = (composeAndroidPackages realAndroidsdkArgs).androidsdk; + + realXcodewrapperArgs = { + inherit xcodeBaseDir; + } // xcodewrapperArgs; + + xcodewrapper = composeXcodeWrapper xcodewrapperArgs; deleteKeychain = '' - security default-keychain -s login.keychain - security delete-keychain $keychainName - rm -f $HOME/lock-keychain + if [ -f $HOME/lock-keychain ] + then + security default-keychain -s login.keychain + security delete-keychain $keychainName + rm -f $HOME/lock-keychain + fi ''; -in -stdenv.mkDerivation { - name = stdenv.lib.replaceChars [" "] [""] name; - inherit src; - buildInputs = [ nodejs titanium alloy jdk python which file ] ++ stdenv.lib.optional (stdenv.hostPlatform.system == "x86_64-darwin") xcodewrapper; - + extraArgs = removeAttrs args [ "name" "preRebuild" "androidsdkArgs" "xcodewrapperArgs" ]; +in +stdenv.mkDerivation ({ + name = stdenv.lib.replaceChars [" "] [""] name; + + buildInputs = [ nodejs titanium alloy python which file jdk ] + ++ stdenv.lib.optional (target == "iphone") xcodewrapper; + buildPhase = '' ${preBuild} - export HOME=$TMPDIR - + export HOME=${if target == "iphone" then "/Users/$(whoami)" else "$TMPDIR"} + ${stdenv.lib.optionalString (tiVersion != null) '' # Replace titanium version by the provided one sed -i -e "s|[0-9a-zA-Z\.]*|${tiVersion}|" tiapp.xml ''} - + # Simulate a login mkdir -p $HOME/.titanium cat > $HOME/.titanium/auth_session.json < $TMPDIR/config.json titanium --config-file $TMPDIR/config.json --no-colors config sdk.defaultInstallLocation ${titaniumsdk} titanium --config-file $TMPDIR/config.json --no-colors config paths.modules ${titaniumsdk} - + mkdir -p $out - - ${if target == "android" then - '' - titanium config --config-file $TMPDIR/config.json --no-colors android.sdkPath ${androidsdkComposition}/libexec - export PATH=$(echo ${androidsdkComposition}/libexec/tools):$(echo ${androidsdkComposition}/libexec/build-tools/android-*):$PATH - export GRADLE_USER_HOME=$TMPDIR/gradle - - ${if release then - '' - ${stdenv.lib.optionalString stdenv.isDarwin '' - # Signing the app does not work with OpenJDK on macOS, use host SDK instead - export JAVA_HOME="$(/usr/libexec/java_home -v 1.8)" - ''} - titanium build --config-file $TMPDIR/config.json --no-colors --force --platform android --target dist-playstore --keystore ${androidKeyStore} --alias ${androidKeyAlias} --store-password ${androidKeyStorePassword} --output-dir $out - '' - else - ''titanium build --config-file $TMPDIR/config.json --no-colors --force --platform android --target emulator --build-only -B foo --output $out''} - '' - else if target == "iphone" then - '' - ${if release then - '' - export HOME=/Users/$(whoami) - export keychainName=$(basename $out) - - # Create a keychain with the component hash name (should always be unique) - security create-keychain -p "" $keychainName - security default-keychain -s $keychainName - security unlock-keychain -p "" $keychainName - security import ${iosCertificate} -k $keychainName -P "${iosCertificatePassword}" -A - security set-key-partition-list -S apple-tool:,apple: -s -k "" $keychainName - provisioningId=$(grep UUID -A1 -a ${iosMobileProvisioningProfile} | grep -o "[-A-Za-z0-9]\{36\}") - - # Ensure that the requested provisioning profile can be found - - if [ ! -f "$HOME/Library/MobileDevice/Provisioning Profiles/$provisioningId.mobileprovision" ] - then - mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" - cp ${iosMobileProvisioningProfile} "$HOME/Library/MobileDevice/Provisioning Profiles/$provisioningId.mobileprovision" - fi - - # Simulate a login - mkdir -p $HOME/.titanium - cat > $HOME/.titanium/auth_session.json < $out/nix-support/hydra-build-products - - ${stdenv.lib.optionalString enableWirelessDistribution '' - appname=$(basename $out/*.ipa .ipa) - bundleId=$(grep '[a-zA-Z0-9.]*' tiapp.xml | sed -e 's|||' -e 's|||' -e 's/ //g') - version=$(grep '[a-zA-Z0-9.]*' tiapp.xml | sed -e 's|||' -e 's|||' -e 's/ //g') - - sed -e "s|@INSTALL_URL@|${installURL}?bundleId=$bundleId\&version=$version\&title=$appname|" ${../xcodeenv/install.html.template} > "$out/$appname.html" - echo "doc install \"$out/$appname.html\"" >> $out/nix-support/hydra-build-products - ''} - '' - else if target == "iphone" then "" - else throw "Target: ${target} is not supported!"} - ${if target == "android" then '' - mkdir -p $out/nix-support - echo "file binary-dist \"$(ls $out/*.apk)\"" > $out/nix-support/hydra-build-products - '' else ""} + titanium config --config-file $TMPDIR/config.json --no-colors android.sdkPath ${androidsdk}/libexec/android-sdk + + export PATH=${androidsdk}/libexec/android-sdk/tools:$(echo ${androidsdk}/libexec/android-sdk/build-tools/android-*):$PATH + export GRADLE_USER_HOME=$TMPDIR/gradle + + ${if release then '' + ${stdenv.lib.optionalString stdenv.isDarwin '' + # Signing the app does not work with OpenJDK on macOS, use host SDK instead + export JAVA_HOME="$(/usr/libexec/java_home -v 1.8)" + ''} + titanium build --config-file $TMPDIR/config.json --no-colors --force --platform android --target dist-playstore --keystore ${androidKeyStore} --alias "${androidKeyAlias}" --store-password "${androidKeyStorePassword}" --output-dir $out + '' else '' + titanium build --config-file $TMPDIR/config.json --no-colors --force --platform android --target emulator --build-only -B foo --output $out + ''} + '' + else if target == "iphone" then '' + # Configure the path to Xcode + titanium --config-file $TMPDIR/config.json --no-colors config paths.xcode ${xcodeBaseDir} + + # Link the modules folder + if [ ! -e modules ] + then + ln -s ${titaniumsdk}/modules modules + createdModulesSymlink=1 + fi + + ${if release then '' + # Create a keychain with the component hash name (should always be unique) + export keychainName=$(basename $out) + + security create-keychain -p "" $keychainName + security default-keychain -s $keychainName + security unlock-keychain -p "" $keychainName + security import ${iosCertificate} -k $keychainName -P "${iosCertificatePassword}" -A + security set-key-partition-list -S apple-tool:,apple: -s -k "" $keychainName + provisioningId=$(grep UUID -A1 -a ${iosMobileProvisioningProfile} | grep -o "[-A-Za-z0-9]\{36\}") + + # Ensure that the requested provisioning profile can be found + + if [ ! -f "$HOME/Library/MobileDevice/Provisioning Profiles/$provisioningId.mobileprovision" ] + then + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + cp ${iosMobileProvisioningProfile} "$HOME/Library/MobileDevice/Provisioning Profiles/$provisioningId.mobileprovision" + fi + + # Take precautions to prevent concurrent builds blocking the keychain + while [ -f $HOME/lock-keychain ] + do + echo "Keychain locked, waiting for a couple of seconds, or remove $HOME/lock-keychain to unblock..." + sleep 3 + done + + touch $HOME/lock-keychain + + security default-keychain -s $keychainName + + # Do the actual build + titanium build --config-file $TMPDIR/config.json --force --no-colors --platform ios --target ${if iosBuildStore then "dist-appstore" else "dist-adhoc"} --pp-uuid $provisioningId --distribution-name "${iosCertificateName}" --keychain $HOME/Library/Keychains/$keychainName-db --device-family universal --ios-version ${iosVersion} --output-dir $out + + # Remove our generated keychain + ${deleteKeychain} + '' else '' + # Copy all sources to the output store directory. + # Why? Debug application include *.js files, which are symlinked into their + # sources. If they are not copied, we have dangling references to the + # temp folder. + + cp -av * $out + cd $out + + # Execute the build + titanium build --config-file $TMPDIR/config.json --force --no-colors --platform ios --target simulator --build-only --device-family universal --ios-version ${iosVersion} --output-dir $out + + # Remove the modules symlink + if [ "$createdModulesSymlink" = "1" ] + then + rm $out/modules + fi + ''} + '' else throw "Target: ${target} is not supported!"} ''; - + + installPhase = '' + ${if target == "android" then '' + ${if release then "" + else '' + cp "$(ls build/android/bin/*.apk | grep -v '\-unsigned.apk')" $out + ''} + + mkdir -p $out/nix-support + echo "file binary-dist \"$(ls $out/*.apk)\"" > $out/nix-support/hydra-build-products + '' + else if target == "iphone" then + if release then '' + mkdir -p $out/nix-support + echo "file binary-dist \"$(echo $out/*.ipa)\"" > $out/nix-support/hydra-build-products + + ${stdenv.lib.optionalString enableWirelessDistribution '' + appname="$(basename "$out/*.ipa" .ipa)" + bundleId=$(grep '[a-zA-Z0-9.]*' tiapp.xml | sed -e 's|||' -e 's|||' -e 's/ //g') + version=$(grep '[a-zA-Z0-9.]*' tiapp.xml | sed -e 's|||' -e 's|||' -e 's/ //g') + + sed -e "s|@INSTALL_URL@|${installURL}?bundleId=$bundleId\&version=$version\&title=$appname|" ${../xcodeenv/install.html.template} > "$out/$appname.html" + echo "doc install \"$out/$appname.html\"" >> $out/nix-support/hydra-build-products + ''} + '' + else "" + else throw "Target: ${target} is not supported!"} + ''; + failureHook = stdenv.lib.optionalString (release && target == "iphone") deleteKeychain; -} +} // extraArgs) diff --git a/pkgs/development/mobile/titaniumenv/default.nix b/pkgs/development/mobile/titaniumenv/default.nix index f411a8d61fd..e9f6344680b 100644 --- a/pkgs/development/mobile/titaniumenv/default.nix +++ b/pkgs/development/mobile/titaniumenv/default.nix @@ -1,27 +1,19 @@ -{pkgs, xcodeVersion ? "9.2", xcodeBaseDir ? "/Applications/Xcode.app", tiVersion ? "7.1.0.GA"}: +{pkgs, pkgs_i686, androidenv, xcodeenv, tiVersion ? "7.1.0.GA"}: rec { - androidenv = pkgs.androidenv; - - xcodeenv = if pkgs.stdenv.hostPlatform.system == "x86_64-darwin" then pkgs.xcodeenv.override { - version = xcodeVersion; - inherit xcodeBaseDir; - } else null; - titaniumsdk = let - titaniumSdkFile = if tiVersion == "6.3.1.GA" then ./titaniumsdk-6.3.nix - else if tiVersion == "7.1.0.GA" then ./titaniumsdk-7.1.nix + titaniumSdkFile = if tiVersion == "7.1.0.GA" then ./titaniumsdk-7.1.nix else throw "Titanium version not supported: "+tiVersion; in import titaniumSdkFile { - inherit (pkgs) stdenv fetchurl unzip makeWrapper python jdk; + inherit (pkgs) stdenv fetchurl unzip makeWrapper; }; - + buildApp = import ./build-app.nix { inherit (pkgs) stdenv python which file jdk nodejs; - inherit (pkgs.nodePackages_6_x) alloy titanium; - inherit (androidenv) androidsdk; - inherit (xcodeenv) xcodewrapper; - inherit titaniumsdk xcodeBaseDir; + inherit (pkgs.nodePackages_8_x) alloy titanium; + inherit (androidenv) composeAndroidPackages; + inherit (xcodeenv) composeXcodeWrapper; + inherit titaniumsdk; }; } diff --git a/pkgs/development/mobile/titaniumenv/examples/default.nix b/pkgs/development/mobile/titaniumenv/examples/default.nix deleted file mode 100644 index 78f91dd39fb..00000000000 --- a/pkgs/development/mobile/titaniumenv/examples/default.nix +++ /dev/null @@ -1,91 +0,0 @@ -{ nixpkgs ? -, systems ? [ "x86_64-linux" "x86_64-darwin" ] -, xcodeVersion ? "9.2" -, xcodeBaseDir ? "/Applications/Xcode.app" -, tiVersion ? "7.1.0.GA" -, rename ? false -, newBundleId ? "com.example.kitchensink", iosMobileProvisioningProfile ? null, iosCertificate ? null, iosCertificateName ? "Example", iosCertificatePassword ? "", iosVersion ? "11.2" -, enableWirelessDistribution ? false, installURL ? null -}: - -let - pkgs = import nixpkgs {}; -in -rec { - kitchensink_android_debug = pkgs.lib.genAttrs systems (system: - let - pkgs = import nixpkgs { inherit system; }; - in - import ./kitchensink { - inherit (pkgs) fetchgit; - titaniumenv = pkgs.titaniumenv.override { inherit xcodeVersion xcodeBaseDir tiVersion; }; - inherit tiVersion; - target = "android"; - }); - - kitchensink_android_release = pkgs.lib.genAttrs systems (system: - let - pkgs = import nixpkgs { inherit system; }; - in - import ./kitchensink { - inherit (pkgs) fetchgit; - titaniumenv = pkgs.titaniumenv.override { inherit xcodeVersion xcodeBaseDir tiVersion; }; - inherit tiVersion; - target = "android"; - release = true; - }); - - emulate_kitchensink_debug = pkgs.lib.genAttrs systems (system: - let - pkgs = import nixpkgs { inherit system; }; - in - import ./emulate-kitchensink { - inherit (pkgs) androidenv; - kitchensink = builtins.getAttr system kitchensink_android_debug; - }); - - emulate_kitchensink_release = pkgs.lib.genAttrs systems (system: - let - pkgs = import nixpkgs { inherit system; }; - in - import ./emulate-kitchensink { - inherit (pkgs) androidenv; - kitchensink = builtins.getAttr system kitchensink_android_release; - }); - -} // (if builtins.elem "x86_64-darwin" systems then - let - pkgs = import nixpkgs { system = "x86_64-darwin"; }; - in - rec { - kitchensink_ios_development = import ./kitchensink { - inherit (pkgs) fetchgit; - titaniumenv = pkgs.titaniumenv.override { inherit xcodeVersion xcodeBaseDir tiVersion; }; - inherit tiVersion iosVersion; - target = "iphone"; - }; - - simulate_kitchensink = import ./simulate-kitchensink { - inherit (pkgs) stdenv; - xcodeenv = pkgs.xcodeenv.override { version = xcodeVersion; inherit xcodeBaseDir; }; - kitchensink = kitchensink_ios_development; - bundleId = if rename then newBundleId else "com.appcelerator.kitchensink"; - }; -} else {}) // (if rename then - let - pkgs = import nixpkgs { system = "x86_64-darwin"; }; - in - { - kitchensink_ipa = import ./kitchensink { - inherit (pkgs) stdenv fetchgit; - titaniumenv = pkgs.titaniumenv.override { inherit xcodeVersion xcodeBaseDir tiVersion; }; - target = "iphone"; - inherit tiVersion; - release = true; - rename = true; - inherit newBundleId iosMobileProvisioningProfile iosCertificate iosCertificateName iosCertificatePassword iosVersion; - inherit enableWirelessDistribution installURL; - }; - } - -else {}) diff --git a/pkgs/development/mobile/titaniumenv/examples/emulate-kitchensink/default.nix b/pkgs/development/mobile/titaniumenv/examples/emulate-kitchensink/default.nix deleted file mode 100644 index cad4503dc96..00000000000 --- a/pkgs/development/mobile/titaniumenv/examples/emulate-kitchensink/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{androidenv, kitchensink}: - -androidenv.emulateApp { - name = "emulate-${kitchensink.name}"; - app = kitchensink; - platformVersion = "16"; - useGoogleAPIs = true; - package = "com.appcelerator.kitchensink"; - activity = ".KitchensinkActivity"; -} diff --git a/pkgs/development/mobile/titaniumenv/examples/kitchensink/default.nix b/pkgs/development/mobile/titaniumenv/examples/kitchensink/default.nix deleted file mode 100644 index 70ab1b168c4..00000000000 --- a/pkgs/development/mobile/titaniumenv/examples/kitchensink/default.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ titaniumenv, fetchgit, target, androidPlatformVersions ? [ "25" "26" ], tiVersion ? "7.1.0.GA", release ? false -, rename ? false, stdenv ? null, newBundleId ? null, iosMobileProvisioningProfile ? null, iosCertificate ? null, iosCertificateName ? null, iosCertificatePassword ? null, iosVersion ? "11.2" -, enableWirelessDistribution ? false, installURL ? null -}: - -assert rename -> (stdenv != null && newBundleId != null && iosMobileProvisioningProfile != null && iosCertificate != null && iosCertificateName != null && iosCertificatePassword != null); - -let - src = fetchgit { - url = https://github.com/appcelerator/kitchensink-v2.git; - rev = "94364df2ef60a80bd354a4273e3cb5f4c5185537"; - sha256 = "0q4gzidpsq401frkngy4yk5kqvm8dz00ls74bw3fnpvg4714d6gf"; - }; - - # Rename the bundle id to something else - renamedSrc = stdenv.mkDerivation { - name = "KitchenSink-renamedsrc"; - inherit src; - buildPhase = '' - sed -i -e "s|com.appcelerator.kitchensink|${newBundleId}|" tiapp.xml - ''; - installPhase = '' - mkdir -p $out - mv * $out - ''; - }; -in -titaniumenv.buildApp { - name = "KitchenSink-${target}-${if release then "release" else "debug"}"; - src = if rename then renamedSrc else src; - preBuild = '' - sed -i -e "s|23|25|" tiapp.xml - ''; # Raise minimum android SDK from 23 to 25 - inherit tiVersion; - - inherit target androidPlatformVersions release; - - androidKeyStore = ./keystore; - androidKeyAlias = "myfirstapp"; - androidKeyStorePassword = "mykeystore"; - - inherit iosMobileProvisioningProfile iosCertificate iosCertificateName iosCertificatePassword iosVersion; - inherit enableWirelessDistribution installURL; -} diff --git a/pkgs/development/mobile/titaniumenv/examples/kitchensink/generatekeystore.sh b/pkgs/development/mobile/titaniumenv/examples/kitchensink/generatekeystore.sh deleted file mode 100755 index 57451e8a507..00000000000 --- a/pkgs/development/mobile/titaniumenv/examples/kitchensink/generatekeystore.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -e - -( echo "John Doe" - echo "My Company" - echo "My Organization" - echo "My City" - echo "My State" - echo "US" - echo "yes" -) | keytool --genkeypair --alias myfirstapp --keystore ./keystore --storepass mykeystore diff --git a/pkgs/development/mobile/titaniumenv/examples/kitchensink/keystore b/pkgs/development/mobile/titaniumenv/examples/kitchensink/keystore deleted file mode 100644 index ee0a9c7989badede112afd57b011d1d53206425b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1315 zcmezO_TO6u1_mYu1_nkj=E|*1%PcA`Nh~N}U|@9aGkPZGYU91qmZWr0|J9S~S;fLt;eb*}E*fgAO#JFsH{hPU6_$9yAt4!w?n*YkYSi*T6 zw;il`$8Y7fd`HV+wlh99n|Y^*M4mW5+b92D1AmyY=P8+W^CxKLEPu!~h2N*OTKu<} zUY^)Hh%z2BF z4()KBJS9ikf1j6cd*b2^|86ejyq&LJ_0x}GZer`fWu?b&P8HnU^lo1w|HH%CxgyE8 zOpk2YXumtc>Fzq4`EnV%7scQ9;t*n7z+`Wo=J`bDSzfBkmQuMdYqfLg5}WzfhVFdP z(A_8fZ^N>yHCDCm6SEnoE&BSc!^W}UOv}QnM?U|W-nHr7iDfx?4}LH24qP?U{r>r5 zTZIL7&2zfUEZnvFWslSXSM%51&aNL~rM$d3e{uY@WHp$2x3a-DzzZBztPy&q29^vA z%$^2K%&rDa%*+dznHZUvSjsDm*$lYZ*tOa`&RMW9voaWz7;+nMvN4CUun9AT1{(?* z@PjxU!W_Pp3c)3bC8>r227DkPc42nkN(JZ4l1f7f12K>YmoUF?rGkG^dSYH?RbokI zex9MQfgnhRTbK)^Hb1u@F|QJ4CbKYySAIsGf=hm?ft)z6p^1T^p^1TkiIJg26qsuQ z=MIponizK&G%@PHf``$diBX=Zv610#eQ={}sgP#SeXlusZ=O8=Cb&Jw=DiEUVLAJT zZGmc;uCWeEfxjbddn11BKl*i-!MD@1LvyY-S~tj=+HZ6MH!u=sa<}1i)-ybe$y*AtS-swH7W6XAh@2s4%e)lh{ zS4}^|eYkDeo6-X#!~1q!iS&O~$S%;>u*FlXQ2B+AhM4g7^PEOKCzd_55v%GBsp-{t zuy~!E_U2`!6Z00=bN^B_P5Ssdsftm>`plA#`&I-_=v>i!|6$bA`D~NA)8ZPcLse54 zeXReT_tau;Y-Iqs7(uh-46c)_@u<@wz#<3;RC`VJa9 zaPiy6Gxf?`kouQ(zT^J&g|)AI&;RGzed%8x->>*dMQ5g*zx2t;@Y+YMvNu0;_gg-8 z`uIxhT;Okq1~uDVGn-beI{)(Uz2bGpuG(yhc