unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
       [not found] ` <20170715081545.DB2A022E4F@vcs0.savannah.gnu.org>
@ 2017-07-23 22:22   ` Mark H Weaver
  2017-07-24  6:37     ` Ricardo Wurmus
                       ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Mark H Weaver @ 2017-07-23 22:22 UTC (permalink / raw)
  To: Danny Milosavljevic; +Cc: guix-devel

Hi,

I just got bitten again by a problem that has plagued us over the years,
namely our extensive tangled knot of cyclic module dependencies which
occasionally causes hard-to-debug compilation failures.

Here's how it looked this time, while running 'make' in my git checkout:

--8<---------------cut here---------------start------------->8---
Backtrace:
In ice-9/boot-9.scm:
  2879:24 19 (_)
   230:29 18 (map1 _)
   230:29 17 (map1 _)
   230:29 16 (map1 _)
   230:29 15 (map1 _)
   230:29 14 (map1 _)
   230:29 13 (map1 _)
   230:29 12 (map1 _)
   230:17 11 (map1 (((gnu packages linux)) ((gnu packages ncurses))))
  2792:17 10 (resolve-interface (gnu packages linux) #:select _ #:hide _ #:prefix _ #:renamer _ #:version _)
  2718:10  9 (_ (gnu packages linux) _ _ #:ensure _)
  2986:16  8 (try-module-autoload _ _)
   2316:4  7 (save-module-excursion _)
  3006:22  6 (_)
In unknown file:
           5 (primitive-load-path "gnu/packages/linux" #<procedure 13b8740 at ice-9/boot-9.scm:2993:32 ()>)
In ice-9/eval.scm:
    619:8  4 (_ #f)
   626:19  3 (_ #<directory (gnu packages linux) 13df820>)
   191:35  2 (_ #(#<directory (gnu packages linux) 13df820> #<variable 4b8c1e0 value: #<procedure loop (a)>>))
   223:20  1 (proc #(#<directory (gnu packages linux) 13df820> #<variable 4b8c1e0 value: #<procedure loop (a)>>))
In unknown file:
           0 (%resolve-variable (7 . ncurses) #<directory (gnu packages linux) 13df820>)

ERROR: In procedure %resolve-variable:
ERROR: Unbound variable: ncurses
make[2]: *** [Makefile:5222: make-go] Error 1
make[2]: Leaving directory '/home/mhw/guix'
make[1]: *** [Makefile:4353: all-recursive] Error 1
make[1]: Leaving directory '/home/mhw/guix'
make: *** [Makefile:2943: all] Error 2
--8<---------------cut here---------------end--------------->8---

It was caused by the following commit, made 2.5 weeks ago, although it
didn't cause trouble for me until today:

dannym@scratchpost.org (Danny Milosavljevic) writes:

> dannym pushed a commit to branch master
> in repository guix.
>
> commit 87ffa416af029722a73b3244b409f86e39086bc3
> Author: Danny Milosavljevic <dannym@scratchpost.org>
> Date:   Wed Jul 5 22:31:03 2017 +0200
>
>     gnu: Add ncurses-with-gpm.
>     
>     * gnu/packages/linux.scm (ncurses/gpm): New variable.
> ---
>  gnu/packages/linux.scm | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
>
> diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm
> index 1a9e9d7..58f6f36 100644
> --- a/gnu/packages/linux.scm
> +++ b/gnu/packages/linux.scm
> @@ -3013,6 +3013,18 @@ applications running on the Linux console.  It allows users to select items
>  and copy/paste text in the console and in xterm.")
>      (license license:gpl2+)))
>  
> +(define-public ncurses/gpm
> +  (package/inherit ncurses
> +    (name "ncurses-with-gpm")
> +    (arguments
> +        (substitute-keyword-arguments (package-arguments ncurses)
> +         ((#:configure-flags cf)
> +          `(cons (string-append "--with-gpm="
> +                                (assoc-ref %build-inputs "gpm")
> +                                "/lib/libgpm.so.2") ,cf))))
> +    (inputs
> +     `(("gpm" ,gpm)))))

I don't blame Danny for this, because it is a non-obvious mistake that
any of us could make, invited by our IMO questionable practice of
allowing cyclic module dependencies, and not sufficiently mitigated by
warnings about this issue.

So, it seems that the time has come again to review the rules that we
must follow in order to avoid problems from cyclic module dependencies:

If two modules are involved in a cyclic dependency, i.e. if they are
part of a cycle in the module-import graph, then neither of them can
safely use macros defined in the other one, and any variable references
from one to the other must be *delayed* until after the modules are
loaded.

Briefly, for our purposes, if a variable reference occurs within the
'arguments', 'inputs', 'propagated-inputs', 'native-inputs', or
'replacement' fields of a package, then it is delayed.  A variable
reference is also delayed if it is made within a procedure body, and if
that procedure is not called during module loading.

However, when a package inherits from another package, then the variable
reference to the inherited package is *not* delayed.  In such a case,
the variable must be accessed while the module is being loaded.

That's what happened here.  The above commit introduced a package
'ncurses/gpm' defined in (gnu packages linux) that inherited from
'ncurses' defined in (gnu packages ncurses).  Those two modules are part
of the same cycle in the module-import graph, so it effectively created
a land mine waiting for some unfortunate person to step on.

I should explain in more detail why this happens.

Normally, if module B imports module A, then we arrange to load module A
before loading module B.  This ensures that module B can use all of the
facilities of module A at any time without restrictions.

However, if module A and module B import each other (directly or
indirectly) then obviously we cannot load each one before loading the
other.  Modern Scheme standards simply prohibit cyclic module
dependencies.

Guile is more lax about this, and here's what happens: if module A and
module B import each other, then one of them will be loaded before the
other one, and it's not obvious which will come first[*].  In practice,
we must ensure that things work regardless of the order.

In this case, things worked as long as (gnu package ncurses) was loaded
first, but if (gnu packages linux) was loaded first, then it failed.

Commit c67d80563fc7021484602889454de534469f2785 resolves this immediate
issue by moving 'ncurses/gpm' to (gnu packages ncurses), to eliminate
the non-delayed cross-module variable reference between
mutually-dependent modules.

FWIW, I would like to see us work to eliminate all cyclic module
dependencies in Guix, by splitting up our package modules as needed so
that they form a directed acyclic graph.

[*] In more detail, what Guile does is this: when asked to load module
A, if it already exists, it does nothing.  Otherwise, it immediately
creates module A as an empty module, and then starts loading it from the
top, starting with its 'define-module' form.  It begins by recursively
loading all modules imported by module A.  At this point, none of the
A's definitions have been loaded yet.  If, in the course of importing
those modules, a request is made to load module A (caused by a circular
dependency), Guile will see that module A already exists, so it does
nothing, although it is empty.  So, the order in which
mutually-dependent modules are populated depends on which module is
loaded first, and the order in which they import each other, etc.

    Regards,
      Mark

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-23 22:22   ` Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.) Mark H Weaver
@ 2017-07-24  6:37     ` Ricardo Wurmus
  2017-07-24  9:25     ` Ludovic Courtès
  2017-07-25 21:59     ` Danny Milosavljevic
  2 siblings, 0 replies; 8+ messages in thread
From: Ricardo Wurmus @ 2017-07-24  6:37 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guix-devel


Mark H Weaver <mhw@netris.org> writes:

> FWIW, I would like to see us work to eliminate all cyclic module
> dependencies in Guix, by splitting up our package modules as needed so
> that they form a directed acyclic graph.

(Note: this goes off on a tagent, but I think it is still relevant.)

I wonder if maybe having a single module per package might be the way to
go.  It may seem extreme and there’s an unwelcome bit of repetition by
declaring dependent modules *in addition* to declaring packages as
inputs — but it might also allow us to “freeze” or serialize a slice of
the package graph and store it alongside the store items.

What I envision is that you could use a much richer interface (namely
Scheme itself) to explore the package closure *long after* the package
has been installed to the store, instead of having to make do with the
rather poor representation of references in the database.  You would be
able to look at the section of the graph Guix describes that resulted in
the store item(s), untangle the graph in a REPL and even use that as a
starting point to create a package variant right inside the REPL without
having to check out an older version of Guix.

This is made more complicated by the fact that we don’t have a direct
mapping of packages to modules, which means that we don’t have a direct
map from store items to Guile ELF files that could be loaded in a REPL.

If we switched to defining a single package per module, however, I think
we need some syntactic sugar to avoid declaring dependencies more than
once.

--
Ricardo

GPG: BCA6 89B6 3655 3801 C3C6  2150 197A 5888 235F ACAC
https://elephly.net

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-23 22:22   ` Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.) Mark H Weaver
  2017-07-24  6:37     ` Ricardo Wurmus
@ 2017-07-24  9:25     ` Ludovic Courtès
  2017-07-25 21:28       ` Ricardo Wurmus
  2017-07-25 21:59     ` Danny Milosavljevic
  2 siblings, 1 reply; 8+ messages in thread
From: Ludovic Courtès @ 2017-07-24  9:25 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guix-devel

Hi Mark,

I agree that it’s terrible that we have to deal with such problems.
Thanks for (re)explaining the issue.

Mark H Weaver <mhw@netris.org> skribis:

> FWIW, I would like to see us work to eliminate all cyclic module
> dependencies in Guix, by splitting up our package modules as needed so
> that they form a directed acyclic graph.

This seems hard to achieve, unless we use one file per package.

Perhaps another option would be to introduce a ‘define-package’ form
that would thunk every package definition, or something like that,
though it wouldn’t help in more complex cases I guess.

Ludo’.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-24  9:25     ` Ludovic Courtès
@ 2017-07-25 21:28       ` Ricardo Wurmus
  2017-07-25 21:45         ` Danny Milosavljevic
                           ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Ricardo Wurmus @ 2017-07-25 21:28 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel


Ludovic Courtès <ludo@gnu.org> skribis:

> Mark H Weaver <mhw@netris.org> skribis:
>
>> FWIW, I would like to see us work to eliminate all cyclic module
>> dependencies in Guix, by splitting up our package modules as needed so
>> that they form a directed acyclic graph.
>
> This seems hard to achieve, unless we use one file per package.

Are there drawbacks to using one file per package other than it’s a bit
“heavy” due to all the boilerplate of license headers and module
definitions?

--
Ricardo

GPG: BCA6 89B6 3655 3801 C3C6  2150 197A 5888 235F ACAC
https://elephly.net

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-25 21:28       ` Ricardo Wurmus
@ 2017-07-25 21:45         ` Danny Milosavljevic
  2017-07-26  7:30         ` Alex Sassmannshausen
  2017-07-27  9:01         ` Ludovic Courtès
  2 siblings, 0 replies; 8+ messages in thread
From: Danny Milosavljevic @ 2017-07-25 21:45 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: guix-devel

On Tue, 25 Jul 2017 23:28:38 +0200
Ricardo Wurmus <rekado@elephly.net> wrote:

> Are there drawbacks to using one file per package other than it’s a bit
> “heavy” due to all the boilerplate of license headers and module
> definitions?

Probably the NFS case gets much slower because of the gazillon stat() calls...

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-23 22:22   ` Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.) Mark H Weaver
  2017-07-24  6:37     ` Ricardo Wurmus
  2017-07-24  9:25     ` Ludovic Courtès
@ 2017-07-25 21:59     ` Danny Milosavljevic
  2 siblings, 0 replies; 8+ messages in thread
From: Danny Milosavljevic @ 2017-07-25 21:59 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guix-devel

Hi Mark,

On Sun, 23 Jul 2017 18:22:39 -0400
Mark H Weaver <mhw@netris.org> wrote:

> any of us could make, invited by our IMO questionable practice of
> allowing cyclic module dependencies, and not sufficiently mitigated by
> warnings about this issue.

Sorry!

It was actually caused by me trying to avoid a dependency of (gnu packages ncurses) to (gnu packages linux).  But in trying to avoid it I made it worse.

I have to say this circular dependency stuff is quite... strange.  I'm not sure what it helps to have it in Guile at all.

But thanks for the explanation.  I've also looked at libguile/modules.c where it indeed first creates an empty module and only then resolves the imports (scm_c_define_module).

I've worked with languages which explicitly disallow module circular dependencies (Pascal etc) and it's not been constraining to me - on the contrary, it makes me better visualize which modules are more fundamental (in my reductionist worldview of a program).

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-25 21:28       ` Ricardo Wurmus
  2017-07-25 21:45         ` Danny Milosavljevic
@ 2017-07-26  7:30         ` Alex Sassmannshausen
  2017-07-27  9:01         ` Ludovic Courtès
  2 siblings, 0 replies; 8+ messages in thread
From: Alex Sassmannshausen @ 2017-07-26  7:30 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: guix-devel


Ricardo Wurmus writes:

> Ludovic Courtès <ludo@gnu.org> skribis:
>
>> Mark H Weaver <mhw@netris.org> skribis:
>>
>>> FWIW, I would like to see us work to eliminate all cyclic module
>>> dependencies in Guix, by splitting up our package modules as needed so
>>> that they form a directed acyclic graph.
>>
>> This seems hard to achieve, unless we use one file per package.
>
> Are there drawbacks to using one file per package other than it’s a bit
> “heavy” due to all the boilerplate of license headers and module
> definitions?

I have two thoughts that are related to this:
- languages like Perl, which have tons of modules on CPAN, a great
  number of which are incredibly simple and small: we are literally
  talking about adding 100s of files. This is quite different from
  adding a "program", such as Emacs, a larger, well-defined definition.
  I don't think the Perl example is a stopper, but perhaps something to
  consider in terms of performance/implementation.

- If we take this direction, perhaps we should aim to have a helper
  commandline script to which you can pass the dependencies, and which
  takes care of writing the boilerplate as well as importing the
  appropriate modules?

Alex

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.)
  2017-07-25 21:28       ` Ricardo Wurmus
  2017-07-25 21:45         ` Danny Milosavljevic
  2017-07-26  7:30         ` Alex Sassmannshausen
@ 2017-07-27  9:01         ` Ludovic Courtès
  2 siblings, 0 replies; 8+ messages in thread
From: Ludovic Courtès @ 2017-07-27  9:01 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: guix-devel

Ricardo Wurmus <rekado@elephly.net> skribis:

> Ludovic Courtès <ludo@gnu.org> skribis:
>
>> Mark H Weaver <mhw@netris.org> skribis:
>>
>>> FWIW, I would like to see us work to eliminate all cyclic module
>>> dependencies in Guix, by splitting up our package modules as needed so
>>> that they form a directed acyclic graph.
>>
>> This seems hard to achieve, unless we use one file per package.
>
> Are there drawbacks to using one file per package other than it’s a bit
> “heavy” due to all the boilerplate of license headers and module
> definitions?

It’d be less convenient for packagers, notably because you’d have to
specify all the modules to import, as you noted before.  We could work
around it with a ‘define-package-module’ macro that would lookup
packages with ‘specification->package’ or something like that.

There might be other implications, I’m not sure.

Ludo’.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2017-07-27  9:01 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20170715081544.12288.82778@vcs0.savannah.gnu.org>
     [not found] ` <20170715081545.DB2A022E4F@vcs0.savannah.gnu.org>
2017-07-23 22:22   ` Trouble with circular module dependencies (Re: 01/02: gnu: Add ncurses-with-gpm.) Mark H Weaver
2017-07-24  6:37     ` Ricardo Wurmus
2017-07-24  9:25     ` Ludovic Courtès
2017-07-25 21:28       ` Ricardo Wurmus
2017-07-25 21:45         ` Danny Milosavljevic
2017-07-26  7:30         ` Alex Sassmannshausen
2017-07-27  9:01         ` Ludovic Courtès
2017-07-25 21:59     ` Danny Milosavljevic

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).