bug#22937: guix package fails when --substitute-urls specifies an HTTPS endpoint

2016-03-07 Thread Chris Marusich
Hi,

I've noticed that "guix package" fails when I specify an HTTPS endpoint
for --substitute-urls. Is that expected behavior?

I recently set up a caching proxy for hydra.gnu.org. The endpoint's DNS
name is hydra-mirror.marusich.info. It's configured to accept both HTTP
and HTTPS requests. When it receives an HTTPS request, it proxies the
request (assuming it's a cache miss) to hydra.gnu.org over HTTP. I've
configured it this way because my understanding is that hydra.gnu.org is
currently only available via HTTP.

Commands like wget, curl, and even "guix download" work fine with the
mirror, even over HTTPS. For example, the following command succeeds:

guix download 
https://hydra-mirror.marusich.info/nar/8kvb2k0n1jqjd5sa7g6qj5qllq0ckcya-linux-libre-4.4

However, when I try to install a package with "guix package" using the
endpoint, it fails with a backtrace like the following (this command was
invoked using the "guix package" from commit
7b3f2682de38a8e39f052705795ec85fcdfc8a96):

--8<---cut here---start->8---
$ ./pre-inst-env guix package 
--substitute-urls="https://hydra-mirror.marusich.info"; -i graphviz
substitute: Backtrace:
substitute: In ice-9/boot-9.scm:
substitute:   63: 19 [call-with-prompt prompt0 ...]
substitute: In ice-9/eval.scm:
substitute:  432: 18 [eval # #]
substitute: In ice-9/boot-9.scm:
substitute: 2401: 17 [save-module-excursion #]
substitute: 4050: 16 [#]
substitute: 1724: 15 [%start-stack load-stack ...]
substitute: 1729: 14 [#]
substitute: In unknown file:
substitute:?: 13 [primitive-load 
"/gnu/store/3lg5c1nidbj0kjdz5b63hn3vp29kzf0s-guix-0.9.0.c3f29bc/bin/.guix-real"]
substitute: In guix/ui.scm:
substitute: 1175: 12 [run-guix-command substitute "--query"]
substitute: In ice-9/boot-9.scm:
substitute:  157: 11 [catch getaddrinfo-error ...]
substitute:  157: 10 [catch srfi-34 # ...]
substitute:  157: 9 [catch system-error ...]
substitute: In guix/scripts/substitute.scm:
substitute:  946: 8 [#]
substitute:  804: 7 [process-query "info 
/gnu/store/x29dbk9inqjym79q0907sx4arp1bfp28-graphviz-2.38.0-doc " ...]
substitute:  633: 6 [lookup-narinfos/diverse # #]
substitute:  617: 5 [lookup-narinfos "https://hydra-mirror.marusich.info"; #]
substitute:  589: 4 [fetch-narinfos "https://hydra-mirror.marusich.info"; #]
substitute:  222: 3 [download-cache-info "https://hydra-mirror.marusich.info";]
substitute: In guix/records.scm:
substitute:  331: 2 [recutils->alist #]
substitute: In ice-9/rdelim.scm:
substitute:  184: 1 [read-line # trim]
substitute: In unknown file:
substitute:?: 0 [%read-line #]
substitute: 
substitute: ERROR: In procedure %read-line:
substitute: ERROR: In procedure %read-line: Wrong type argument in position 1 
(expecting open input port): #
guix package: error: build failed: substituter `substitute' died unexpectedly
--8<---cut here---end--->8---

When I replaced --substitute-urls="https://hydra-mirror.marusich.info";
with --substitute-urls="http://hydra-mirror.marusich.info";, the same
command worked fine.

Chris






bug#22802: guix system init installs grub.cfg gcroot to host

2016-03-07 Thread Jookia
On Mon, Mar 07, 2016 at 01:15:38PM +0100, Ludovic Courtès wrote:
> Jookia <166...@gmail.com> skribis:
> > When running 'guix system init', GRUB requires a GC root to be placed in
> > /var/guix. When building a VM, this GC root is placed in 
> > /var/guix/gcroots
> > however while building a system on another drive, the GC root is placed on 
> > the
> > host's /var/guix/gcroots
>
> Indeed, good catch!
>
> I believe this is fixed with the patch below.  Can you confirm?

I can't easily confirm since I don't have a system I can 'init' to anymore, and
I've already fixed this in my set of GRUB patches so it'd also lock me out of
Libreboot on my actual machine if I did.

> Thanks,
> Ludo’.

Sorry,
Jookia.





bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Alex Kost
Ludovic Courtès (2016-03-08 00:03 +0300) wrote:

[...]
>> what about using (not tested):
>>
>>   (and=> (or (package-by-id id-or-name)
>>  (match (packages-by-name id-or-name)
>>(()#f)
>>((pkg ..1) pkg)))
>>  (compose location->string package-location))
>
> I like it too!
>
>> I know you love my 'pkg' identifier.  ;)
>
> No I don’t!  :-)
>
> I think we should stick to one identifier style, which is to always use
> full words (I’m often tempted to use abbreviations and I force myself
> not to, as silly as I am!)

Good to know, I always thought that things like "pkg" or "drv" are OK in
a local scope.

-- 
Alex





bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Alex Kost
Mathieu Lirzin (2016-03-07 22:58 +0300) wrote:

> l...@gnu.org (Ludovic Courtès) writes:
>
>> diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
>> index 34da6ac..c5d5d75 100644
>> --- a/emacs/guix-main.scm
>> +++ b/emacs/guix-main.scm
>> @@ -954,10 +954,14 @@ GENERATIONS is a list of generation numbers."
>>  
>>  (define (package-location-string id-or-name)
>>"Return a location string of a package with ID-OR-NAME."
>> -  (and-let* ((package  (or (package-by-id id-or-name)
>> -   (first (packages-by-name id-or-name
>> - (location (package-location package)))
>> -(location->string location)))
>> +  (define package
>> +(or (package-by-id id-or-name)
>> +(match (packages-by-name id-or-name)
>> +  (() #f)
>> +  ((first . rest) first
>> +
>> +  (and package
>> +   (location->string (package-location package
>
> Not related to the bug.  but it feels weird to use internal defines for
> something else than a procedure.

I have the same feeling.

> what about using (not tested):
>
>
>   (and=> (or (package-by-id id-or-name)
>  (match (packages-by-name id-or-name)
>(()#f)
>((pkg ..1) pkg)))
>  (compose location->string package-location))

I like this variant!

-- 
Alex





bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Alex Kost
Ludovic Courtès (2016-03-07 19:28 +0300) wrote:

> Currently M-x guix-edit fails badly (actually ‘guix-package-location’)
> pwhen passed the name of a nonexistent package:
[...]
> I think this patch fixes it:
>
>
> diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
> index 34da6ac..c5d5d75 100644
> --- a/emacs/guix-main.scm
> +++ b/emacs/guix-main.scm
> @@ -954,10 +954,14 @@ GENERATIONS is a list of generation numbers."
>  
>  (define (package-location-string id-or-name)
>"Return a location string of a package with ID-OR-NAME."
> -  (and-let* ((package  (or (package-by-id id-or-name)
> -   (first (packages-by-name id-or-name
> - (location (package-location package)))
> -(location->string location)))
> +  (define package
> +(or (package-by-id id-or-name)
> +(match (packages-by-name id-or-name)
> +  (() #f)
> +  ((first . rest) first
> +
> +  (and package
> +   (location->string (package-location package
>  
>  (define (package-source-derivation->store-path derivation)
>"Return a store path of the package source DERIVATION."
>
> Thoughts?

Great!  Thanks for fixing it!  Feel free (you or Mathieu) to push any of
your patches.

-- 
Alex





bug#22650: guixSD default umask is 0000

2016-03-07 Thread Alex Kost
宋文武 (2016-03-07 18:18 +0300) wrote:

> 于 2016年3月7日 GMT+08:00下午8:18:44, l...@gnu.org 写到:
>
> l...@gnu.org (Ludovic Courtès) skribis:
>
>  myglc2  skribis:
>
>  glc@g1 ~$ ssh glc4@g1
>  glc4@g1's password:
>  glc4@g1 ~$ umask
>  
>
>  Oh indeed, I can reproduce it.
>
>  The problem is that lshd resets the umask when it starts (in
>  src/daemon.c:daemon_init) but never changes it again.
>
>  Perhaps we should be using pam_umask and login.defs (although I’m 
> unsure
>  if lshd would honor it), or alternately add explicitly set the umask 
> in
>  /etc/profile.
>
>  Thoughts?
>
> 宋文武 & Alex: WDYT?  (Asking you since I know you’re already familiar
> with these things.  :-))
>
> Ludo’.
>
>
> I never pay attention to umask, but set it in /etc/profile seem the
> right thing to me. IIRC, debian and exherbo set it in there too.

I agree ("ArchLinux" also has "umask 022" in /etc/profile).

--
Alex





bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Ludovic Courtès
Mathieu Lirzin  skribis:

> l...@gnu.org (Ludovic Courtès) writes:
>
>> diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
>> index 34da6ac..c5d5d75 100644
>> --- a/emacs/guix-main.scm
>> +++ b/emacs/guix-main.scm
>> @@ -954,10 +954,14 @@ GENERATIONS is a list of generation numbers."
>>  
>>  (define (package-location-string id-or-name)
>>"Return a location string of a package with ID-OR-NAME."
>> -  (and-let* ((package  (or (package-by-id id-or-name)
>> -   (first (packages-by-name id-or-name
>> - (location (package-location package)))
>> -(location->string location)))
>> +  (define package
>> +(or (package-by-id id-or-name)
>> +(match (packages-by-name id-or-name)
>> +  (() #f)
>> +  ((first . rest) first
>> +
>> +  (and package
>> +   (location->string (package-location package
>
> Not related to the bug.  but it feels weird to use internal defines for
> something else than a procedure.

I don’t find it weird.  :-)

> what about using (not tested):
>
>   (and=> (or (package-by-id id-or-name)
>  (match (packages-by-name id-or-name)
>(()#f)
>((pkg ..1) pkg)))
>  (compose location->string package-location))

I like it too!

> I know you love my 'pkg' identifier.  ;)

No I don’t!  :-)

I think we should stick to one identifier style, which is to always use
full words (I’m often tempted to use abbreviations and I force myself
not to, as silly as I am!)

Ludo’.





bug#22914: "VM stack overflow" with graft dependencies from substitutes

2016-03-07 Thread Ludovic Courtès
Eric Bavier  skribis:

> On Sat, 05 Mar 2016 22:55:37 +0100
> l...@gnu.org (Ludovic Courtès) wrote:
>
>> Eric Bavier  skribis:
>> 
>> > Starting with commit c90cb5c9d84ded26ef44d1e6593508d5b9e4655e (via 'git
>> > bisect') I get the following error messages after `make clean && make
>> > && ./pre-inst-env guix environment texlive`:  
>> 
>> I experienced something similar while doing “guix build icecat”.  I
>> believe this is fixed by dd78e90a4dcd1e637b56ae278c4e631ccb384ee0.
>> 
>> Could you confirm?
>
> This appears to have fixed the issue on my end too.  Thanks!

Great.
Next bug…  :-)

Ludo’.





bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Mathieu Lirzin
l...@gnu.org (Ludovic Courtès) writes:

> diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
> index 34da6ac..c5d5d75 100644
> --- a/emacs/guix-main.scm
> +++ b/emacs/guix-main.scm
> @@ -954,10 +954,14 @@ GENERATIONS is a list of generation numbers."
>  
>  (define (package-location-string id-or-name)
>"Return a location string of a package with ID-OR-NAME."
> -  (and-let* ((package  (or (package-by-id id-or-name)
> -   (first (packages-by-name id-or-name
> - (location (package-location package)))
> -(location->string location)))
> +  (define package
> +(or (package-by-id id-or-name)
> +(match (packages-by-name id-or-name)
> +  (() #f)
> +  ((first . rest) first
> +
> +  (and package
> +   (location->string (package-location package

Not related to the bug.  but it feels weird to use internal defines for
something else than a procedure.

what about using (not tested):

--8<---cut here---start->8---
  (and=> (or (package-by-id id-or-name)
 (match (packages-by-name id-or-name)
   (()#f)
   ((pkg ..1) pkg)))
 (compose location->string package-location))
--8<---cut here---end--->8---

I know you love my 'pkg' identifier.  ;)

-- 
Mathieu Lirzin





bug#22550: (require 'magit) produces error: "no such file or directory" "dash"

2016-03-07 Thread myglc2
Alex Kost  writes:

[...]

> No, I mean it is fixed in the current master, but as I wrote¹ it will
> take effect only after we update our "guix" package.  Currently it is
> "guix-devel" package at commit c3f29bc², which is older then commit
> 004ea62 (that fixed this issue).
>
> After we update "guix" package (either via updating "guix-devel"
> snapshot or via pointing "guix" to "guix-0.9.1" after the new release),
> this will be finally fixed.
>
> ¹ http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22550#38
> ² 
> http://git.savannah.gnu.org/cgit/guix.git/tree/gnu/packages/package-management.scm#n198

Thank you. Sorry you had to explain this twice :(

I re-read your earlier posts. AIUI now, in order to use the latest guix
emacs features from 'git checkout master' one must add to emacs init:

(let ((dir "~/src/guix/emacs"))
  (add-to-list 'load-path dir)
  (setq guix-load-path dir)
  (require 'guix-init nil t))

Do you think we should we add that to (info "(guix) Building from Git") ? 






bug#22933: M-x guix-edit fails gracelessly when passed an nonexistent package name

2016-03-07 Thread Ludovic Courtès
Currently M-x guix-edit fails badly (actually ‘guix-package-location’)
pwhen passed the name of a nonexistent package:

--8<---cut here---start->8---
Debugger entered--Lisp error: (error "Error in evaluating guile expression: 
ERROR: In procedure car:
ERROR: In procedure car: Wrong type (expecting pair): ()

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [3]> ")
  signal(error ("Error in evaluating guile expression: ERROR: In procedure 
car:\nERROR: In procedure car: Wrong type (expecting pair): ()\n\nEntering a 
new prompt.  Type `,bt' for a backtrace or `,q' to 
continue.\nscheme@(guile-user) [3]> "))
  error("Error in evaluating guile expression: %s" "ERROR: In procedure 
car:\nERROR: In procedure car: Wrong type (expecting pair): ()\n\nEntering a 
new prompt.  Type `,bt' for a backtrace or `,q' to 
continue.\nscheme@(guile-user) [3]> ")
  (if (geiser-eval--retort-error res) (error "Error in evaluating guile 
expression: %s" (geiser-eval--retort-output res)) (cdr (assq (quote result) 
res)))
  (let ((res (geiser-eval--send/wait (list (quote :eval) (list (quote :scm) 
str) (if (geiser-eval--retort-error res) (error "Error in evaluating guile 
expression: %s" (geiser-eval--retort-output res)) (cdr (assq (quote result) 
res
  (save-current-buffer (set-buffer (or repl (guix-geiser-repl))) (let ((res 
(geiser-eval--send/wait (list (quote :eval) (list (quote :scm) str) (if 
(geiser-eval--retort-error res) (error "Error in evaluating guile expression: 
%s" (geiser-eval--retort-output res)) (cdr (assq (quote result) res)
  guix-geiser-eval("(package-location-string \"foo\")" #)
  (car (guix-geiser-eval str repl))
  (replace-regexp-in-string "#t" "t" (car (guix-geiser-eval str repl)))
  (replace-regexp-in-string "#f\\|#" "nil" 
(replace-regexp-in-string "#t" "t" (car (guix-geiser-eval str repl
  (read (replace-regexp-in-string "#f\\|#" "nil" 
(replace-regexp-in-string "#t" "t" (car (guix-geiser-eval str repl)
  guix-geiser-eval-read("(package-location-string \"foo\")" #)
  guix-eval-read("(package-location-string \"foo\")")
  guix-package-location("foo")
  eval((guix-package-location "foo") nil)
  eval-expression((guix-package-location "foo") nil)
  call-interactively(eval-expression nil nil)
  command-execute(eval-expression)
--8<---cut here---end--->8---

I think this patch fixes it:

diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
index 34da6ac..c5d5d75 100644
--- a/emacs/guix-main.scm
+++ b/emacs/guix-main.scm
@@ -954,10 +954,14 @@ GENERATIONS is a list of generation numbers."
 
 (define (package-location-string id-or-name)
   "Return a location string of a package with ID-OR-NAME."
-  (and-let* ((package  (or (package-by-id id-or-name)
-   (first (packages-by-name id-or-name
- (location (package-location package)))
-(location->string location)))
+  (define package
+(or (package-by-id id-or-name)
+(match (packages-by-name id-or-name)
+  (() #f)
+  ((first . rest) first
+
+  (and package
+   (location->string (package-location package
 
 (define (package-source-derivation->store-path derivation)
   "Return a store path of the package source DERIVATION."

Thoughts?

Ludo’.


bug#22914: "VM stack overflow" with graft dependencies from substitutes

2016-03-07 Thread Eric Bavier
On Sat, 05 Mar 2016 22:55:37 +0100
l...@gnu.org (Ludovic Courtès) wrote:

> Eric Bavier  skribis:
> 
> > Starting with commit c90cb5c9d84ded26ef44d1e6593508d5b9e4655e (via 'git
> > bisect') I get the following error messages after `make clean && make
> > && ./pre-inst-env guix environment texlive`:  
> 
> I experienced something similar while doing “guix build icecat”.  I
> believe this is fixed by dd78e90a4dcd1e637b56ae278c4e631ccb384ee0.
> 
> Could you confirm?

This appears to have fixed the issue on my end too.  Thanks!

`~Eric





bug#22650: guixSD default umask is 0000

2016-03-07 Thread 宋文武
于 2016年3月7日 GMT+08:00下午8:18:44, l...@gnu.org 写到:
>l...@gnu.org (Ludovic Courtès) skribis:
>
>> myglc2  skribis:
>>
>>> glc@g1 ~$ ssh glc4@g1
>>> glc4@g1's password: 
>>> glc4@g1 ~$ umask
>>> 
>>
>> Oh indeed, I can reproduce it.
>>
>> The problem is that lshd resets the umask when it starts (in
>> src/daemon.c:daemon_init) but never changes it again.
>>
>> Perhaps we should be using pam_umask and login.defs (although I’m
>unsure
>> if lshd would honor it), or alternately add explicitly set the umask
>in
>> /etc/profile.
>>
>> Thoughts?
>
>宋文武 & Alex: WDYT?  (Asking you since I know you’re already familiar
>with these things.  :-))
>
>Ludo’.

I never pay attention to umask, but set it in /etc/profile seem the right thing 
to me. IIRC, debian and exherbo set it in there too.

bug#22492: create new user guixsd

2016-03-07 Thread Ludovic Courtès
ren...@openmailbox.org skribis:

> On 2016-02-12 14:58, l...@gnu.org wrote:
>> ren...@openmailbox.org skribis:
>>
>>> On 2016-01-31 17:29, l...@gnu.org wrote:
 ren...@openmailbox.org skribis:

> i run 'guix system reconfigure config.scm --falback', and add new
> user: tron.

 How did you add the new user account?  You added it in the
 ‘operating-system’ declaration and then run ‘guix system
 reconfigure’,
 right?
>>
>> [...]
>>
>>> i replace bob user with tron in the configuration file, and later i
>>> reconfigure. And i have only root partition.
>>
>> You wrote:
>>
>>> When i try to login such as tron does not allow login and slim
>>> restart.
>>
>> Can you log in as ‘tron’ on the console?  (Try Ctrl-alt-F2 to switch to
>> the console.)
>>
>> Ludo’.
>
> Hi Ludo,
>
> i installed the system from scratch. I will make a scenario for testing.

Hi!  Any update on this bug?

  http://bugs.gnu.org/22492

Thanks,
Ludo’.





bug#22650: guixSD default umask is 0000

2016-03-07 Thread Ludovic Courtès
l...@gnu.org (Ludovic Courtès) skribis:

> myglc2  skribis:
>
>> glc@g1 ~$ ssh glc4@g1
>> glc4@g1's password: 
>> glc4@g1 ~$ umask
>> 
>
> Oh indeed, I can reproduce it.
>
> The problem is that lshd resets the umask when it starts (in
> src/daemon.c:daemon_init) but never changes it again.
>
> Perhaps we should be using pam_umask and login.defs (although I’m unsure
> if lshd would honor it), or alternately add explicitly set the umask in
> /etc/profile.
>
> Thoughts?

宋文武 & Alex: WDYT?  (Asking you since I know you’re already familiar
with these things.  :-))

Ludo’.





bug#22802: guix system init installs grub.cfg gcroot to host

2016-03-07 Thread Ludovic Courtès
Jookia <166...@gmail.com> skribis:

> When running 'guix system init', GRUB requires a GC root to be placed in
> /var/guix. When building a VM, this GC root is placed in 
> /var/guix/gcroots
> however while building a system on another drive, the GC root is placed on the
> host's /var/guix/gcroots

Indeed, good catch!

I believe this is fixed with the patch below.  Can you confirm?

Thanks,
Ludo’.

diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm
index 9f56a96..8ebeb4d 100644
--- a/guix/scripts/system.scm
+++ b/guix/scripts/system.scm
@@ -128,7 +128,8 @@ TARGET, and register them."
 (define (install-grub* grub.cfg device target)
   "This is a variant of 'install-grub' with error handling, lifted in
 %STORE-MONAD"
-  (let* ((gc-root  (string-append %gc-roots-directory "/grub.cfg"))
+  (let* ((gc-root  (string-append target %gc-roots-directory
+  "/grub.cfg"))
  (temp-gc-root (string-append gc-root ".new"))
  (delete-file  (lift1 delete-file %store-monad))
  (make-symlink (lift2 switch-symlinks %store-monad))


bug#22927: system-installed emacs does not find system-installed fonts

2016-03-07 Thread Alex Kost
myglc2 (2016-03-06 21:57 +0300) wrote:

> Not technically not a bug because the doc says ...
>
> 2.6.2 X11 Fonts
>
> The ‘fontconfig’ package in Guix looks for fonts in
> ‘$HOME/.guix-profile’ by default.  Thus, to allow graphical applications
> installed with Guix to display fonts, you have to install fonts with
> Guix as well.

(I have not checked but) I believe this can be fixed by adding
"/run/current-system/profile/share/fonts" to '--with-add-fonts'
configure flag of the 'fontconfig' package.

For now I think the following recipe should work:

1. Make a file "~/.config/fontconfig/fonts.conf" with the following
   contents:




/run/current-system/profile/share/fonts


2. Run "fc-cache -fv".

-- 
Alex


bug#22550: (require 'magit) produces error: "no such file or directory" "dash"

2016-03-07 Thread Alex Kost
myglc2 (2016-03-06 17:47 +0300) wrote:

> Alex Kost  writes:
> [...]
>>> So, should I put ...
>>>
>>> '(guix-emacs-load-autoloads "/run/current-system/profile")'
>>>
>>> ... in init.el, in which case guix INFO should say so.
>>
>> Well, you can do it as a temporary workaround, but we'll fix it soon (I
>> mean the system profile will also be inspected for emacs packages), so
>> it will not be necessary in the closest future.
>
> Hi Alex. Tried removing this and got 'command-execute: Symbol's function
> definition is void: magit-status'  Shouldn't it be fixed by now?

No, I mean it is fixed in the current master, but as I wrote¹ it will
take effect only after we update our "guix" package.  Currently it is
"guix-devel" package at commit c3f29bc², which is older then commit
004ea62 (that fixed this issue).

After we update "guix" package (either via updating "guix-devel"
snapshot or via pointing "guix" to "guix-0.9.1" after the new release),
this will be finally fixed.

¹ http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22550#38
² 
http://git.savannah.gnu.org/cgit/guix.git/tree/gnu/packages/package-management.scm#n198

-- 
Alex