#lang scheme/base
(require scheme/contract
scheme/port
scheme/class
scheme/list
scheme/string
scheme/runtime-path
net/url
net/uri-codec
"../../program-resources.ss"
"../../collects/moby/runtime/permission-struct.ss"
"../../compiler/pinfo.ss"
(only-in "../../compiler/helpers.ss" program?)
(prefix-in javascript: "../../compiler/beginner-to-javascript.ss")
(only-in "../../compiler/helpers.ss" identifier->munged-java-identifier)
"../../template.ss"
"../../resource.ss"
"../../program-resources.ss"
"../../preferences.ss")
(define-runtime-path phonegap-path "../../../support/phonegap-fork/android-1.5/assets/phonegap.js")
(define-runtime-path javascript-support-path "../../../support/js")
(define-runtime-path javascript-main-template "../../../support/js/main.js.template")
(define current-server-url
(lambda ()
(get-remote-apk-builder)))
(define (build-android-package name program/resources)
(let ([data (encode-parameters-in-data name program/resources)])
(with-handlers ([exn:fail:network?
(lambda (exn)
(handle-network-failure exn))])
(log-debug (format "Sending ~s ~s" (current-server-url) data))
(let* ([ip (post-impure-port (string->url (current-server-url))
data)]
[headers (purify-port ip)]
[status-code (get-status-code headers)])
(cond
[(= status-code 200)
(port->bytes ip)]
[else
(raise (make-exn:fail (bytes->string/utf-8 (port->bytes ip))
(current-continuation-marks)))])))))
(define (handle-network-failure exn)
(raise (make-exn:fail
(string-append "We are unable to build your Android package; the web service appears to be offline.\n"
"Please contact the Moby developers; in your response, include the following:\n"
(exn-message exn))
(current-continuation-marks))))
(define (get-status-code response-headers)
(log-debug (format "Response headers are ~s~n" response-headers))
(string->number (second (regexp-match #px"^[^\\s]+\\s([^\\s]+)" response-headers))))
(define (unique-strings strs)
(define ht (make-hash))
(for-each (lambda (s) (hash-set! ht s #t)) strs)
(hash-map ht (lambda (k v) k)))
(define (encode-parameters-in-data name program/resources)
(let*-values ([(program)
(program/resources-program program/resources)]
[(compiled-program)
(do-compilation program)]
[(defns pinfo)
(values (javascript:compiled-program-defns compiled-program)
(javascript:compiled-program-pinfo compiled-program))]
[(android-permissions)
(unique-strings (apply append (map permission->android-permissions (pinfo-permissions pinfo))))])
(string->bytes/utf-8
(alist->form-urlencoded
(append (list
(cons 't "moby2")
(cons 'n name))
(map (lambda (permission)
(cons 'ps permission))
android-permissions)
(list
(cons 'r (format "~s"
(list 'resource
"main.js"
(string->bytes/utf-8
(compiled-program->main.js compiled-program))))))
(map (lambda (a-resource)
(cons 'r (format "~s"
(list 'resource
(format "~a" (send a-resource get-name))
(send a-resource get-bytes)))))
(append (program/resources-resources program/resources)
(collect-directory-resources
(build-path javascript-support-path "runtime/compat") "runtime/compat")
(list (path->resource
(build-path javascript-support-path "index.html")
"index.html")
(path->resource
(build-path javascript-support-path "runtime/compressed-runtime.js")
"runtime/compressed-runtime.js")
(path->resource phonegap-path "runtime/phonegap.js")))))))))
(define (path->resource a-path name)
(new named-bytes-resource%
[name name]
[bytes (call-with-input-file* a-path port->bytes)]))
(define (do-compilation program)
(javascript:program->compiled-program/pinfo program (get-base-pinfo 'moby)))
(define (get-permission-js-array perms)
(string-append "["
(string-join (map (lambda (x)
(format "plt.Kernel.invokeModule('moby/runtime/permission-struct').EXPORTS['string->permission'](~s)" (permission->string x)))
perms)
", ")
"]"))
(define (compiled-program->main.js compiled-program)
(let*-values ([(defns pinfo)
(values (javascript:compiled-program-defns compiled-program)
(javascript:compiled-program-pinfo compiled-program))]
[(output-port) (open-output-string)]
[(mappings)
(build-mappings
(PROGRAM-DEFINITIONS defns)
(IMAGES (string-append "[" "]"))
(PROGRAM-TOPLEVEL-EXPRESSIONS
(javascript:compiled-program-toplevel-exprs
compiled-program))
(PERMISSIONS (get-permission-js-array (pinfo-permissions pinfo))))])
(fill-template-port (open-input-file javascript-main-template)
output-port
mappings)
(get-output-string output-port)))
(provide/contract [build-android-package
(string? program/resources? . -> . bytes?)])