Re: [racket-users] Distributing application with run-time configuration

2018-09-13 Thread 'Paulo Matos' via Racket Users



On 12/09/2018 16:37, Matthew Flatt wrote:
> As you
> say, you could have a macro expand to the result of `(getenv "ARCH")`
> to pin it down at compile time, but you'll need to import that macro
> into two phases, since the right-hand side of
> `define-runtime-module-path-index` is implicitly used at two phases. 

Thanks for your thorough explanation of the issues. That clarified it
completely.

I have got it to working and realised that actually a compile time
configuration is more future-proof in case I end up developing a private
backend for a customer which I should ship to another customer by
mistake, therefore embedding a single backend per release is better.

I setup a file which does this to choose the backend at compile-time:
#lang racket/base
;;
---

(require (for-syntax racket/base))

(provide get-configured-backend-path)

;;
---
;; This module is used to move the compile time variable s10arch, read
from the environment
;; to a build-time variable using a macro.

(begin-for-syntax
  ;; An environment variable must be defined selecting the arch to use.
  (define s10arch (getenv "S10ARCH"))

  (unless s10arch
(raise-user-error 'driver "Please define S10ARCH environment
variable")))

(define-syntax (get-configured-backend-path stx)
  (syntax-case stx ()
[(_) (datum->syntax stx s10arch)]))


Then from another module I am doing:
(require s10/arch-choice)

(define-runtime-module-path-index backend-path
  (build-path (get-configured-backend-path) "name.rkt"))

However the problem here is that get-configured-backend-path does not
exist in level0 - only in level1.

I tried to provide it then like so:
(provide get-configured-backend-path
 (for-syntax get-configured-backend-path))

and then require it for syntax as well in the driver module, however
this generates the strange:
s10/arch-choice.rkt:7:21: provide: provided identifier is not defined or
required

How can get get-configured-backend-path to exist at both level which
seems to be what define-runtime-module-path-index requires?


-- 
Paulo Matos

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] Distributing application with run-time configuration

2018-09-12 Thread Matthew Flatt
Hi Paulo,

Some of your problems come from working with filesystem paths, instead
of module paths. That's a problem because the deployment filesystem
isn't necessarily anything like the build filesystem, and you have to
do work to manage the difference. The simplest solution is to write
things in terms of module paths, instead, using
`define-runtime-module-path-index`:

driver.rkt

#lang racket
(require racket/runtime-path)

(unless (getenv "ARCH")
  (raise-user-error 'driver "Please define ARCH with suitable path"))

(define-runtime-module-path-index arm-name "archs/arm/name.rkt")
(define-runtime-module-path-index intel-name "archs/intel/name.rkt")

(define arch-path (getenv "ARCH"))
(define arch-name-path
  (case arch-path
[("archs/arm") arm-name]
[("archs/intel") intel-name]
[else (error 'driver "unknown ~s" arch-path)]))

(module+ main

  (define arch-proc
(dynamic-require arch-name-path
 'get-arch))
  (printf "loading arch ~a~n" (arch-proc)))


To include only support for a module specified at build time:

driver.rkt

#lang racket
(require racket/runtime-path)

(define-runtime-module-path-index arch-name-path
  (case (getenv "ARCH")
[(#f)
 (raise-user-error 'driver "Please define ARCH with suitable path")]
[("archs/arm") "archs/arm/name.rkt"]
[("archs/intel") "archs/intel/name.rkt"]
[else (error 'driver "unknown")]))

(module+ main

  (define arch-proc
(dynamic-require arch-name-path
 'get-arch))
  (printf "loading arch ~a~n" (arch-proc)))


That second strategy still expects "ARCH" to be defined at run time,
and it only work if the definition is the same at both times. As you
say, you could have a macro expand to the result of `(getenv "ARCH")`
to pin it down at compile time, but you'll need to import that macro
into two phases, since the right-hand side of
`define-runtime-module-path-index` is implicitly used at two phases. Or
just use the first strategy if you don't mind including support for all
architectures in all builds.

With either of these "driver.rkt" variants, you should drop the
"archs/X/name.rkt" items from the `#:modules` argument of
`create-embedding-executable`, because using
`define-runtime-module-path-index` makes the dependency visible to the
embedder.

In fact, leaving them in `#:modules` creates a problem:

 #:modules (list (list #f '(file "driver.rkt"))
 ;; module name is 'name:
 (list #f '(file "archs/intel/name.rkt"))
 ;; module name is also 'name:
 (list #f '(file "archs/arm/name.rkt")))

Unfortunately, the embedder doesn't currently notice that you have
asked for two modules that will both be called 'name, so it effectively
picks one at random. That answers your question about prefixing: you
could use different prefixes to give the modules different names. But
using `define-runtime-module-path-index` saves you from this problem
(and, as it happens, some other problems).

Meanwhile, running `bin/prog` doesn't do anything, because your
`create-embedding-executable` uses `namespace-require` to start the
module "driver.rkt", but your program starts through the `main`
submodule of "driver.rkt". Unlike specifying a module path on the
command line of `racket`, giving a module path to `namespace-require`
does not automatically do anything with the `main` submodule.

So, you need to embed the `main` submodule and explicitly require it:

 ;; in "build.rkt":
 (create-embedding-executable
  "prog"
  #:verbose? #t
  #:modules (list (list #f "driver.rkt" '(main))) ; <<<
  #:configure-via-first-module? #t
  #:literal-expression
  (parameterize ([current-namespace (make-base-namespace)])
(compile
 `(begin
(namespace-require '(submod 'driver main) ; 
  #:cmdline (list "-U" "--"))

Finally, you ask about that `#:literal-expression` argument. Note that
the namespace is created at build time to get the argument for
`literal-expression`, not at run time for the generated executable.
That build-time namespace is used to compile the `namespace-require`
form to bytecode, so that no macro expansion and compilation needs to
happen at run time to get your program running. Compiled to using
`require`, using `namespace-require` compiles to lighter code with fewer
dependencies.

Matthew

At Wed, 12 Sep 2018 10:46:23 +0200, "'Paulo Matos' via Racket Users" wrote:
> Hi,
> 
> I am trying to create racket distribution for my application, except I
> am tripping on my first few steps - that of generating the binary.
> 
> First, the architecture of the application. There is a driver program
> that is configured with a backend at run-time (although one can argue
> this should be a compile time configuration - happy to change that).
> 
> The configuration that matches the driver to the 

[racket-users] Distributing application with run-time configuration

2018-09-12 Thread 'Paulo Matos' via Racket Users
Hi,

I am trying to create racket distribution for my application, except I
am tripping on my first few steps - that of generating the binary.

First, the architecture of the application. There is a driver program
that is configured with a backend at run-time (although one can argue
this should be a compile time configuration - happy to change that).

The configuration that matches the driver to the backend is an
environment relative path - which is then used to dynamic-require the
backend.

The tree looks like:

`|- info.rkt
 |- driver.rkt
 |- build.rkt
 |
 `-archs
   |- intel
   |  `- name.rkt
   `- arm
  `- name.rkt

info.rkt:
#lang info

(define collection 'multi)
;


driver.rkt:

#lang racket

(unless (getenv "ARCH")
  (raise-user-error 'driver "Please define ARCH with suitable path"))

(define arch-path (getenv "ARCH"))
(define arch-name-path
  (build-path arch-path "name.rkt"))

(module+ main

  (define arch-proc
(dynamic-require arch-name-path
 'get-arch))
  (printf "loading arch ~a~n" (arch-proc)))
;

archs/intel/name.rkt

#lang racket

(provide get-arch)

(define (get-arch)
  (if (fixnum? (expt 2 32))
  'x86_64
  'i386))
;

archs/arm/name.rkt


#lang racket

(provide get-arch)

;; Support for 32bits arm only
(define (get-arch)
  'arm32)


So far so good.
$ raco pkg install --link --auto
$ ARCH=archs/intel racket driver.rkt
loading arch x86_64
$ ARCH=archs/arm racket driver.rkt
loading arch arm32

Not, I try to create an executable using raco exe:
$ mkdir bin
$ raco exe ++lib archs/intel/name.rkt ++lib archs/arm/name.rkt -o
bin/r-prog driver.rkt
$ ARCH=archs/intel bin/r-prog
loading arch x86_64
$ cd bin
$ ARCH=archs/intel ./r-prog
open-input-file: cannot open module file
  module path: /home/pmatos/tmp/racket-distro/bin/archs/intel/name.rkt

This doesn't work but it's understandable. However, if only I could
replicate this behaviour in a build script using
create-embedding-executable:

build.rkt:

#lang racket

(require compiler/embed)

(create-embedding-executable
 "prog"
 #:verbose? #t
 #:modules (list (list #f '(file "driver.rkt"))
 (list #f '(file "archs/intel/name.rkt"))
 (list #f '(file "archs/arm/name.rkt")))
 #:configure-via-first-module? #t
 #:literal-expression
 (parameterize ([current-namespace (make-base-namespace)])
   (compile
`(begin
   (namespace-require ''driver
 #:cmdline (list "-U" "--"))

(make-directory* "bin")
(delete-directory/files "bin/prog" #:must-exist? #f)
(copy-directory/files "prog" "bin/prog")
(delete-directory/files "prog")


$ ARCH=archs/intel bin/prog

This finishes successfully and returns nothing which makes me think that
nothing is being executed. This is slightly different from the error I
initially had but I am quite lost with the inner workings of
create-embedding-executable and am using examples online to guide me
which are few and don't really deal exactly with the kind of program I
am trying to compile.

At this point I have a few questions:
1. It is still not clear what the first element in each sublist in
#:modules exactly represents. I understand that it is related to a
module prefix but I don't understand how any value besides #f is useful.
The example in the docs has #f so I used that. Can someone clarify the
usefulness of having a prefix or #t?
2. All examples I have seen create a new namespace in literal-expression
and then use namespace-require. Why can't I just do (require ...) in the
literal-expression?
3. I will have some problems once I got the basics to work with paths. I
assume the solution is define-runtime-path.

I tried
(define-runtime-path arch-path (getenv "ARCH"))

but this will, I think, evaluate the value at both compile time and
runtime which means I will potentially get two different values for
arch-path depending if I am compiling or executing. This also means I am
moving the backend configuration to compile time which is ok but how can
I get the variable at compile time, and then cache that for use at runtime?

I tried
(define-for-syntax foo (getenv "FOO"))
(define-runtime-path p (build-path foo "name.rkt"))

but this will tell me foo is not available at runtime, so I sort of need
to move the compile-time value to runtime. Maybe I am over-complicating
this though so I am happy to hear some comments on this.

Kind regards,

-- 
Paulo Matos

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.