Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP! Attempt at changing plugin loading structure #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

uliska
Copy link
Contributor

@uliska uliska commented Oct 29, 2018

This doesn't work yet, but I want to submit it for review.

The idea is to create better modularity for the various exporter modules (see discussion at #17 (comment)).

What I think is the right thing is how package.ily goes along with choosing the "backend"/target.

I also think that FileExport in api.scm does the right thing.

What does not work is making the export-lilypond function from within Humdrum.scm (so far this only touches the Humdrum file) available to api.scm. I really don't understand it because in my MWE it did work. (see #17 (comment))

This doesn't work yet, but I want to submit it for review.
What I think is the right thing is how package.ily goes along with
choosing the "backend"/target.

I also think that FileExport in api.scm does the right thing.

What does *not* work is making the export-lilypond function from
within Humdrum.scm (so far this only touches the Humdrum file)
available to api.scm. I really don't understand it because in my MWE
it *did* work. (see #17 (comment))
@uliska uliska self-assigned this Oct 29, 2018
@rshura
Copy link

rshura commented Oct 30, 2018

I have to admit that I got no clue about modules, packages, and the best way to call things, either in scheme in general or in LP in particular. I'm very new to all of this, and I'm sure you guys will know how to expose this the best. Sorry, but I just don't have enough expertise and intuition to offer here :-)

@PaulMorris
Copy link

I don't have a good grasp on the OLL package mechanisms but I'll try to take a look (but likely not before this weekend).

@uliska
Copy link
Contributor Author

uliska commented Oct 31, 2018

@PaulMorris my problem is very little about how OLL's package mechanism, but mainly about how Scheme modules are handled in the different constellations between LilyPond code/files and Scheme code/files.

@PaulMorris
Copy link

Hi @uliska , I had a chance to take a look. I'm seeing this:

Starting lilypond 2.19.82 [export-example.ly]...
Processing `/lilypond-export/export-example.ly'
Parsing...
/lilypond-export/package.ily:35:2: error: GUILE signaled an error for the expression beginning here
#
 (use-modules (lilypond-export api))
no code for module (oll-core internal music-tools)
/lilypond-export/package.ily:54:5: In expression (FileExport (quasiquote #)):
/ly-to-musicxml/lilypond-export/package.ily:54:5: Unbound variable: FileExport
Exited with return code 1.

Is the problem the error "no code for module (oll-core internal music-tools)"? (Where this module is loaded at the top of lilypond-export/api.scm.) When I look in oll-core/internal/ I don't see a music-tools module there. I have checked out the current master branch of oll-core repo.

I tried checking out the previous commit in the lilypond-export repo b69b579 (Move music-is? function...). I get a similar "no code for module (oll-core internal music-tools)" error:

Starting lilypond 2.19.82 [export-example.ly]...
Processing `/lilypond-export/export-example.ly'
Parsing...
/lilypond-export/package.ily:35:2: error: GUILE signaled an error for the expression beginning here
#
 (use-modules (lilypond-export api))
/lilypond-export/package.ily:45:2: error: GUILE signaled an error for the expression beginning here
#
 (let
/lilypond-export/export-example.ly:55:14: error: syntax error, unexpected \default
\exportMusic 
             \default hum \music
/lilypond-export/export-example.ly:57:18: error: GUILE signaled an error for the expression beginning here
opts.exporter = #
                 exportMusicXML
/lilypond-export/export-example.ly:62:5: error: unknown escaped string: `\FileExport'
    
    \FileExport #opts
/lilypond-export/export-example.ly:62:17: error: syntax error, unexpected SCM_TOKEN, expecting '.' or '=' or ','
    \FileExport 
                #opts
Interpreting music...
Preprocessing graphical objects...
Interpreting music...
Preprocessing graphical objects...
Interpreting music...
MIDI output to `export-example.midi'...
Finding the ideal number of pages...
Fitting music on 1 page...
Drawing systems...
Layout output to `/tmp/lilypond-THBmpd'...
Converting to `export-example.pdf'...
Deleting `/tmp/lilypond-THBmpd'...
no code for module (oll-core internal music-tools)
Unbound variable: exportLilyPond
Unbound variable: exportMusicXML
fatal error: failed files: "/lilypond-export/export-example.ly"
Exited with return code 1.

I see the commit where music-is? is removed from lilypond-export, but not a commit where it was added to oll-core. Do I have the wrong oll-core branch or something? That's as far as i got.

@uliska
Copy link
Contributor Author

uliska commented Nov 1, 2018

Is the problem the error "no code for module (oll-core internal music-tools)"?

No, the problem comes later. Sorry for not being explicit enough, but the music-tools module is still on the unmerged pull request openlilylib/oll-core#41 and its grob-tools branch.

BTW Scheme modules starting with oll-core are not in the internal directory of the repository but in scheme/oll-core, so music-tools resides in scheme/oll-core/internal/music-tools.scm

With that FileExport is found but then in https://github.com/openlilylib/lilypond-export/pull/19/files#diff-c7155ddda8f4cb08ec1cc013a8856450R59 , after "loading" the determined "plugin" module exporter is not found.

Copy link

@PaulMorris PaulMorris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @uliska , Thanks for the details on what oll-core branch to use. Once I un-commented the two lines indicated below, I got no more errors and a success message in the console when compiling the export-example.ly. See also Guile 1.8 docs on creating modules and using modules.

@@ -33,7 +33,7 @@

;% TODO ties, slurs, grace notes

(define-module (lilypond-export Humdrum))
;(define-module (lilypond-export Humdrum))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still need this define-module here.

@@ -41,12 +41,28 @@
(oll-core internal music-tools)
(lilypond-export lily)
(lilypond-export MusicXML)
(lilypond-export Humdrum)
;(lilypond-export Humdrum)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you still need to use the Humdrum module here, so the functions that are define-public in it can be used in this api module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PaulMorris thank you for looking into this. Now that I see it I can't believe that I seem to have managed to miss that combination. But yet, it seems it works properly now, and I'll be able to remove the "WIP" from this PR's title ASAP (was completely away over the weekend and will need some time to catch up, though).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to help @uliska . I had another thought about this. So far only the humdrum module is exporting an 'export-lilypond' function, but when musicxml does as well (and eventually MEI, etc.), will we end up with naming collisions where the api module doesn't know which 'export-lilypond' function to call? I may be missing something, but it would be worth testing this out before merging. (Which may well have been your next step!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is by design. Until now only the Humdrum module has been modified, and once that works I'll make all modules behave that way. Actually that's the requirement for plugging in a new exporter: providing an export-lilypond function. The actual idea is that load-exporter is configured to load the appropriate module and then takes the export-lilypond procedure from that module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also Guile 1.8 docs on creating modules and using modules.

I had actually read this and still was confused, and I'd say the problem at hand isn't explained in a way that I would be able to really understand it.

The Guile 1.8 docs on Loading from file state that load-from-file does "Load filename and evaluate its contents in the top-level environment".
What is the "top-level environment" here? Wouldn't that imply the result of a define or define-public should be visible in the file that loads the module? =>
If api.scm' "loads" humdrum.scm and export-lilypond is defined in humdrum.scm I would expect the code in api.scm (after the loading) to see export-lilypond. Is there a way to make that work without having to explicitly define a module in humdrum.scm and use it in api.scm?
Is primitive-load anything different than load and load-from-file ir is the absence of the optional reader argument the only difference?

My goal would be to make it possible that a new exporter is added by only adding a new exporter file, without the need to update api.scm.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is some code:

\version "2.19.82"
\include "oll-core/package.ily"
% I don't load the lilypond-export package here to show the import from a list

% force loading of lilypond-export_api_module - this is not needed, if the base package is already loaded
#(display (@ (lilypond-export api) moment->duration))

% this list shall be generated with a file walk through the export-module folder
exporters.MusicXML = #'exportMusicXML
exporters.Humdrum = #'exportHumdrum

% now fetch the 
mods = #'()
#(for-each
  (lambda (p)
     ; the (cdr p) may be replaced by mandatory const like 'lilypond-export
    (let ((exporter (module-ref (resolve-module `(lilypond-export ,(car p))) (cdr p)) ))
      ; add this to the list of exporters
      (set! mods `(,@mods ,exporter)) ; use (cons key exporter)
      )) exporters)

% log found exporters with its file-suffix
#(for-each
  (lambda (exporter)
    ; when the exporter is defined with a mandatory name the name is stored in an object-property
    (ly:message "~A ~A" (procedure-name exporter) (object-property exporter 'file-suffix))
    ) mods)

So you can collect all files in folder, import a function lilypond-export and add it to an alist (or some other kind of store). That way you can add exporters by dropping them in a directory - as long as the module follows the mandatory rules.

When my schedule allows I will prepare some code fitting into this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only have a vague idea after a first look, but there's one thing I'd definitely not want to do: produce a list of all available exporter functions. These functions are already large and will become huge when they will become more comprehensive, so we should really only load one such function.

Therefore we may produce a list of available module names but the actual module loading and function parsing should occur only once.

I think what I'm missing is somehow hidden in the (let ((exporter (module-ref (resolve-module line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken this approach doesn't help either with my problem. resolve-module allows loading a module by its name, but this seems to require the module being "used" too - so there's no real advantage over using load-from-path.

@jpvoigt
Copy link
Collaborator

jpvoigt commented Nov 5, 2018 via email

@jpvoigt
Copy link
Collaborator

jpvoigt commented Nov 5, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants